# Настройки логирования в модулях ## ML-компонент Для единообразия логов самого модуля и логов библиотеки unip-mlcmp рекомендуется использовать один из двух форматов логирования: 1. Простой формат. 2. Формат с добавлением request_id. ### Простой формат Для соответствия простому формату, ваше сообщение должно форматироваться строкой: ``` "[%(asctime)s] %(name)-15.15s [%(levelname)-8.8s] %(message)s" ``` Примеры настроек приведены ниже. #### Настройка с помощью `dictConfig`: ```python from logging.config import dictConfig dictConfig({ 'version': 1, 'formatters': { 'default_formatter': { 'format': '[%(asctime)s] %(name)-15.15s [%(levelname)-8.8s] %(message)s' } }, 'handlers': { 'default_handler': { 'class': 'logging.StreamHandler', 'stream': 'ext://sys.stdout', 'formatter': 'default_formatter' } }, 'loggers': { 'my_project': { 'level': 'INFO', 'handlers': ['default_handler'] }, } 'disable_existing_loggers': False }) ``` #### Настройка с помощью `fileConfig`: Файл `logging.conf` ```toml [loggers] keys=root,my_project [handlers] keys=default_handler [formatters] keys=default_formatter [logger_root] level=INFO handlers= [logger_my_project] level=INFO handlers=default_handler propagate=1 qualname=my_project [handler_default_handler] class=StreamHandler level=NOTSET formatter=default_formatter args=(sys.stdout,) [formatter_default_formatter] format=[%(asctime)s] %(name)-15.15s [%(levelname)-8.8s] %(message)s datefmt= style=% validate=True class=logging.Formatter ``` Настройка в коде: ```python from logging.config import fileConfig fileConfig("logging.conf") ``` ### Формат с добавлением request_id `request_id` - это идентификатор корреляции, уникальный идентификатор, который присваивается каждому запросу. Он позволяет провести соответствие между записями в логах и запросами к сервису модели. Этот идентификатор представляет из себя строку. Если при запросе к сервису модели в заголовке `X-Request-Id` была передана строка, она используется в качестве идентификатора корреляции запроса. Если при запросе заголовок `X-Request-Id` не передан, идентификатор корреляции генерируется сервисом. В ответе на запрос к сервису модели в заголовке `X-Request-Id` идентификатор корреляции возвращается в любом случае - передан он был в запросе или нет. Для добавления `request_id` в логи достаточно изменить формат на следующий: ``` "[%(asctime)s] %(name)-15.15s [%(levelname)-8.8s] [%(request_id)s] %(message)s" ``` `request_id` добавляется в запись логов при обработке запроса сервисом модели. Чтобы логирование с `request_id` не приводило к ошибкам при выполнении кода модуля вне сервиса, рекомендуется использовать функцию-фильтр, добавляющую поле `request_id` в объект `LogRecord`, полями которого заполняется строка логов. #### Фильтр напрямую в коде Пример добавления фильтр-функции через `logger.addFilter()`. Предполагается, что обработчик (hanlder) добавлен только к логгеру `"my_project"` как корневому логгеру проекта. Метод `addFilter` нужно вызвать для всех логгеров, к которым добавлены обработчики, либо к самому обработчику. ```python logger = logging.getLogger("my_project") def filter_request_id(record): if not hasattr(record, "request_id"): record.request_id = "-" return True logger.addFilter(filter_request_id) ``` #### Фильтр в `dictConfig` При настройке фильтров в `dictConfig` нужно передавать не саму фильтр-функцию, а функцию, которая возвращает фильтр-функцию. Пример: ```python def filter_record_factory(): def filter_record(record): if not hasattr(record, "request_id"): record.request_id = "-" return True return filter_record dictConfig({ 'version': 1, 'formatters': { 'default_formatter': { 'format': '[%(asctime)s] %(name)-15.15s [%(levelname)-8.8s] %(message)s' } }, 'filters': { 'request_id': { '()': filter_record_factory }, } 'handlers': { 'default_handler': { 'class': 'logging.StreamHandler', 'stream': 'ext://sys.stdout', 'filters': ['request_id'] 'formatter': 'default_formatter', } }, 'loggers': { 'my_project': { 'level': 'INFO', 'handlers': ['default_handler'] }, } 'disable_existing_loggers': False }) ``` ## Пайплайн В пайплайнах рекомендуется использовать следующий формат логирования: `[%(asctime)s] %(name)s [%(levelname)s] %(message)s`. Пример конфигурации: ```python from logging.config import dictConfig dictConfig({ 'version': 1, 'formatters': { 'default_format': { 'format': '[%(asctime)s] %(name)s [%(levelname)s] %(message)s', } }, 'handlers': { 'default': { 'class': 'logging.StreamHandler', 'stream': 'ext://sys.stdout', 'formatter': 'default_format', } }, 'loggers': { 'my_project': { 'level': 'INFO', 'handlers': ['default_handler'] }, } }) ```