Записывание пользовательских атрибутов Django в formatter
Как Django может использовать журнал для ведения журнала с использованием пользовательских атрибутов в форматировании? Я имею в виду, например, регистрировать зарегистрированное имя пользователя.
В settings.py
script определена переменная LOGGING:
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'filters': {
'require_debug_false': {
'()': 'django.utils.log.RequireDebugFalse'
},
},
'formatters' : {
'info_format' : {
'format' : '%(asctime)s %(levelname)s - %(message)s',
},
}
}
Я хочу использовать формат, что-то вроде:
'format' : '%(asctime).19s %(levelname)s - %(username)s: %(message)s'
Где имя пользователя будет в настоящее время зарегистрированным пользователем. Возможно, здесь могут быть добавлены любые другие переменные сеанса.
Обходной путь здесь заключается в использовании параметра extra
для методов журнала, который получает словарь с ключами в качестве строк, которые я хочу использовать в строке формата:
logger.info(message, extra={'username' : request.user.username})
Другим (уродливым) решением будет создание атрибута message для включения тех вещей, которые не являются частью атрибутов по умолчанию, которые имеют записи в форматах.
message = request.user.username + " - " + message
logger.info(message)
Но есть ли способ настроить строку форматирования с определенными атрибутами и заставить Django автоматически передавать их в API ведения журнала? Если, например,% (имя пользователя), имя request.user.username, любого другого...
Ответы
Ответ 1
Вы можете использовать фильтр, чтобы добавить свой собственный атрибут. Например:
def add_my_custom_attribute(record):
record.myAttribute = 'myValue'
record.username = record.request.user.username
return True
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'filters': {
...
'add_my_custom_attribute': {
'()': 'django.utils.log.CallbackFilter',
'callback': add_my_custom_attribute,
}
},
'handlers': {
...
'django.server': {
'level': 'INFO',
'class': 'logging.StreamHandler',
'filters': ['add_my_custom_attribute'],
'formatter': 'django.server',
},
},
...
}
Установив фильтр, вы можете обрабатывать каждую запись журнала и решать, следует ли передавать ее из регистратора в обработчик.
Фильтр получает запись журнала, содержащую все данные журнала (например, время, степень серьезности, запрос, код состояния).
Атрибуты записи используются форматированием для форматирования его в строковое сообщение. Если вы добавите свои собственные атрибуты в эту запись, они также будут доступны для форматирования.
Ответ 2
Ключевое слово extra
не является обходным путем. Это самый красноречивый способ написания настраиваемых форматировщиков, если вы вообще не пишете пользовательское ведение журнала.
format: '%(asctime).19s %(levelname)s - %(username)s: %(message)s'
logging.basicConfig(format=format)
logger.info(message, extra={'username' : request.user.username})
Некоторые примечания из документации (** kwars для Django logger):
Ключи в словаре, переданные дополнительно, не должны сталкиваться с ключами, используемыми системой ведения журнала.
Если строки, ожидаемые Formatter, отсутствуют, сообщение не будет регистрироваться.
Эта функция предназначена для использования в особых обстоятельствах, и не всегда.
Ответ 3
Я предоставлю один из многих возможных полных ответов на этот вопрос:
Как Django может использовать журнал для ведения журнала с использованием пользовательских атрибутов в форматировании? Я имею в виду, например, регистрировать зарегистрированное имя пользователя.
Другие ответы касались того, как добавить дополнительную контекстуальную информацию через утилиты регистрации python. Метод использования фильтров для присоединения дополнительной информации к записи журнала идеален и лучше всего описан в документах:
https://docs.python.org/3/howto/logging-cookbook.html#using-filters-to-impart-contextual-information
Это еще не говорит нам, как получить пользователя от запроса универсальным способом. Следующая библиотека делает это:
https://github.com/ninemoreminutes/django-crum
Таким образом, объедините два, и у вас будет полный ответ на заданный вопрос.
import logging
from crum import get_current_user
class ContextFilter(logging.Filter):
"""
This is a filter injects the Django user
"""
def filter(self, record):
record.user = get_current_user()
return True
if __name__ == '__main__':
logging.basicConfig(level=logging.DEBUG,
format='User: %(user)-8s %(message)s')
a1 = logging.getLogger('a.b.c')
f = ContextFilter()
a1.addFilter(f)
a1.debug('A debug message')
Это должно произойти в цикле запросов-ответа Django с установленной библиотекой CRUM.