Модуль logging в Python представляет собой встроенный инструмент для ведения логов. Это гибкий механизм записи сообщений (логов) в различные места, такие как файлы, консоль, потоки и т. д. Он обеспечивает структурированный способ организации информации о работе программы, что полезно при отладке, мониторинге и анализе работы приложения.
Раньше я создава логи самостоятельно в простой текстовый файл. Но с библиотекой logging логирование будет намного эффективнее, так как можно создавать файлы с разным уровнем важности информации.
Основные компоненты logging в Python
- Logger (Логгер): Это основной компонент модуля, представляющий собой объект, который выполняет запись сообщений. Логгеры организованы иерархически по именам, что позволяет создавать логгеры в различных частях приложения. Вы создаете логгер с помощью
logging.getLogger(name). - Handler (Обработчик): Обработчики определяют, куда направлять вывод логов. Например, обработчик может записывать логи в файл, консоль, отправлять по электронной почте и т. д. Вы добавляете обработчики к логгеру с помощью метода
logger.addHandler(handler). - Formatter (Форматировщик): Форматировщики определяют, каким образом лог-сообщения будут представлены в записях. Вы создаете форматировщики и устанавливаете их для обработчиков с помощью
handler.setFormatter(formatter). - Level (Уровень): Каждый лог-сообщение имеет свой уровень, который указывает на важность сообщения. Уровни включают
DEBUG,INFO,WARNING,ERROR, иCRITICAL. Устанавливая уровень логгирования, вы можете контролировать, какие сообщения будут записаны.
В Python существует несколько способов ведения логов. Один из наиболее популярных и удобных способов — использование модуля logging, встроенного в стандартную библиотеку Python. Вот примеры использования:
Рассмортим самый простой пример использования библиотеки logging
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | 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() |
Объяснения к коду:
logging.basicConfig: Эта функция выполняет базовую настройку логгера. В данном примере указано имя файла лога (example.log), уровень логгирования (DEBUG— самый подробный уровень), и формат сообщений лога.logging.debug,logging.info,logging.warning,logging.error,logging.critical: Это методы для записи сообщений разных уровней в лог. Уровень логгирования можно настроить, чтобы записывать только сообщения с определенного уровня и выше. А можно записывать разные уровни в разные файлы. Об этом дальше.exc_info=True: Этот параметр добавляет информацию об исключении в лог, если произошла ошибка.
При использовании логгера, вы можете легко настроить уровень детализации лога и перенаправить вывод в файл, консоль или другие места. Кроме того, логгеры можно конфигурировать для отправки логов на удаленный сервер или в различные хранилища, что делает их мощным инструментом для отладки и мониторинга вашего приложения.
Logger — Настройка конфигурации логгера
1 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | 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) в ваше приложение. Инициализация логгера лучше проводить один раз, при запуске программы.
Примерно так может выглядеть структура инициализации логгера в основном файле вашей программы:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | # 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() |
Теперь, чтобы использовать один и тот же логгер в других файлах, вам необходимо сделать следующее:
1 2 3 4 5 6 7 8 9 10 11 12 13 | # 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__) будет заменено на имя текущего модуля, что гарантирует уникальность логгера в рамках вашего приложения.
Конфигуратор — отдельный файл (модуль) для настроек логгера
Конфигуратор для логгера может представлять собой отдельный модуль, который содержит функцию или класс для настройки логгера. Вот пример конфигуратора:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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 |
Теперь, в основном файле вашей программы (главном файле) вы можете использовать этот конфигуратор следующим образом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | # 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’. Вы можете вызывать этот конфигуратор в каждом модуле, где вам необходим логгер, чтобы использовать один и тот же логгер по всему приложению.
1 2 3 4 5 6 7 8 9 10 11 12 13 | # 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 и записывает их в отдельные файлы, а также выводит логи в консоль.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | 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 |