logo

Расширяем Python logging: добавляем имя файла и номер строки

Быстрый ответ: логирование с подробностями о файле и строке

Если вам нужно понять, откуда было отправлено сообщение в журнале, прибегните к использованию модуля logging в Python. Формат сообщений в журнале можно указать следующим образом:

Python
Скопировать код
import logging

logging.basicConfig(format='%(asctime)s – %(filename)s:%(lineno)d – %(levelname)s: %(message)s',
                    datefmt='%d/%m/%Y %H:%M:%S',
                    level=logging.DEBUG)

logging.debug('Это не та запись в журнале, которую вы ищете')

Такое форматирование позволит получить вывод с указанием имени файла и номера строки:

31/12/9999 23:59:59 – secret_script.py:8 – DEBUG – Это не та запись в журнале, которую вы ищете

Добавление контекста при помощи настройки формата

Для более точной ориентации в месте возникновения сообщения дополните формат логирования именем функции %(funcName)s:

Python
Скопировать код
logging.basicConfig(format='%(asctime)s – %(filename)s:%(lineno)d – %(funcName)s: %(message)s')

Интеллектуальное логирование исключений

Применяйте блоки try-except и метод logging.getLogger() для логирования исключений, что упростит процесс отладки:

Python
Скопировать код
logger = logging.getLogger(__name__)

try:
    risky_operation()
except Exception as e:
    logger.error('О, вот и ошибка!', exc_info=True)

Лог будет содержать сведения об исходном файле и строке, где произошло исключение.

Декораторы – наше спасение!

Декораторы позволяют логировать моменты входа в функцию, её окончания и исключения, одновременно фиксируя информацию о файле и строке.

Python
Скопировать код
def log_decorator(func):
    def wrapper(*args, **kwargs):
        logger = logging.getLogger(func.__module__)
        logger.debug(f'Входим в функцию: {func.__name__}')
        try:
            return func(*args, **kwargs)
        except Exception as e:
            logger.error(f'Произошла ошибка в {func.__name__}', exc_info=True)
            raise
        finally:
            logger.debug(f'Выходим из функции: {func.__name__}')
    return wrapper

Добавляем контекст в журнал

Качество журнала зависит от последовательности, ясности и контекста. Контекстные сведения придают записям в журнале большую ценность.

Различаем модули

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

Python
Скопировать код
logger = logging.getLogger('unique.module.name')

Достоинства PyCharm и Eclipse Pydev

PyCharm и Eclipse Pydev связывают записи в журнале с кодом, упрощая навигацию по выводу.

Тонкая настройка логирования

Для гибкой конфигурации логирования примените дополнительные библиотеки:

Python
Скопировать код
import logging
import sys
import os

Повышаем качество отладки

Включаем всех возможных условий для отладки

Для получения максимума информации установите уровень логирования DEBUG:

Python
Скопировать код
logging.basicConfig(level=logging.DEBUG)

Интеллектуальное отключение журналов отладки

Вы можете скрывать отладочные журналы в продакшене, используя следующий подход:

Python
Скопировать код
if not os.getenv('DEBUG'):
    logging.disable(logging.DEBUG)

Используем logging вместо print

logging лучше print, так как он дает больше возможностей для управления и настройки, плюс его можно отключить при необходимости.

Визуализация

Журналирование файлов и строк можно сравнить с расстановкой маяков на железнодорожных путях вашего кода – это поможет легче следить за логическими связями сообщений:

Markdown
Скопировать код
**Файл 1** (train_red.py)
🚇 Функция A – def start_journey():
    📍 log.info("Начинаем...") # [train_red.py:2 – Начинаем...]

**Файл 2** (train_blue.py)
🚇 Функция B – def continue_journey():
    📍 log.info("Продолжаем...") # [train_blue.py:2 – Продолжаем...]

**Файл 3** (train_green.py)
🚇 Функция C – def end_journey():
    📍 log.info("Завершаем...") # [train_green.py:2 – Завершаем...]

Эти "маяки" позволят точно определять местонахождение ваших сообщений в пространстве кода.

Настраиваем систему журналирования

Журналирование можно настраивать в зависимости от потребностей, оптимизируя процесс отладки:

Журналирование со StreamHandler

StreamHandler перенаправляет события логирования в стандартный вывод, что улучшает доступность логов:

Python
Скопировать код
stream_handler = logging.StreamHandler(sys.stdout)
logger.addHandler(stream_handler)

Настраиваем формат времени

Вы можете настроить формат даты и времени в журнале, используя параметр datefmt:

Python
Скопировать код
logging.basicConfig(datefmt='%H:%M:%S')

Добавляем контекст

Вы можете расширить контекст ваших сообщений, добавив дополнительные параметры форматирования, такие как %(module)s, %(pathname)s, %(threadName)s.

Упрощаем журналирование с помощью Loguru

Если стандартное журналирование кажется сложным, воспользуйтесь библиотекой Loguru. Она значительно упрощает весь процесс.

Полезные материалы

  1. Как использовать журналирование — Python 3.12.2 документация – Подробное руководство по модулю журналирования Python.
  2. Рецепты журналирования — Python 3.12.2 документация – Осмотру рассматриваются продвинутые вопросы журналирования в Python.
  3. Как добавить пользовательский уровень журналирования в систему журналирования Python – Stack Overflow – Обсуждение по вопросу настройки пользовательских уровней логирования.
  4. PEP 282 – Система журналирования | peps.python.org – Предложение о введении системы журналирования в Python.
  5. logging — Система журналирования для Python — Python 3.12.2 документация – Документация по объектам Logger.
  6. GitHub – Delgan/loguru: Журналирование на Python сделано (невероятно) простым – Ознакомление с Loguru.
  7. Отслеживание ошибок и мониторинг производительности Python | Sentry – Инструменты Sentinel для отслеживания ошибок и мониторинга производительности в Python.