简体   繁体   English

Django中的Python日志记录

[英]Python logging in Django

I'm developing a Django app, and I'm trying to use Python's logging module for error/trace logging. 我正在开发一个Django应用程序,我正在尝试使用Python的日志记录模块进行错误/跟踪记录。 Ideally I'd like to have different loggers configured for different areas of the site. 理想情况下,我希望为站点的不同区域配置不同的记录器。 So far I've got all of this working, but one thing has me scratching my head. 到目前为止,我已经完成了所有这些工作,但有一件事让我摸不着头脑。

I have the root logger going to sys.stderr, and I have configured another logger to write to a file. 我有根记录器转到sys.stderr,我已经配置了另一个记录器来写入文件。 This is in my settings.py file: 这是在我的settings.py文件中:

sviewlog = logging.getLogger('MyApp.views.scans')
view_log_handler = logging.FileHandler('C:\\MyApp\\logs\\scan_log.log')
view_log_handler.setLevel(logging.INFO)
view_log_handler.setFormatter(logging.Formatter('%(asctime)s %(name)-12s %(levelname)-8s %(message)s'))
sviewlog.addHandler(view_log_handler)

Seems pretty simple. 看起来很简单。 Here's the problem, though: whatever I write to the sviewlog gets written to the log file twice. 不过这是问题所在:无论我写入sviewlog还是写入日志文件两次。 The root logger only prints it once. 根记录器只打印一次。 It's like addHandler() is being called twice. 就像addHandler()被调用两次一样。 And when I put my code through a debugger, this is exactly what I see. 当我将代码放入调试器时,这正是我所看到的。 The code in settings.py is getting executed twice, so two FileHandlers are created and added to the same logger instance. settings.py中的代码执行两次,因此创建了两个FileHandler并将其添加到同一个logger实例中。 But why? 但为什么? And how do I get around this? 我该如何解决这个问题呢?

Can anyone tell me what's going on here? 谁能告诉我这里发生了什么? I've tried moving the sviewlog logger/handler instantiation code to the file where it's used (since that actually seems like the appropriate place to me), but I have the same problem there. 我已经尝试将sviewlog记录器/处理程序实例化代码移动到它所使用的文件中(因为这实际上对我来说似乎是合适的地方),但我有同样的问题。 Most of the examples I've seen online use only the root logger, and I'd prefer to have multiple loggers. 我在网上看到的大多数例子都只使用根记录器,而我更喜欢使用多个记录器。

