Элегантная настройка ведения журнала Python в Django

Мне еще предстоит найти способ настройки ведения журнала Python с Django, которым я доволен. Мои требования довольно просты:

  • Различные обработчики журналов для разных событий - то есть, я хочу иметь возможность регистрироваться в разных файлах.
  • Легкий доступ к регистраторам в моих модулях. Модуль должен иметь возможность находить свой регистратор без особых усилий.
  • Должно быть легко применимо к модулям командной строки. Части системы - автономные процессы командной строки или демона. Ведение журнала должно легко использоваться с этими модулями.

Моя текущая настройка - использовать файл logging.conf и вести настройку в каждом модуле, из которого я регистрирую. Это не так.

У вас есть настройка ведения журнала, которая вам нравится? Пожалуйста, подробно объясните: как вы настраиваете конфигурацию (используете ли вы logging.conf или устанавливаете его в коде), где/когда вы запускаете регистраторы, и как вы получаете доступ к ним в своих модулях и т.д.

Ответ 1

Лучший способ, который я нашел до сих пор, - инициализировать настройку ведения журнала в settings.py - нигде больше. Вы можете либо использовать файл конфигурации, либо делать это программно поэтапно - это просто зависит от ваших требований. Главное, что я обычно добавляю обработчиков, которые я хочу в корневой журнал, используя уровни и иногда протоколируя. Фильтры для получения событий, которые я хочу для соответствующих файлов, консоли, системных журналов и т.д. Конечно, вы можете добавить обработчики к любым другим регистраторам тоже, но в моем опыте нет необходимости в этом.

В каждом модуле я определяю логгер, используя

logger = logging.getLogger(__name__)

и использовать это для регистрации событий в модуле (и, если я хочу дифференцировать дальше), используйте регистратор, который является потомком созданного выше регистратора.

Если мое приложение будет потенциально использоваться на сайте, который не настраивает ведение журнала в settings.py, я определяю NullHandler где-то следующим образом:

#someutils.py

class NullHandler(logging.Handler):
    def emit(self, record):
        pass

null_handler = NullHandler()

и убедитесь, что его экземпляр добавлен ко всем регистраторам, созданным в модулях моих приложений, которые используют ведение журнала. (Примечание: NullHandler уже находится в пакете протоколирования для Python 3.1 и будет находиться в Python 2.7.) Итак:

logger = logging.getLogger(__name__)
logger.addHandler(someutils.null_handler)

Это делается для того, чтобы ваши модули хорошо играли на сайте, который не настраивает ведение журнала в settings.py, и что вы не получаете раздражающих сообщений "Нет обработчиков для сообщений XYZ журнала" (которые предупреждения о потенциально неправильно сконфигурированном протоколировании).

Выполнение этого так соответствует вашим заявленным требованиям:

  • Вы можете настроить различные обработчики журналов для разных событий, как вы сейчас делаете.
  • Легкий доступ к регистраторам в ваших модулях - используйте getLogger(__name__).
  • Легко применим к модулям командной строки - они также импортируют settings.py.

Обновление: Обратите внимание, что с версии 1.3 Django теперь включает поддержку ведения журнала.

Ответ 2

Я знаю, что это уже решенный ответ, но в соответствии с django >= 1.3 существует новая настройка ведения журнала.

Переход от старого к новому не является автоматическим, поэтому я решил записать его здесь.

И, конечно, еще django doc.

Это базовый conf, созданный по умолчанию с помощью django-admin createproject v1.3 - пробег может измениться с помощью последних версий django:

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'mail_admins': {
            'level': 'ERROR',
            'class': 'django.utils.log.AdminEmailHandler',
        }
    },
    'loggers': {
        'django.request': {
            'handlers': ['mail_admins'],
            'level': 'ERROR',
            'propagate': True,
        }
    }
}

Эта структура основана на стандартном Python logging dictConfig, который диктует следующие блоки:

  • formatters - соответствующее значение будет dict, в котором каждая клавиша является идентификатором форматирования, а каждое значение является dict, описывающим, как настроить соответствующий экземпляр Formatter.
  • filters - соответствующее значение будет dict, в котором каждая клавиша является идентификатором фильтра, и каждое значение является dict, описывающим, как настроить соответствующий экземпляр фильтра.
  • handlers - соответствующее значение будет dict, в котором каждый ключ является идентификатором обработчика, а каждое значение - это dict, описывающее, как настроить соответствующий экземпляр Handler. Каждый обработчик имеет следующие клавиши:

    • class (обязательно). Это полное имя класса обработчика.
    • level (необязательно). Уровень обработчика.
    • formatter (необязательно). Идентификатор форматирования для этого обработчика.
    • filters (необязательно). Список идентификаторов фильтров для этого обработчика.

