django.request记录器未传播到root



使用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文档的示例,我只是将根记录器配置为使用consolefile处理程序:

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记录器(

最新更新