Python предлагает весьма сильную библиотеку логирования в стандартной библиотеке. Многие программисты используют оператор print для лечения багов (в том числе и я), но вы можете использовать логирование для этих целей. Использование лога также более чистый метод, если вы не хотите просматривать весь свой код, чтобы удалить все операторы print. В данном разделе мы рассмотрим следующее:
- Создание простого логгера;
- Использование нескольких модулей для логирования;
- Форматирование лога;
- Настройки лога
К концу данного раздела вы сможете уверенно создавать логи для своих приложений. Приступим!
Создаем простой логгер
Создание лога при помощи модуля logging это очень просто. Для начала, будет проще взглянуть на часть кода и объяснить его:
1 2 3 4 5 6 7 8 |
import logging # add filemode="w" to overwrite logging.basicConfig(filename="sample.log", level=logging.INFO) logging.debug("This is a debug message") logging.info("Informational message") logging.error("An error has happened!") |
Как и ожидалось, чтобы получит доступ к модулю logging, для начала нужно импортировать модуль. Простейший способ создания лога – это использовать функцию basicConfig модуля logging и передать ей несколько ключевых аргументов. Функция принимает следующее: filename, filemode, format, datefmt, level и stream. В нашем примере, мы передадим её названию файла и уровню логирования, что мы и настроим в INFO.
Существует пять уровней логирования (в порядке возрастания): DEBUG, INFO, WARNING, ERROR и CRITICAL. По умолчанию, если вы запустите этот код несколько раз, он добавится в лог, если он существует. Если вы хотите, чтобы ваш логгер перезаписывал лог, передайте его filemode=”w”, как было указано в комментарии к коду. Говоря о запуске кода, вы должны получить следующий результат, после запуска:
1 2 |
INFO:root:Informational message ERROR:root:An error has happened! |
Обратите внимание на то, что сообщение о дебаггинге находится не на выходе. Это связанно с тем, что мы установили уровень в INFO, так что наш логгер будет записывать только сообщения INFO, WARNING, ERROR или CRITICAL. Часть root означает, что данное сообщение пришло либо от корневого логгера, либо от основного. Мы взглянем на то, как это можно изменить, что бы следующая часть была более понятной. Если вы не используете basicConfig, тогда модуль протоколирования выведет на консоль / stdout. Модуль logging также может учитывать некоторые исключения в файле, или там, где вы указали. Взглянем на пример:
1 2 3 4 5 6 7 8 9 |
import logging logging.basicConfig(filename="sample.log", level=logging.INFO) log = logging.getLogger("ex") try: raise RuntimeError except RuntimeError: log.exception("Error!") |
Давайте немного притормозим. Здесь мы использовали метод getLogger модуля logging, чтобы вернуть объект логгера под названием ex. Это удобно, если у вас есть несколько логгеров в одном приложении, так как это позволяет вам узнать, какие сообщения приходят с каждого логгера. Этот пример провоцирует возникновение ошибки RuntimeError, затем это регистрируется в файле, что может быть очень удобно при лечении багов.
Есть вопросы по Python?
На нашем форуме вы можете задать любой вопрос и получить ответ от всего нашего сообщества!
Паблик VK
Одно из самых больших сообществ по Python в социальной сети ВК. Видео уроки и книги для вас!
Логирование из нескольких модулей (а также форматирование)
Чем больше вы кодите, тем чаще вам нужно создавать набор настраиваемых модулей, которые должны работать вместе. Если вы хотите, чтобы запись велась в одном месте, значит, вы обратились по адресу. Мы рассмотрим простой пример, затем взглянем на более сложный метод, который также и более настраиваемый. Вот простой способ сделать это:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
import logging import otherMod def main(): """ The main entry point of the application """ logging.basicConfig(filename="mySnake.log", level=logging.INFO) logging.info("Program started") result = otherMod.add(7, 8) logging.info("Done!") if __name__ == "__main__": main() |
Здесь мы импортируем logging и модуль нашего творения (“otherMod”). После чего мы создаем лог-файл, как мы делали это раньше. Другой модуль выглядит следующим образом:
1 2 3 4 5 6 7 |
# otherMod.py import logging def add(x, y): """""" logging.info("added %s and %s to get %s" % (x, y, x+y)) return x+y |
Если вы запустите основной код, вы получите лог со следующим содержимым:
1 2 3 |
INFO:root:Program started INFO:root:added 7 and 8 to get 15 INFO:root:Done! |
Заметили проблему в этом? Вы не можете однозначно сказать, откуда приходят сообщения. При этом, чем больше модулей пишут в лог, тем сложнее становится картина. Так что нам нужно это исправить. Таким образом, мы приходим к более сложному способу создание логгера. Давайте взглянем на другой способ создания:
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 import otherMod2 def main(): """ The main entry point of the application """ logger = logging.getLogger("exampleApp") logger.setLevel(logging.INFO) # create the logging file handler fh = logging.FileHandler("new_snake.log") formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') fh.setFormatter(formatter) # add handler to logger object logger.addHandler(fh) logger.info("Program started") result = otherMod2.add(7, 8) logger.info("Done!") if __name__ == "__main__": main() |
Здесь мы создали экземпляр логгера под названием “exampleApp”. Мы установили его уровень ведения журнала, создали объект обработчика лог-файла, и объект форматирование. Обработчик файла должен установить объект форматирование в качестве своего форматтера, после чего обработчик файла должен быть добавлен в регистратор логгера. Остальная часть кода по большому счету остается неизменной. Только обратите внимание на то, что вместо logging.info мы используем logger.info, или любое другое название переменной вашего логгера. Давайте рассмотрим обновленный код otherMod2:
1 2 3 4 5 6 7 8 9 10 |
# otherMod2.py import logging module_logger = logging.getLogger("exampleApp.otherMod2") def add(x, y): """""" logger = logging.getLogger("exampleApp.otherMod2.add") logger.info("added %s and %s to get %s" % (x, y, x+y)) return x+y |
Обратите внимание на то, что здесь у нас два определенных логгера. Мы ничего не делаем с module_logger в данном случае, но мы используем другой модуль. Если вы запустите основной скрипт, вы увидите следующий результат в своем файле:
1 2 3 |
2012-08-02 15:37:40,592 - exampleApp - INFO - Program started 2012-08-02 15:37:40,592 - exampleApp.otherMod2.add - INFO - added 7 and 8 to get 15 2012-08-02 15:37:40,592 - exampleApp - INFO - Done! |
Вы заметите ,что все ссылки на root были удалены. Вместо этого, мы используем наш объект Formatter, который говорит нам, что нам нужно получить читаемое время, имя логгера, уровень логирования и сообщение. Все это в комплексе также известно как атрибуты LogRecord. Для полного списка атрибутов LogRecord обратитесь к документации Python, так как объем информации достаточно велик.
Конфигурация логов для работы
Модуль logging может быть сконфигурирован тремя различными способами. Вы можете настроить данный модуль при помощи методов (логгеров, форматтеров, обработчиков, как мы делали это ранее в данном разделе. Вы можете использовать файл конфигурации и передать его fileConfig(). Или же вы можете создать словарь информацией о конфигурации и передать его функции fileConfig(). Давайте создадим файл конфигурации для начала, а затем взглянем на то, как выполнить его в Python. Вот пример файла config:
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 |
[loggers] keys=root,exampleApp [handlers] keys=fileHandler, consoleHandler [formatters] keys=myFormatter [logger_root] level=CRITICAL handlers=consoleHandler [logger_exampleApp] level=INFO handlers=fileHandler qualname=exampleApp [handler_consoleHandler] class=StreamHandler level=DEBUG formatter=myFormatter args=(sys.stdout,) [handler_fileHandler] class=FileHandler formatter=myFormatter args=("config.log",) [formatter_myFormatter] format=%(asctime)s - %(name)s - %(levelname)s - %(message)s datefmt= |
Обратите внимание на то, что у нас есть два определенных логгера: root и exampleApp. По какой-то причине, необходим именно root. Так что если вы его не включите в код, возникнет ошибка ValueError функции config.py _install_loggers, которая является частью модуля logging. Если вы установите обработчик root на fileHandler, то это приведет к дублированию выхода журнала, так что во избежание этого, мы отправляем его на консоль. Внимательно изучите данный пример. Вам нужна секция для каждого ключа в первых трех секциях. Давайте взглянем на то, как загрузить это в код:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
# log_with_config.py import logging import logging.config import otherMod2 def main(): """ Based on http://docs.python.org/howto/logging.html#configuring-logging """ logging.config.fileConfig('logging.conf') logger = logging.getLogger("exampleApp") logger.info("Program started") result = otherMod2.add(7, 8) logger.info("Done!") if __name__ == "__main__": main() |
Как вы видите, все, что вам нужно это передать путь файла config нашему logging.config.fileConfig. Обратите внимание на то, что нам больше не нужен установочный код, для этого у нас есть файл config. Также мы можем просто импортировать модуль otherMod2 без каких либо изменений. В любом случае, если вы запустите указанный выше код, вы получите следующий результат в своем лог файле:
1 2 3 |
2012-08-02 18:23:33,338 - exampleApp - INFO - Program started 2012-08-02 18:23:33,338 - exampleApp.otherMod2.add - INFO - added 7 and 8 to get15 2012-08-02 18:23:33,338 - exampleApp - INFO - Done! |
Как вы могли догадаться, это очень похоже на другой пример. Теперь мы готовы перейти к следующему методу config. Метода словаря конфигураций dictConfig не существовало в Python до версии 2.7. Так что убедитесь в том, что вы работаете в последней версии, иначе вы не сможете приступить к следующей части. В документации не сказано прямо, как это работает. В действительности, примеры в документации, по какой-то причине показывают YAML. В любом случае, давайте рассмотрим один рабочий код:
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 |
# log_with_config.py import logging import logging.config import otherMod2 def main(): """ Based on http://docs.python.org/howto/logging.html#configuring-logging """ dictLogConfig = { "version":1, "handlers":{ "fileHandler":{ "class":"logging.FileHandler", "formatter":"myFormatter", "filename":"config2.log" } }, "loggers":{ "exampleApp":{ "handlers":["fileHandler"], "level":"INFO", } }, "formatters":{ "myFormatter":{ "format":"%(asctime)s - %(name)s - %(levelname)s - %(message)s" } } } logging.config.dictConfig(dictLogConfig) logger = logging.getLogger("exampleApp") logger.info("Program started") result = otherMod2.add(7, 8) logger.info("Done!") if __name__ == "__main__": main() |
Если вы запустите этот код, вы получите такой же результат, как в предыдущем методе. Обратите внимание на то, что вам больше не нужен логгер root, когда вы используете словарь конфигурации.
Подведем итоги
С этого момента вы должны уметь пользовать логгерами и выполнять их конфигурацию несколькими различными методами. Вы также получили все необходимые знания для модификации выдачу при помощи объекта Formatter. Модуль logging очень полезен в решении возникших проблем в работе вашего приложения. Постарайтесь провести больше времени за практической работой с данным модулем перед написанием большого приложения.
Являюсь администратором нескольких порталов по обучению языков программирования Python, Golang и Kotlin. В составе небольшой команды единомышленников, мы занимаемся популяризацией языков программирования на русскоязычную аудиторию. Большая часть статей была адаптирована нами на русский язык и распространяется бесплатно.
E-mail: vasile.buldumac@ati.utm.md
Образование
Universitatea Tehnică a Moldovei (utm.md)
- 2014 — 2018 Технический Университет Молдовы, ИТ-Инженер. Тема дипломной работы «Автоматизация покупки и продажи криптовалюты используя технический анализ»
- 2018 — 2020 Технический Университет Молдовы, Магистр, Магистерская диссертация «Идентификация человека в киберпространстве по фотографии лица»