Обычно я делаю это как минимум:

  • добавить файл .log
  • настроить мои приложения для записи в этот журнал

Это означает:

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'verbose': {
            'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s'
        },
        'simple': {
            'format': '%(levelname)s %(message)s'
        },
    },
    'filters': {
        'require_debug_false': {
            '()': 'django.utils.log.RequireDebugFalse'
        }
    },
    'handlers': {
        'null': {
            'level':'DEBUG',
            'class':'django.utils.log.NullHandler',
        },
        'console':{
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',
            'formatter': 'simple'
        },
        # I always add this handler to facilitate separating loggings
        'log_file':{
            'level': 'DEBUG',
            'class': 'logging.handlers.RotatingFileHandler',
            'filename': os.path.join(VAR_ROOT, 'logs/django.log'),
            'maxBytes': '16777216', # 16megabytes
            'formatter': 'verbose'
        },
        'mail_admins': {
            'level': 'ERROR',
            'filters': ['require_debug_false'],
            'class': 'django.utils.log.AdminEmailHandler',
            'include_html': True,
        }
    },
    'loggers': {
        'django.request': {
            'handlers': ['mail_admins'],
            'level': 'ERROR',
            'propagate': True,
        },
        'apps': { # I keep all my of apps under 'apps' folder, but you can also add them one by one, and this depends on how your virtualenv/paths are set
            'handlers': ['log_file'],
            'level': 'INFO',
            'propagate': True,
        },
    },
    # you can also shortcut 'loggers' and just configure logging for EVERYTHING at once
    'root': {
        'handlers': ['console', 'mail_admins'],
        'level': 'INFO'
    },
}

изменить

См. запросы исключений теперь всегда регистрируются и Ticket # 16288:

Я обновил приведенный выше пример conf, чтобы явно включить правильный фильтр для mail_admins, чтобы по умолчанию сообщения электронной почты не отправлялись, когда debug is True.

Вы должны добавить фильтр:

'filters': {
    'require_debug_false': {
        '()': 'django.utils.log.RequireDebugFalse'
    }
},

и примените его к обработчику mail_admins:

    'mail_admins': {
        'level': 'ERROR',
        'filters': ['require_debug_false'],
        'class': 'django.utils.log.AdminEmailHandler',
        'include_html': True,
    }

В противном случае django.core.handers.base.handle_uncaught_exception не передает ошибки в журнал django.request, если параметры .DEBUG имеют значение True.

Если вы не сделаете этого в Django 1.5, вы получите

DeprecationWarning: у вас нет фильтров, определенных в обработчике журнала mail_admins: добавление неявного фильтра debug-false-only

но все будет работать правильно ОБА в django 1.4 и django 1.5.

** end edit **

Этот conf сильно вдохновлен образцом conf в django doc, но добавляет часть файла журнала.

Я также часто делаю следующее:

LOG_LEVEL = 'DEBUG' if DEBUG else 'INFO'

...
    'level': LOG_LEVEL
...

Тогда в моем коде на Python я всегда добавляю NullHandler в случае, если никакой протокол ведения журнала не определен. Это позволяет избежать предупреждений о том, что обработчик не указан. Особенно полезен для библиотек, которые не обязательно называются только в Django (ref)

import logging
# Get an instance of a logger
logger = logging.getLogger(__name__)
class NullHandler(logging.Handler): #exists in python 3.1
    def emit(self, record):
        pass
nullhandler = logger.addHandler(NullHandler())

# here you can also add some local logger should you want: to stdout with streamhandler, or to a local file...

[...]

logger.warning('etc.etc.')

Надеюсь, это поможет!

Ответ 3

В настоящее время я использую систему регистрации, которую я создал сам. Он использует формат CSV для ведения журнала.

django-csvlog

В этом проекте пока нет полной документации, но я над этим работаю.

Ответ 4

Мы инициализируем ведение журнала на верхнем уровне urls.py с помощью файла logging.ini.

Местоположение logging.ini предоставляется в settings.py, но все.

Каждый модуль затем выполняет

logger = logging.getLogger(__name__)

Чтобы отличить экземпляры тестирования, разработки и производства, у нас есть разные файлы logging.ini. По большей части у нас есть "консольный журнал", который идет только в stderr с ошибками. У нас есть "журнал приложений", в котором используется обычный файл скользящего журнала, который отправляется в каталог журналов.