Как использовать стандартный и пользовательский логгер в django, но избежать дублирования логов

У меня есть два набора API в проекте Django, для которых я хочу использовать свой пользовательский логгер. Поэтому в каждом наборе представлений я буду делать logger.info(...) или logger.error(...) перед ответами.

Везде в проекте, например, на страницах администратора и в других инструментах, я бы хотел пока оставить протоколирование как есть.

Вот как я определил свои LOGGING в settings.py.

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'standard': {
            'format':
                'ts=%(asctime)s level=%(levelname)s caller=%(filename)s:%(funcName)s:%(lineno)s %(message)s',
            'datefmt': '%Y-%m-%d %H:%M:%S',
        },
    },
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
            'formatter': 'standard',
        },

    },
    'loggers': {
        'my_logger': {
            'handlers': ['console'],
            'level': os.getenv('DJANGO_LOG_LEVEL', 'INFO'),
            'propagate': False,
        },
    },
}

В моем представлении я вызываю регистратор следующим образом:

logger = logging.getLogger('my_logger')
logger.info("Some message")

Проблема в том, что в таком виде логирование в API дублируется для каждого запроса, мой логгер и существующий логгер django. Как я могу "отключить" стандартный django logger в API, но сохранить его везде?

Пример дублирования журналов:

ts=2022-06-07 09:50:14 level=INFO caller=views.py:retrieve:450 path=/api/v1/<path>/<uuid>/ method=GET status=200 user=71 msg=Some message
[07/Jun/2022 09:50:14] "GET /api/v1/<path>/<uuid>/ HTTP/1.1" 200 41633

Вы назвали свой пользовательский регистратор django. Логгер django является корневым логгером, который перехватывает все журналы, если только дочерние логгеры не настроены с помощью ключевого слова "propagate": False. См. https://docs.djangoproject.com/en/4.0/ref/logging/#default-logging-conditions. Если вы дадите ему другое имя, то вы сможете вести журнал непосредственно в этот логгер, например

    'loggers': {
        'my_custom_logger': {
            'propagate': False,
            # other properties here
        }
    }

По вашему мнению, используйте

logger = logging.getLogger("my_custom_logger")
logger.ERROR("foo")

Эти сообщения журнала не должны распространяться на стандартный регистратор.


Согласно вашему обновленному вопросу, вы можете отключить записи логгера по умолчанию django.server для URL-путей API, добавив объект Filter. См. https://docs.python.org/3/library/logging.html#filter-objects и https://docs.djangoproject.com/en/4.0/ref/logging/#filters: если метод фильтра .filter() возвращает 0 или False для данной записи журнала, она не будет записана в журнал.

Достаточно изменить конфигурацию на что-то вроде

'loggers': {
    'django': {
        'level': 'CRITICAL'
    },
    'my_logger': {
        'handlers': ['console'],
        'level': os.getenv('DJANGO_LOG_LEVEL', 'INFO')
    },
},

потому что тогда все пакеты/модули Django не должны ничего выводить (кроме CRITICAL материала). Вам не нужен параметр propagate, поскольку вы не настроили никаких обработчиков для корневого логгера (родителя логгера my_logger) - propagate будет только контролировать, запрашиваются ли обработчики родительских логгеров на обработку события, а в данном случае таких обработчиков нет.

Вернуться на верх