Categories: Python Создание сайта

Модуль logging в Python — удобное логирование программы

Модуль logging в Python представляет собой встроенный инструмент для ведения логов. Это гибкий механизм записи сообщений (логов) в различные места, такие как файлы, консоль, потоки и т. д. Он обеспечивает структурированный способ организации информации о работе программы, что полезно при отладке, мониторинге и анализе работы приложения.
Раньше я создава логи самостоятельно в простой текстовый файл. Но с библиотекой logging логирование будет намного эффективнее, так как можно создавать файлы с разным уровнем важности информации.

Основные компоненты logging в Python

  1. Logger (Логгер): Это основной компонент модуля, представляющий собой объект, который выполняет запись сообщений. Логгеры организованы иерархически по именам, что позволяет создавать логгеры в различных частях приложения. Вы создаете логгер с помощью logging.getLogger(name).
  2. Handler (Обработчик): Обработчики определяют, куда направлять вывод логов. Например, обработчик может записывать логи в файл, консоль, отправлять по электронной почте и т. д. Вы добавляете обработчики к логгеру с помощью метода logger.addHandler(handler).
  3. Formatter (Форматировщик): Форматировщики определяют, каким образом лог-сообщения будут представлены в записях. Вы создаете форматировщики и устанавливаете их для обработчиков с помощью handler.setFormatter(formatter).
  4. Level (Уровень): Каждый лог-сообщение имеет свой уровень, который указывает на важность сообщения. Уровни включают DEBUG, INFO, WARNING, ERROR, и CRITICAL. Устанавливая уровень логгирования, вы можете контролировать, какие сообщения будут записаны.

В Python существует несколько способов ведения логов. Один из наиболее популярных и удобных способов — использование модуля logging, встроенного в стандартную библиотеку Python. Вот примеры использования:

Рассмортим самый простой пример использования библиотеки logging

import logging

