Подтвердить что ты не робот

Django.request logger не распространяется на 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 logger не настроен, он будет распространяться на корневой журнал, который печатает все на консоли.

Я не нашел информации о том, что django.request является специальным.

Почему это не работает?

Здесь Я читал:

До Django 1.5 параметр LOGGING всегда перезаписывал конфигурацию регистрации Django по умолчанию. Начиная с Django 1.5 вперед можно включить конфигурацию регистрации проектов, объединенную с по умолчанию Djangos, поэтому вы можете решить, хотите ли вы добавить или заменить существующую конфигурацию.

Если для параметра disable_existing_loggers в LOGGING dictConfig установлено значение 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.

4b9b3361

Ответ 1

Решение состоит в том, чтобы запретить Django настраивать ведение журнала и обрабатывать его самостоятельно. К счастью, это легко. В settings.py:

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

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

UPDATE ~ March 2015: Django имеет уточнил свои документация:

Если установлен ключ disable_existing_loggers в LOGGING dictConfig на 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 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)

Ответ 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, а только отключает его.

Поэтому мне приходится вручную перенастраивать log2 django.request в мою конфигурацию.: (

Ответ 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

Как указано в документах:

Все регистраторы, кроме django.server, распространяют регистрацию своих родителей, вплоть до корневого регистратора django. Консоль и обработчики mail_admins подключаются к корневому журналу, чтобы обеспечить описанное выше поведение.

Это соответствует документам propagate которых говорится, что:

Заметка

Если вы присоедините обработчик к регистратору и одному или нескольким его предкам, он может издавать одну и ту же запись несколько раз. В общем случае вам не нужно прикреплять обработчик к нескольким регистраторам - если вы просто присоедините его к соответствующему регистратору, который является самым высоким в иерархии регистратора, тогда он увидит все события, регистрируемые всеми регистраторами-потомками при условии, что их распространение для параметра установлено значение True. Обычный сценарий заключается в том, чтобы привязать обработчики только к корневому журналу и позволить распространению заботиться обо всем остальном.

Поэтому я решил не запрещать Django настраивать ведение журнала. Я хотел прекратить отправлять электронные письма администраторам, потому что я использую часовое, и я только что настроил корневой журнал для использования console и обработчиков file соответствии с примерами django docs:

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

Пока не тестировалось в производстве, но похоже, что он будет работать так, как ожидалось.