django.request记录器没有传播到root?

时间:2013-11-29 09:37:23

标签: python django logging

使用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特殊的任何信息。

为什么它不起作用?

Here我读到了:

  

在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.requestpropagate = False。但就我而言,我有'disable_existing_loggers': True

3 个答案:

答案 0 :(得分:72)

解决方案是阻止Django配置日志记录并自行处理。幸运的是,这很容易。在settings.py

LOGGING_CONFIG = None
LOGGING = {...}  # whatever you want, as you already have

import logging.config
logging.config.dictConfig(LOGGING)

更新〜2015年3月:Django已clarified documentation

  

如果设置了LOGGING dictConfig中的disable_existing_loggers键   从True到默认的所有记录器   配置将被禁用。禁用记录器不一样   删除;记录器仍然存在,但会默默地丢弃   记录到它的任何内容,甚至没有将条目传播到父级   记录仪。因此你应该非常小心使用   'disable_existing_loggers':是的;它可能不是你想要的。   相反,您可以将disable_existing_loggers设置为False并重新定义   部分或全部默认记录器;或者你可以设置LOGGING_CONFIG   无,并自己处理日志配置。

为了后人和细节:解释?我认为大多数混乱都归结为Django disable_existing_loggers的{​​{1}},其中说明当“True”时,“默认配置被完全覆盖”。在你自己的回答中,你发现这是不正确的;发生的事情是Django已配置的现有记录器禁用未被替换。

Python日志记录explanation更好地解释了它(强调添加):

  

disable_existing_loggers - 如果指定为False,则存在记录器   进行此调用时,请保持不变。默认值为True,因为   这可以以向后兼容的方式启用旧行为。这个   行为是禁用任何现有的记录器,除非他们或他们的   祖先在日志记录配置中明确命名。

基于我们认为的Django文档,“使用我自己的LOGGING配置覆盖默认值,而我未指定的任何内容将冒泡”。我也绊倒了这个期望。我们期望的行为是 replace_existing_loggers (这不是真实的)。相反,Django记录器关闭而不是冒泡

我们需要首先阻止这些Django记录器的设置,这里Django documentation更有帮助:

  

如果您根本不想配置日志记录(或者您想手动配置)   使用您自己的方法配置日志记录),您可以设置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)

答案 1 :(得分:1)

好的,所以行为是“正确的”,但不是预期的。 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记录器。 :(

答案 2 :(得分: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}\n\n'.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

引用in the docs

  

除django.server以外的所有记录器都将日志记录传播给其父级,直到根django记录器为止。控制台和mail_admins处理程序已附加到根记录器,以提供上述行为。

这与propagate的文档一致,该文档指出:

  

注意

     

如果您将处理程序附加到记录器及其一个或多个祖先,   它可能会多次发出同一条记录。一般来说,您不应该   需要将一个处理程序附加到多个记录器上-如果您只是附加   将其发送到记录器层次结构中最高的适当记录器,   然后它将看到所有后代记录器记录的所有事件   将其传播设置保留为True。常见的情况是   仅将处理程序附加到根记录器,并允许传播   照顾好其余的。

因此,我决定不阻止Django配置日志记录。我想停止向管理员发送电子邮件,因为我使用sentry,并且我刚刚将根记录程序配置为使用consolefile处理程序,根据Django文档{{3} }:

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

尚未在生产中进行测试,但似乎可以正常工作。

相关问题