# Настройка конфигурации логгера
logging.basicConfig(filename='example.log', level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')

# Примеры использования логгера
def example_function():
    try:
        result = 10 / 0
    except Exception as e:
        # Запись ошибки в лог
        logging.error(f"An error occurred: {e}", exc_info=True)

def another_function():
    # Запись информационного сообщения в лог
    logging.info("This is an informational message.")

def main():
    # Запись отладочной информации в лог
    logging.debug("This is a debug message.")
    
    example_function()
    another_function()

if __name__ == "__main__":
    main()

Объяснения к коду:

  1. logging.basicConfig: Эта функция выполняет базовую настройку логгера. В данном примере указано имя файла лога (example.log), уровень логгирования (DEBUG — самый подробный уровень), и формат сообщений лога.
  2. logging.debug, logging.info, logging.warning, logging.error, logging.critical: Это методы для записи сообщений разных уровней в лог. Уровень логгирования можно настроить, чтобы записывать только сообщения с определенного уровня и выше. А можно записывать разные уровни в разные файлы. Об этом дальше.
  3. exc_info=True: Этот параметр добавляет информацию об исключении в лог, если произошла ошибка.

При использовании логгера, вы можете легко настроить уровень детализации лога и перенаправить вывод в файл, консоль или другие места. Кроме того, логгеры можно конфигурировать для отправки логов на удаленный сервер или в различные хранилища, что делает их мощным инструментом для отладки и мониторинга вашего приложения.

Logger — Настройка конфигурации логгера

logging.basicConfig(filename='example.log', level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s') 

Конфигурация (настройка) логгера выполняется с использованием функции basicConfig из модуля logging. Давайте подробно рассмотрим параметры этой функции:

  • filename='example.log': Этот параметр указывает имя файла, в который будут записываться логи. В данном случае, логи будут записываться в файл с именем ‘example.log’. Если вы не указываете filename, логи будут выводиться в стандартный поток вывода (консоль).
  • level=logging.DEBUG: Этот параметр устанавливает уровень логгирования. Уровень определяет, какие сообщения будут записаны. В данном случае, установлен уровень DEBUG, что означает, что будут записаны все сообщения, начиная от уровня DEBUG и выше. Вы можете выбрать из уровней логгирования, таких как DEBUG, INFO, WARNING, ERROR, и CRITICAL, в порядке увеличения уровня критичности.
  • format='%(asctime)s - %(levelname)s - %(message)s': Этот параметр определяет формат сообщений лога. В данном случае, используется формат, который включает время записи лога (%(asctime)s), уровень логгирования (%(levelname)s), и само сообщение (%(message)s). Время записи лога форматируется в соответствии с форматом времени, и это позволяет вам видеть, когда каждое сообщение было записано.

Пример формата времени в %(asctime)s:

  • %Y: Год с веком в виде полного числа.
  • %m: Месяц как число (01-12).
  • %d: День месяца как число (01-31).
  • %H: Час (00-23).
  • %M: Минуты (00-59).
  • %S: Секунды (00-59).

Таким образом, формат '%(asctime)s - %(levelname)s - %(message)s' создает записи лога следующего вида: «2023-11-14 15:30:00 — DEBUG — This is a debug message.»

Level -уровни логирования логов.

В приведенном примере выше, строка с вызовом logging.debug("This is a debug message.") представляют собой пример отладочной информации. Содержание информации задается программистом. Данная инфоррмация запишется в логи, при уровне логирования DEBUG. Если бы уровень был установлен на более высокий, например, INFO, сообщение с уровнем DEBUG не было бы записано. Уровень логгирования можно регулировать, чтобы включить или выключить запись сообщений определенного уровня. И это очень удобно на разных этапах тестирования программы.

По сути логи — это сообщения, заданные создателем программы. Но то какое сообщения будет записано в логи — будет зависить от уровня логирования.

Уровни логгирования по степени критичности:

  • DEBUG: Наиболее подробные сообщения, предназначенные для отладки. Используются для записи деталей выполнения кода.
  • INFO: Информационные сообщения о ходе выполнения программы. Подходит для отображения основной информации о процессе работы приложения.
  • WARNING: Предупреждения, которые не являются фатальными, но требуют внимания. Используются для выявления потенциальных проблем.
  • ERROR: Сообщения об ошибках, которые не позволяют продолжить выполнение программы.
  • CRITICAL: Критические ошибки, которые говорят о невозможности продолжения выполнения программы.

При установке уровня логирования записываться будут все сообщения с установленным уровня и выше. Например, если уровень установлен в DEBUG, то все сообщения с уровнем DEBUG, INFO, WARNING, ERROR, и CRITICAL будут записаны. Если уровень установлен в INFO, то будут записаны только сообщения с уровнем INFO, WARNING, ERROR, и CRITICAL, и так далее.

Файлы с разным уровнем логгирования logging в Python

import logging

# Настройка логгера с уровнем INFO и записью в файл info.log
logger_info = logging.getLogger('info_logger')
logger_info.setLevel(logging.INFO)
file_handler_info = logging.FileHandler('info.log')
file_handler_info.setLevel(logging.INFO)
formatter_info = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
file_handler_info.setFormatter(formatter_info)
logger_info.addHandler(file_handler_info)

# Настройка логгера с максимальным уровнем DEBUG и записью в файл debug.log
logger_debug = logging.getLogger('debug_logger')
logger_debug.setLevel(logging.DEBUG)
file_handler_debug = logging.FileHandler('debug.log')
file_handler_debug.setLevel(logging.DEBUG)
formatter_debug = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
file_handler_debug.setFormatter(formatter_debug)
logger_debug.addHandler(file_handler_debug)

def main():
    # Примеры использования логгеров
    logger_info.info("This is an informational message.")
    logger_debug.debug("This is a debug message.")

В этом примере создаются два логгера: logger_info с уровнем INFO и записью в файл info.log, а также logger_debug с максимальным уровнем DEBUG и записью в файл debug.log. Вы можете использовать эти логгеры по отдельности в различных частях вашего кода в зависимости от того, какую информацию вы хотите записывать.

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

Инициализация логгера — подключение логгера к программе

Инициализация логгера обычно выполняется в основном модуле программы или в специальном модуле, который выполняет роль настройщика (configurator). Это место часто называют «точкой входа» (entry point) в ваше приложение. Инициализация логгера лучше проводить один раз, при запуске программы.

Примерно так может выглядеть структура инициализации логгера в основном файле вашей программы:

# main.py

import logging
from my_module import example_function, another_function

def setup_logging():
    logging.basicConfig(filename='main.log', level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')

def main():
    # Инициализация логгера
    setup_logging()

    # Запись отладочной информации в лог
    logging.debug("This is a debug message.")
    
    example_function()
    another_function()

if __name__ == "__main__":
    main()

Теперь, чтобы использовать один и тот же логгер в других файлах, вам необходимо сделать следующее:

# my_module.py

import logging

# Получение логгера с тем же именем, что и в основном файле
logger = logging.getLogger(__name__)

def example_function():
    try:
        result = 10 / 0
    except Exception as e:
        # Запись ошибки в лог
        logger.error(f"An error occurred: {e}", exc_info=True)

В этом примере __name__ в getLogger(__name__) будет заменено на имя текущего модуля, что гарантирует уникальность логгера в рамках вашего приложения.

Конфигуратор — отдельный файл (модуль) для настроек логгера

Конфигуратор для логгера может представлять собой отдельный модуль, который содержит функцию или класс для настройки логгера. Вот пример конфигуратора:

import logging

def setup_logger(log_file, log_level=logging.DEBUG):
    # Создаем логгер
    logger = logging.getLogger()
    logger.setLevel(log_level)

    # Проверяем наличие обработчиков, чтобы обрабочик использовался один раз
    if logger.hasHandlers(): 
        logger.handlers = []

    # Создаем обработчик для записи в файл
    file_handler = logging.FileHandler(log_file)
    file_handler.setLevel(log_level)

    # Создаем форматтер
    formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
    file_handler.setFormatter(formatter)

    # Добавляем обработчик к логгеру
    logger.addHandler(file_handler)

    return logger

Теперь, в основном файле вашей программы (главном файле) вы можете использовать этот конфигуратор следующим образом:

# main.py

from logger_configurator import setup_logger
from my_module import example_function, another_function

def main():
    # Используем конфигуратор для настройки логгера
    logger = setup_logger('main.log', log_level=logging.DEBUG)

    # Запись отладочной информации в лог
    logger.debug("This is a debug message.")
    
    example_function()
    another_function()

if __name__ == "__main__":
    main()

Таким образом, конфигуратор setup_logger настраивает логгер с уровнем логгирования по умолчанию DEBUG и записью в файл ‘main.log’. Вы можете вызывать этот конфигуратор в каждом модуле, где вам необходим логгер, чтобы использовать один и тот же логгер по всему приложению.

# my_module.py

from logger_configurator import setup_logger

# Используем конфигуратор для настройки логгера в модуле
logger = setup_logger('my_module.log', log_level=logging.INFO)

def example_function():
    try:
        result = 10 / 0
    except Exception as e:
        # Запись ошибки в лог
        logger.error(f"An error occurred: {e}", exc_info=True)

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

Для записи логов в каждом файле, где они нужны, вы должны инициализировать логгер.И чтобы избежать избыточного кода и создания множества логгеров с одинаковыми настройками, часто используется шаблон (отдельный модуль) с использованием функции конфигурации.

В этом шаблоне есть главный файл, где вызывается функция конфигурации логгера. Затем в других файлах этот логгер используется без повторной инициализации. Это создает централизованный логгер с общими настройками по всему приложению.

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

И напоследок мой код конфигуратора, который фильтрует сообщения INFO и ERROR и записывает их в отдельные файлы, а также выводит логи в консоль.

import logging
import os


def setup_logger():

    log_folder = '02-log'
    # Создаем общий логгер
    logger = logging.getLogger()
    logger.setLevel(logging.DEBUG)
    
    # Чтобы логи не дублировались сбрасываем все обработчики
    if logger.hasHandlers(): 
        logger.handlers = []

    # Создаем обработчик для общих логов
    file_handler = logging.FileHandler(os.path.join(log_folder, 'debug.log'))
    formatter = logging.Formatter('[%(asctime)s] %(levelname)s | %(message)s')
    file_handler.setFormatter(formatter)
    logger.addHandler(file_handler)

    # Создаем отдельные обработчики для INFO и ERROR
    info_handler = logging.FileHandler(os.path.join(log_folder, 'info.log'))
    error_handler = logging.FileHandler(os.path.join(log_folder, 'error.log'))

    # Определяем фильтры
    info_filter = logging.Filter()
    error_filter = logging.Filter()

    # Устанавливаем фильтры для INFO и ERROR обработчиков
    info_filter.filter = lambda record: record.levelno == logging.INFO
    error_filter.filter = lambda record: record.levelno == logging.ERROR

    # Присваиваем фильтры обработчикам
    info_handler.addFilter(info_filter)
    error_handler.addFilter(error_filter)

    # Устанавливаем форматтер для обработчиков
    info_handler.setFormatter(formatter)
    error_handler.setFormatter(formatter)

    # Добавляем обработчики к логгеру
    # Добавляем обработчик только если его еще нет
    if not any(isinstance(h, logging.FileHandler) and h.baseFilename == 'error.log' for h in logger.handlers):
        logger.addHandler(error_handler)
    if not any(isinstance(h, logging.FileHandler) and h.baseFilename == 'info.log' for h in logger.handlers):
        logger.addHandler(info_handler)

    # Создаем обработчик для вывода сообщений в консоль
    console_handler = logging.StreamHandler()
    console_handler.setFormatter(formatter)
    logger.addHandler(console_handler)

    return logger

Recent Posts

  • Excel

Как правильно открыть CSV с многострочными ячейками в Excel

Если при открытие csv файла с помощью excel У все поехали ячейки - это простой… Read More

% дней назад
  • Python

Загрузка проекта на Github и Автодеплой на сервер

Простые шаги как происходит Загрузка проекта на Github и Автодеплой на сервер Сделайте работу проще! Read More

% дней назад
  • Python
  • Полезная минутка

Миграция базы данных в проектах Python

Миграция базы данных - помогают с этим библиотеки SQLAlchemy и Alembic. Read More

% дней назад
  • Python

Команды Git – Шпаргалка по основным командам Git

Основные Команды Git, которые точно пригодятся. Сохрани себе шпаргалку Git Read More

% дней назад
  • Python

Установка PostgreSQL на сервер Ubuntu и подключение через pgAdmin

Установка PostgreSQL на сервер Read More

% дней назад
  • Полезная минутка

Как создать PrimaryKey для таблицы в PostgreSQL

Как создать PrimaryKey в талице базы данных SQL [РЕШЕНО] Ошибка relation tablename_id_seq doesn't exist Read More

% дней назад