Записывание пользовательских атрибутов 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.