使用Django 1.5.1:
DEBUG = False
LOGGING = {
'version': 1,
'disable_existing_loggers': True,
'formatters': {
'verbose': {
'format': '%(levelname)s %(asctime)s %(module)s %(message)s'
},
},
'handlers': {
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'formatter': 'verbose',
},
},
'loggers': {
# root logger
'': {
'handlers': ['console'],
},
#'django.request': {
# 'handlers': ['console'],
# 'level': 'DEBUG',
# 'propagate': False,
#},
}
}
如果我取消注释注释行并调用具有1/0
的视图,则回溯将打印到控制台:
ERROR 2013-11-29 13:33:23,102 base Internal Server Error: /comment/*******/
Traceback (most recent call last):
...
File "*****/comments/views.py", line 10, in post
1/0
ZeroDivisionError: integer division or modulo by zero
WARNING 2013-11-29 13:33:23,103 csrf Forbidden (CSRF cookie not set.): /comment/******/
[29/Nov/2013 13:33:23] "POST /comment/******/ HTTP/1.0" 500 27
但是,如果行保持注释,则不会将回溯打印到控制台,只是:
[29/Nov/2013 13:33:23] "POST /comment/******/ HTTP/1.0" 500 27
我想如果不配置django.request
记录器,它会传播到根记录器,它将所有内容打印到控制台。
我没有发现任何关于django.request
特别的信息。
为什么它不起作用?
我在这里读到:
在Django 1.5之前,LOGGING设置总是覆盖默认的Django日志配置。从Django 1.5开始,可以将项目的日志记录配置与Django的默认配置合并,因此您可以决定是添加还是替换现有配置。
如果LOGGING dictConfig中的disable_existing_loggers键设置为True(默认值),则会完全覆盖默认配置。或者,您可以通过将disable_existing_loggers设置为False来重新定义部分或全部记录器。
在django/utils/log.py
:中
# Default logging for Django. This sends an email to the site admins on every
# HTTP 500 error. Depending on DEBUG, all other log records are either sent to
# the console (DEBUG=True) or discarded by mean of the NullHandler (DEBUG=False).
DEFAULT_LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'filters': {
'require_debug_false': {
'()': 'django.utils.log.RequireDebugFalse',
},
'require_debug_true': {
'()': 'django.utils.log.RequireDebugTrue',
},
},
'handlers': {
'console':{
'level': 'INFO',
'filters': ['require_debug_true'],
'class': 'logging.StreamHandler',
},
'null': {
'class': 'django.utils.log.NullHandler',
},
'mail_admins': {
'level': 'ERROR',
'filters': ['require_debug_false'],
'class': 'django.utils.log.AdminEmailHandler'
}
},
'loggers': {
'django': {
'handlers': ['console'],
},
'django.request': {
'handlers': ['mail_admins'],
'level': 'ERROR',
'propagate': False,
},
'py.warnings': {
'handlers': ['console'],
},
}
}
因此,默认情况下django.request
具有propagate = False
。但就我而言,我有'disable_existing_loggers': True
。
解决方案是防止Django配置日志并自己处理。幸运的是,这很容易。在settings.py
:中
LOGGING_CONFIG = None
LOGGING = {...} # whatever you want, as you already have
import logging.config
logging.config.dictConfig(LOGGING)
更新~ 2015年3月:Django已经澄清了他们的文档:
如果设置了LOGGING dictConfig中的disable_existing_loggers键设置为True,则默认值中的所有记录器配置将被禁用。禁用的记录器与删除;记录器将仍然存在,但将静默地丢弃任何记录到它的内容,甚至不将条目传播到父项记录器。因此,您应该非常小心地使用'disable_existing_loggers':True;这可能不是你想要的。相反,您可以将disable_existing_loggers设置为False并重新定义部分或全部默认记录器;或者可以将LOGGING_CONFIG设置为无,自己处理日志配置。
为了子孙后代和细节:解释?我认为,大多数困惑都源于Django对disable_existing_loggers
的糟糕解释,即当为True时,"默认配置将被完全覆盖"。在你自己的回答中,你发现这是不正确的;现在的情况是Django已经配置的现有记录器被禁用了而没有被替换。
Python日志记录文档更好地解释了这一点(增加了重点):
disable_existing_loggers–如果指定为False,则存在的记录器当打这个电话的时候,他们就不用管了。默认值为True,因为这使得旧的行为可以向后兼容。这行为是禁用任何现有的记录器,除非它们祖先在日志记录配置中显式命名。
基于Django文档,我们认为"用我自己的LOGGING配置覆盖默认值,我没有指定的任何内容都会出现"。我也被这种期望绊倒了。我们期望的行为遵循replace_existing_loggers(这不是真实的)。相反,Django记录程序是关闭,而不是弹出。
我们首先需要防止这些Django记录器的设置,这里的Django文档更有用:
如果您根本不想配置日志记录(或者您想手动使用您自己的方法配置日志),您可以设置logging_CONFIG至无。这将禁用配置过程。
注意:将LOGGING_CONFIG设置为None仅表示配置进程被禁用,而不是日志记录本身。如果禁用配置过程中,Django仍然会进行日志调用返回到定义的任何默认日志记录行为。
Django仍然会使用它的记录器,但由于它们没有被配置处理(然后被禁用),这些记录器将按预期出现。以上设置的简单测试:
manage.py shell
>>> import logging
>>> logging.warning('root logger')
WARNING 2014-03-11 13:35:08,832 root root logger
>>> l = logging.getLogger('django.request')
>>> l.warning('request logger')
WARNING 2014-03-11 13:38:22,000 django.request request logger
>>> l.propagate, l.disabled
(1, 0)
对于Django-2.1,我发现日志配置更简洁:
$ ./manage.py shell
>>> import logging
>>> # Grub all Django loggers
>>> loggers = [
name for name in logging.root.manager.loggerDict
if 'django' in name
]
>>> for each in loggers:
logger = logging.getLogger(each)
print(
'Logger Name: {0}nLogger Handlers: {1}n'
'Logger Propagates: {2}nn'.format(
each,
logger.handlers,
logger.propagate
)
)
Logger Name: django.db
Logger Handlers: []
Logger Propagates: True
Logger Name: django.request
Logger Handlers: []
Logger Propagates: True
Logger Name: django.template
Logger Handlers: []
Logger Propagates: True
Logger Name: django.db.backends
Logger Handlers: []
Logger Propagates: True
Logger Name: django.db.backends.schema
Logger Handlers: []
Logger Propagates: True
Logger Name: django.security.csrf
Logger Handlers: []
Logger Propagates: True
Logger Name: django
Logger Handlers: [<logging.StreamHandler object at 0x7f706d5dd780>, <django.utils.log.AdminEmailHandler object at 0x7f706d740cf8>]
Logger Propagates: True
Logger Name: django.contrib.gis
Logger Handlers: []
Logger Propagates: True
Logger Name: django.contrib
Logger Handlers: []
Logger Propagates: True
Logger Name: django.security
Logger Handlers: []
Logger Propagates: True
Logger Name: django.server
Logger Handlers: [<logging.StreamHandler object at 0x7f706d59eba8>]
Logger Propagates: False
如文件中所引用:
除了django.server之外的所有记录器都将日志记录传播到它们的父级,直到根django记录器。控制台和mail_admins处理程序连接到根记录器,以提供上述行为。
这与propagate
的文件一致,其中规定:
票据
如果将处理程序附加到记录器及其一个或多个祖先,它可以多次发出相同的记录。一般来说,你不应该需要将一个处理程序附加到多个记录器-如果您只是附加将其发送到记录器层次结构中最高的适当记录器,然后它将看到所有子记录器记录的所有事件将其传播设置保留为True。常见的情况是仅将处理程序附加到根记录器,并允许传播照顾好其余的。
因此,我决定不阻止Django配置日志记录。我想停止向管理员发送电子邮件,因为我使用了sentry,根据django文档的示例,我只是将根记录器配置为使用console
和file
处理程序:
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'verbose': {
'format': '{levelname} {asctime} {module} {process:d} {thread:d} {message}',
'style': '{',
},
'simple': {
'format': '{levelname} {message}',
'style': '{',
},
},
'filters': {
'require_debug_false': {
'()': 'django.utils.log.RequireDebugFalse',
},
'require_debug_true': {
'()': 'django.utils.log.RequireDebugTrue',
},
},
'handlers': {
'console': {
'level': 'INFO',
'filters': ['require_debug_true'],
'class': 'logging.StreamHandler',
'formatter': 'simple'
},
'file': {
'level': 'INFO',
'filters': ['require_debug_false'],
'class': 'logging.FileHandler',
'filename': os.path.join(LOGGING_DIR, 'django.log'),
'formatter': 'verbose'
},
},
'loggers': {
'django': {
'handlers': ['file', 'console'],
'level': 'INFO',
'propagate': True,
},
}
}
结果是:
Logger Name: django
Logger Handlers: [<logging.FileHandler object at 0x7f5aa0fd1cc0>, <logging.StreamHandler object at 0x7f5aa0fd1ef0>]
Logger Propagates: True
尚未在生产中进行测试,但它似乎会像预期的那样工作。
好的,所以行为是"正确的",但不是预期的。django/conf/__init__.py:65
:
def _configure_logging(self):
...
if self.LOGGING_CONFIG:
from django.utils.log import DEFAULT_LOGGING
# First find the logging configuration function ...
logging_config_path, logging_config_func_name = self.LOGGING_CONFIG.rsplit('.', 1)
logging_config_module = importlib.import_module(logging_config_path)
logging_config_func = getattr(logging_config_module, logging_config_func_name)
logging_config_func(DEFAULT_LOGGING)
if self.LOGGING:
# Backwards-compatibility shim for #16288 fix
compat_patch_logging_config(self.LOGGING)
# ... then invoke it with the logging settings
logging_config_func(self.LOGGING)
实际情况是应用了默认的日志记录配置,并创建了django.request
记录器。然后,我的自定义LOGGING
配置与disable_existing_loggers = True
一起应用,但Python不会删除已经存在的记录器django.request
,而只是禁用它。
因此,我必须在配置中手动重新配置django.request
记录器(