在Django中进行Python日志logging

我正在开发一个Django应用程序,我正在尝试使用Python的日志logging模块进行错误/跟踪logging。 理想情况下,我想为站点的不同区域configuration不同的logging器。 到目前为止,我已经完成了所有这些工作,但有一件事让我挠头。

我有根logging器去sys.stderr,我已经configuration另一个logging器写入文件。 这是在我的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) 

看起来很简单。 但是,问题在于:无论我写入sviewlog,都会被写入日志文件两次。 根logging器只打印一次。 这就像addHandler()被调用两次。 而当我通过一个debugging器把我的代码,这正是我所看到的。 settings.py中的代码被执行了两次,因此创build了两个FileHandler并将其添加到同一个logging器实例中。 但为什么? 我该如何解决这个问题?

谁能告诉我这里发生了什么事? 我已经尝试移动sviewloglogging器/处理程序实例化代码到它使用的文件(因为这实际上似乎是适当的地方给我),但我有同样的问题。 我在网上看到的大多数例子都只使用根logging器,而我更喜欢有多个logging器。

请允许我回答我自己的问题。 这里的根本问题是settings.py被导入两次,或者甚至更多(请看这里 )。 (我还是不明白这是为什么,也许一些Django的专家可以向我解释这个)。对于其他一些模块来说,这似乎也是如此。 在这一点上,我不认为这是明智的做出settings.py将被导入多less次的假设。 就此而言,这样的假设一般来说是不安全的。 我在settings.py以外的地方使用了这个代码,结果是相似的。

你必须在此编码。 也就是说,在添加其他处理程序之前,您必须先检查现有的处理程序。 这有点难看,因为将多个处理程序(甚至是同一types的处理程序)连接到一个logging程序是完全合理的。 有几个解决scheme来处理这个问题。 一个是检查你的logging器对象的处理程序属性。 如果您只需要一个处理程序,并且您的长度> 0,则不要添加它。 就我个人而言,我不喜欢这个解决scheme,因为更多的处理程序让它变得混乱。

我更喜欢这样的东西(感谢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 

我必须说,我希望事实Djangoimportsettings.py多次更好地logging。 而且我会想象我的configuration在某种程度上会导致这种多重导入,但是我很难找出导致问题的原因以及为什么。 也许我只是无法在他们的文档中find这些信息,但是我认为这就是您需要提醒用户注意的事情。

从版本1.3开始,Django使用标准的Python日志logging,configurationLOGGING设置(这里logging: 1.3 , dev )。

Django日志参考: 1.3 , dev 。

很难评论你的具体情况。 如果settings.py被执行了两次,那么每发送一个日志都会得到两行是正常的。

我们遇到了同样的问题,所以我们在项目中设置了一个专门用于logging的模块。 那个模块有一个“module singleton”模式,所以我们只执行一次有趣的代码。

它看起来像这样:

 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() 

第一次导入log.py会正确configuration日志。

恢复旧的线程,但我遇到重复的消息,同时使用dictConfig格式的 Django 1.3 Python日志logging。

disable_existing_loggers通过多个settings.py加载消除重复的处理程序/日志logging问题,但是如果您没有在特定的日志logging程序上指定propagate布尔值,仍然可以获得重复的日志消息。 也就是说,确保你为子logging器设置了propagate=False 。 例如,

 '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, }, } 

在这里, project.customapp设置propagate=False这样它也不会被projectlogging器捕获。 与往常一样, Django日志文档非常出色。

“为了回答”Django多次导入settings.py“的问题:它不会。”

实际上,它会被导入两次(跳过第一个代码块直接进入它,但是如果你有时间的话可以阅读):

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

PS–对不起,要恢复旧的线程。

您可以通过在执行初始化时检查处理程序的数量来解决问题。

 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 ) 

我不认为这是一个好办法。 就个人而言,为了使用python日志模块logindjango,我为每个我感兴趣的应用程序在views.py中创build一个日志logging器,然后在每个视图函数中抓取日志logging器。

 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') 

这是一个相当不错的文章,关于debuggingdjango和涵盖一些日志logging: http : //simonwillison.net/2008/May/22/debugging/

要回答为什么“Django多次导入settings.py”的问题:它不。

您可能正在运行一个多进程/multithreading的Web服务器,它创build了几个Python子解释器,其中每个都从django应用程序导入一次代码。

testing在Django的testing服务器上,你应该看到设置不会被多次导入。

前一段时间,我用我的第一个django / apache应用程序devise了一个不错的单例(python borg idiom版本更准确),然后我很快意识到是的,我创build了不止一个单例。

您也可以使用一次运行的中间件来获得类似的效果,而不使用私有variables。 请注意,这只会configurationweb请求的日志logging – 如果您想要login到shell或命令运行,则需要find其他解决scheme。

 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.') 

为什么使用python logger而不是django-logging? 试试看,它可能只是解决你的问题。

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

目前它只允许查看根logging器,但您可以确定写入多个logging器。

一个骇客的方式,但是你可以尝试把日志代码放在一个admin.py中。 它应该只能导入一次。

另外; 你能首先检查MyApp.views.scans日志是否存在? 如果存在(可能会引发错误),则可以简单地跳过创build(因此不要再添加处理程序)。 一个更清洁的方式,但我还没有尝试过这一点。

也必须有一个更合适的地方来放这个代码( __init__.py ?)。 settings.py是用于设置的。

要添加到李的文章,python日志logging文件陈述这个传播:

Logger.propagate

如果计算结果为false,则日志logging消息不会被此logging器或其子logging器传递给更高级别(祖先)logging器的处理程序。 构造函数将此属性设置为1。

这意味着如果propagate == False子logging器不会将日志消息传递给其父logging器