Django.request logger未传播到根目录?

2024-06-06 15:02:26 发布

您现在位置:Python中文网/ 问答频道 /正文

使用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.requestlogger没有配置,它会传播到根logger,根logger会将所有内容打印到控制台。

我没有发现任何关于django.request的特殊信息。

为什么不起作用?

Here我读到:

Prior to Django 1.5, the LOGGING setting always overwrote the default Django logging configuration. From Django 1.5 forward, it is possible to get the project’s logging configuration merged with Django’s defaults, hence you can decide if you want to add to, or replace the existing configuration.

If the disable_existing_loggers key in the LOGGING dictConfig is set to True (which is the default) the default configuration is completely overridden. Alternatively you can redefine some or all of the loggers by setting disable_existing_loggers to 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


Tags: thetodjangodebuglogfalselogginghandlers
3条回答

解决方案是防止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

If the disable_existing_loggers key in the LOGGING dictConfig is set to True then all loggers from the default configuration will be disabled. Disabled loggers are not the same as removed; the logger will still exist, but will silently discard anything logged to it, not even propagating entries to a parent logger. Thus you should be very careful using 'disable_existing_loggers': True; it’s probably not what you want. Instead, you can set disable_existing_loggers to False and redefine some or all of the default loggers; or you can set LOGGING_CONFIG to None and handle logging config yourself.

对于后代和细节:解释?我认为大多数的混乱都是由于Django的explanation糟糕的disable_existing_loggers,它说如果是真的,“默认配置被完全覆盖”。在您自己的回答中,您发现这是不正确的;正在发生的是,Django已经配置的现有记录器被禁用了,没有被替换。

Python日志记录documentation更好地解释了这一点(添加了重点):

disable_existing_loggers – If specified as False, loggers which exist when this call is made are left alone. The default is True because this enables old behaviour in a backward-compatible way. This behaviour is to disable any existing loggers unless they or their ancestors are explicitly named in the logging configuration.

基于Django文档,我们认为,“用我自己的日志配置覆盖默认值,而我没有指定的任何内容都将冒泡起来”。我也被这个期望绊倒了。我们期望的行为是按照替换现有的记录器(这不是真的)。取而代之的是,Django伐木工人是闭嘴而不是冒泡而来。

我们首先需要防止这些Django记录器的设置,这里Djangodocs更有用:

If you don’t want to configure logging at all (or you want to manually configure logging using your own approach), you can set LOGGING_CONFIG to None. This will disable the configuration process.

Note: Setting LOGGING_CONFIG to None only means that the configuration process is disabled, not logging itself. If you disable the configuration process, Django will still make logging calls, falling back to whatever default logging behavior is defined.

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}\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

All loggers except django.server propagate logging to their parents, up to the root django logger. The console and mail_admins handlers are attached to the root logger to provide the behavior described above.

这符合^{}的文件,其中规定:

Note

If you attach a handler to a logger and one or more of its ancestors, it may emit the same record multiple times. In general, you should not need to attach a handler to more than one logger - if you just attach it to the appropriate logger which is highest in the logger hierarchy, then it will see all events logged by all descendant loggers, provided that their propagate setting is left set to True. A common scenario is to attach handlers only to the root logger, and to let propagation take care of the rest.

因此,我决定不阻止Django配置日志记录。我想停止向管理员发送电子邮件,因为我使用了sentry,而且根据django文档examples,我只是将根日志配置为使用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记录器。:(一)

相关问题 更多 >