Allow me to answer my own question. 请允许我回答我自己的问题。 The underlying problem here is that settings.py gets imported twice, or maybe even more (See here ). 这里的根本问题是settings.py被导入两次,甚至更多(见这里 )。 (I still don't understand why this is. Maybe some Django expert could explain that to me.) This seems to be true of some other modules as well. (我仍然不明白为什么会这样。也许一些Django专家可以向我解释。)这似乎也适用于其他一些模块。 At this point I don't think it's wise to make assumptions about how many times settings.py will be imported. 在这一点上,我认为假设将导入settings.py的次数是不明智的。 For that matter, such assumptions aren't safe in general. 就此而言,这种假设一般不安全。 I've had this code in places other than settings.py, and the results are similar. 我在settings.py以外的地方有这个代码,结果很相似。

You have to code around this. 你必须围绕这个编码。 That is, you have to check your logger for existing handlers before adding additional handlers to it. 也就是说,在向其添加其他处理程序之前,您必须检查记录器中的现有处理程序。 This is a bit ugly because it's perfectly reasonable to have multiple handlers -- even of the same type -- attached to one logger. 这有点难看,因为将多个处理程序(即使是相同类型)连接到一个记录器是完全合理的。 There are a few solutions to dealing with this. 有一些解决方案可以解决这个问题。 One is check the handlers property of your logger object. 一个是检查记录器对象的处理程序属性。 If you only want one handler and your length > 0, then don't add it. 如果您只需要一个处理程序且长度> 0,则不要添加它。 Personally I don't love this solution, because it gets messy with more handlers. 就个人而言,我不喜欢这个解决方案,因为它会让更多处理程序变得混乱。

I prefer something like this (thanks to Thomas Guettler): 我更喜欢这样的东西(感谢Thomas Guettler):

# file logconfig.py
if not hasattr(logging, "set_up_done"):
    logging.set_up_done=False

def set_up(myhome):
    if logging.set_up_done:
        return
    # set up your logging here
    # ...
    logging.set_up_done=True

I must say, I wish the fact that Django imports settings.py multiple times were better documented. 我必须说,我希望Django多次导入settings.py更好地记录下来。 And I would imagine that my configuration is somehow cause this multiple import, but I'm having trouble finding out what is causing the problem and why. 而且我会想象我的配置会以某种方式导致这种多重导入,但我无法找到导致问题的原因以及原因。 Maybe I just couldn't find that in their documents, but I would think that's the sort of thing you need to warn your users about. 也许我只是在他们的文档中找不到,但我认为这是你需要警告用户的事情。

As of version 1.3, Django uses standard python logging, configured with the LOGGING setting (documented here: 1.3 , dev ). 从版本1.3开始,Django使用标准的python日志记录,使用LOGGING设置进行配置(此处记录: 1.3dev )。

Django logging reference: 1.3 , dev . Django日志参考: 1.3dev

Difficult to comment on your specific case. 很难对您的具体案例发表评论。 If settings.py is executed twice, then it's normal that you get two lines for every log sent. 如果settings.py执行两次,那么每发送一次日志就会得到两行是正常的。

We had the same issue, so we set it up in our projects to have one module dedicated to logging. 我们遇到了同样的问题,所以我们在项目中设置了一个专门用于记录的模块。 That modules has a "module singleton" pattern, so that we only execute the interesting code once. 那些模块有一个“模块单例”模式,所以我们只执行一次有趣的代码。

It looks like this: 它看起来像这样:

def init_logging():
    stdoutHandler = logging.StreamHandler( sys.stdout )
    stdoutHandler.setLevel( DEBUG )
    stdoutHandler.setFormatter( logging.Formatter( LOG_FORMAT_WITH_TIME ) )
    logging.getLogger( LOG_AREA1 ).addHandler( stdoutHandler )

logInitDone=False #global variable controlling the singleton.
if not logInitDone:
    logInitDone = True
    init_logging()

Importing the log.py the first time will configure the logging correctly. 首次导入log.py将正确配置日志记录。

Reviving an old thread, but I was experiencing duplicate messages while using Django 1.3 Python logging with the dictConfig format . 恢复旧线程,但在使用dictConfig格式的 Django 1.3 Python日志记录时,我遇到了重复的消息。

The disable_existing_loggers gets rid of the duplicate handler/logging problem with multiple settings.py loads, but you can still get duplicate log messages if you don't specify the propagate boolean appropriately on the specific logger . disable_existing_loggers通过多个settings.py加载消除了重复的处理程序/日志记录问题,但如果未在特定logger上正确指定propagate布尔值,则仍可以获取重复的日志消息。 Namely, make sure you set propagate=False for child loggers. 即,确保为子记录器设置propagate=False Eg, 例如,

'loggers': {
    'django': {
        'handlers':['null'],
        'propagate': True,
        'level':'INFO',
    },
    'django.request': {
        'handlers': ['console'],
        'level': 'ERROR',
        'propagate': False,
    },
    'project': {
        'handlers': ['console', 'project-log-file'],
        'level': 'DEBUG',
        'propagate': True,
    },
    'project.customapp': {
        'handlers': ['console', 'customapp-log-file'],
        'level': 'DEBUG',
        'propagate': False,
    },
}

Here, project.customapp sets propagate=False so that it won't be caught by the project logger as well. 这里, project.customapp设置propagate=False以便它也不会被project记录器捕获。 The Django logging docs are excellent, as always. Django日志记录文档一如既往地非常出色。

To answer the question about why does "Django imports settings.py multiple times": it does not. 回答关于为什么“Django多次导入settings.py”的问题:它没有。

Actually, it does get imported twice (skip past the first code chunk to get right into it but a good read if you've got the time): 实际上,它确实会被导入两次(跳过第一个代码块以进入它,但如果你有时间则读取一个好的):

http://blog.dscpl.com.au/2010/03/improved-wsgi-script-for-use-with.html http://blog.dscpl.com.au/2010/03/improved-wsgi-script-for-use-with.html

PS– Sorry to revive an old thread. PS-抱歉恢复旧线程。

You could get around your problem by checking for the number of handlers when you are doing your init. 您可以通过在执行init时检查处理程序的数量来解决您的问题。

def init_logging():
    stdoutHandler = logging.StreamHandler( sys.stdout )
    stdoutHandler.setLevel( DEBUG )
    stdoutHandler.setFormatter( logging.Formatter( LOG_FORMAT_WITH_TIME ) )
    logger = logging.getLogger( LOG_AREA1 )
    if len(logger.handlers) < 1:
        logger.addHandler( stdoutHandler )

I don't think this is a great way to handle it. 我不认为这是处理它的好方法。 Personally, for logging in django with the python logging module, I create a logger in views.py for each application I'm interested in, then grab the logger in each view function. 就个人而言,为了使用python日志记录模块登录django,我在views.py中为我感兴趣的每个应用程序创建一个记录器,然后在每个视图函数中获取记录器。

from django.http import HttpResponse
from magic import makeLogger
from magic import getLogger

makeLogger('myLogName', '/path/to/myLogName.log')
def testLogger(request):
    logger = getLogger('myLogName')
    logger.debug('this worked')
    return HttpResponse('TEXT, HTML or WHATEVER')

This is a pretty good article about debugging django and covers some logging: http://simonwillison.net/2008/May/22/debugging/ 这是关于调试django的一篇非常好的文章,并涵盖了一些日志记录: http//simonwillison.net/2008/May/22/debugging/

To answer the question about why does "Django imports settings.py multiple times": it does not. 回答关于为什么“Django多次导入settings.py”的问题:它没有。

You are probably running a multiprocess/multithread web server which creates several python sub-interpreters, where each of those imports the code from your django app once. 您可能正在运行一个多进程/多线程Web服务器,它创建了几个python子解释器,其中每个子解释器都会从您的django应用程序中导入一次代码。

Test that on the django test server and you should see that settings are not imported many times. 在django测试服务器上测试,您应该看到多次导入设置。

Some time ago, I had designed a nice singleton (python borg idiom version to be more precise) with my first django/apache app, before I quickly realised that yes, I had more than one instances of my singleton created... 前段时间,我用我的第一个django / apache应用程序设计了一个不错的单例(python borg成语版本更加精确),然后我很快意识到是的,我创建了多个单例实例...

You can also use a run-once Middleware to get a similar effect, without the private variables. 您还可以使用一次运行的中间件来获得类似的效果,而不使用私有变量。 Note that this will only configure the logging for web-requests - you'll need to find a different solution if you want logging in your shell or command runs. 请注意,这只会配置Web请求的日志记录 - 如果要在shell或命令运行中登录,则需要找到不同的解决方案。

from django.conf import settings
from django.core.exceptions import MiddlewareNotUsed
import logging
import logging.handlers
import logging.config

__all__ = ('LoggingConfigMiddleware',)


class LoggingConfigMiddleware:
    def __init__(self):
        '''Initialise the logging setup from settings, called on first request.'''
        if hasattr(settings, 'LOGGING'):
            logging.config.dictConfig(settings.LOGGING)
        elif getattr(settings, 'DEBUG', False):
            print 'No logging configured.'
        raise MiddlewareNotUsed('Logging setup only.')

Why using python logger instead of django-logging? 为什么使用python logger而不是django-logging? Give it a try it might just solve your problem. 试一试它可能只是解决你的问题。

http://code.google.com/p/django-logging/wiki/Overview http://code.google.com/p/django-logging/wiki/Overview

At the moment it would only allow to view the root logger, but you can sure write to multiple loggers. 目前它只允许查看根记录器,但您可以确保写入多个记录器。

A hackish way, but you can try to put the logging code inside an admin.py. 一种hackish方式,但您可以尝试将日志代码放在admin.py中。 It is supposed to be imported only once. 它应该只导入一次。

Alternatively; 另外; can you first check if MyApp.views.scans log exists? 你能先检查MyApp.views.scans日志是否存在吗? If it exists (maybe an error is raised) you can simply skip creation (and therefore not add the handler again). 如果它存在(可能引发错误),您可以简单地跳过创建(因此不再添加处理程序)。 A cleaner way but I haven't tried this though. 一个更干净的方式,但我没有尝试过这个。

Also there must be a more appropriate place to put this code ( __init__.py ?). 还有一个更适合放置此代码的地方( __init__.py ?)。 settings.py is for settings. settings.py用于设置。

To add to A Lee post, python logging documentation states this about propagate: 要添加到A Lee帖子,python日志记录文档说明了有关传播的内容:

Logger.propagate Logger.propagate

If this evaluates to false, logging messages are not passed by this logger or by its child loggers to the handlers of higher level (ancestor) loggers. 如果此计算结果为false,则此记录器或其子记录器不会将记录消息传递给更高级别(祖先)记录器的处理程序。 The constructor sets this attribute to 1. 构造函数将此属性设置为1。

This means that if propagate == False then child logger will NOT pass logging message to its parent logger 这意味着如果propagate == False子记录器不会将记录消息传递给其父记录器

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM