Элегантная настройка ведения журнала 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 с ошибками. У нас есть "журнал приложений", в котором используется обычный файл скользящего журнала, который отправляется в каталог журналов.