【问题标题】:django.request logger not propagated to root?django.request 记录器没有传播到根目录?
【发布时间】:2013-12-15 11:01:05
【问题描述】:

使用 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

【问题讨论】:

  • 感谢您在 stackoverflow 上提出这个问题。我还可以问您,根据默认日志记录配置,键“传播”设置为 false,所以,在您的情况下,它没有传播到根记录器,但它是否随后将有关错误的邮件发送给管理员?跨度>

标签: python django logging


【解决方案1】:

解决办法是阻止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 的可怜的 explanationdisable_existing_loggers,它表示当为 True 时,“默认配置被完全覆盖”。在您自己的答案中,您发现这是不正确的;发生的事情是 Django 已经配置的现有记录器禁用没有被替换。

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

disable_existing_loggers – 如果指定为 False,则存在的记录器 拨打此电话时,请不要理会。默认值为 True 因为 这以向后兼容的方式启用旧行为。这 行为是禁用任何现有的记录器,除非他们或他们的 祖先在日志配置中明确命名。

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

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

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

【讨论】:

  • 非常感谢您,我已经为这个问题努力了一个小时。 LOGGING_CONFIG = 没有办法。
  • 多年来我一直在努力解决这个问题(断断续续)! ;) 这太棒了,我终于设法设置了一个我自己理解的日志记录配置!
  • 另外,分析当前日志配置的好工具是logging_tree
【解决方案2】:

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

【讨论】:

    【解决方案3】:

    对于 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 的文档一致,其中声明:

    注意

    如果您将处理程序附加到记录器及其一个或多个祖先, 它可能会多次发出相同的记录。一般来说,你不应该 需要将处理程序附加到多个记录器 - 如果您只是附加 它到记录器层次结构中最高的适当记录器, 然后它将看到所有后代记录器记录的所有事件,前提是 他们的传播设置被设置为真。一个常见的场景是 仅将处理程序附加到根记录器,并让传播 剩下的就自己处理了。

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

    尚未在生产中进行测试,但它似乎会按预期工作。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-09-28
      • 1970-01-01
      • 2012-05-04
      • 2015-02-18
      • 1970-01-01
      • 2016-04-20
      • 2021-03-23
      • 1970-01-01
      相关资源
      最近更新 更多