Всем увееееееей ребята, сегодня мы поговорим о конфигах и логах python. Ну что-ж меньше слов больше дела, приступим.
Конфиги. Все хранят их по разному. Кто-то в .yaml, кто-то в .ini, а кто-то вообще в исходном коде. Я же буду делать в .ini .
Ладно, раз уж мы решили, что хранить данные в коде — не круто, то давайте искать альтернативу. Для конфигурационных файлов изобретено немалое количество различных форматов, в последнее время набирают большую популярность toml.
Но мы начнём с того, что нам предлагает сам Python — .ini. В стандартной библиотеке имеется библиотека configparser.
Файл config.ini:
[program]
name=pythonlogexample
Напоминаю что значения надо писать без кавычек иначе пример ниже выведет "pythonlogexample" а не просто pythonlogexample (без кавычек)
Файл python
import configparser # импортируем библиотеку
config = configparser.ConfigParser() # создаём объекта парсера
config.read("config.ini") # читаем конфиг
print(config["program"]["name"]) # обращаемся как к обычному словарю!
Вывод: pythonlogexample
Логирование в Python
Если вы — новичок, то вы, наверняка, привыкли пользоваться командой print(), выводя с её помощью определённые значения в ходе работы программы, проверяя, работает ли код так, как от него ожидается. Использование print() вполне может оправдать себя при отладке маленьких Python-программ. Но, когда вы перейдёте к более крупным и сложным проектам, вам понадобится постоянный журнал, содержащий больше информации о поведении вашего кода, помогающий вам планомерно отлаживать и отслеживать ошибки.
Подключим лог:
import logging
Дадим краткие характеристики уровней логирования:
- Debug (10): самый низкий уровень логирования, предназначенный для отладочных сообщений, для вывода диагностической информации о приложении.
- Info (20): этот уровень предназначен для вывода данных о фрагментах кода, работающих так, как ожидается.
- Warning (30): этот уровень логирования предусматривает вывод предупреждений, он применяется для записи сведений о событиях, на которые программист обычно обращает внимание. Такие события вполне могут привести к проблемам при работе приложения. Если явно не задать уровень логирования — по умолчанию используется именно warning.
- Error (40): этот уровень логирования предусматривает вывод сведений об ошибках — о том, что часть приложения работает не так как ожидается, о том, что программа не смогла правильно выполниться.
- Critical (50): этот уровень используется для вывода сведений об очень серьёзных ошибках, наличие которых угрожает нормальному функционированию всего приложения. Если не исправить такую ошибку — это может привести к тому, что приложение прекратит работу.
Теперь попробуем вывести что-нибудь в лог:
logging.debug("A DEBUG Message")
logging.info("An INFO")
logging.warning("A WARNING")
logging.error("An ERROR")
logging.critical("A message of CRITICAL severity")
Вывод будет таков:
WARNING:root:A WARNING
ERROR:root:An ERROR
CRITICAL:root:A message of CRITICAL severity
Как видите, сообщения, выведенные с уровнями логирования warning, error и critical, попадают в консоль. А сообщения с уровнями debug и info — не попадают.
Это так из-за того, что в консоль выводятся лишь сообщения с уровнями от warning и выше. Но это можно изменить, настроив логгер и указав ему, что в консоль надо выводить сообщения, начиная с некоего, заданного вами, уровня логирования.
Подобный подход к логированию, когда данные выводятся в консоль, не особо лучше использования print(). На практике может понадобиться записывать логируемые сообщения в файл. Этот файл будет хранить данные и после того, как работа программы завершится. Такой файл можно использовать в качестве журнала отладки.
Логирование в файл
Для того чтобы настроить простую систему логирования в файл — можно воспользоваться конструктором basicConfig(). Вот как это выглядит:
logging.basicConfig(level=logging.INFO, filename="py_log.log",filemode="w")
logging.debug("A DEBUG Message")
logging.info("An INFO")
logging.warning("A WARNING")
logging.error("An ERROR")
logging.critical("A message of CRITICAL severity")
Поговорим о логгере root, рассмотрим параметры basicConfig():
- level: это — уровень, на котором нужно начинать логирование. Если он установлен в info — это значит, что все сообщения с уровнем debug игнорируются.
- filename: этот параметр указывает на объект обработчика файла. Тут можно указать имя файла, в который нужно осуществлять логирование.
- filemode: это — необязательный параметр, указывающий режим, в котором предполагается работать с файлом журнала, заданным параметром filename. Установка filemode в значение w (write, запись) приводит к тому, что логи перезаписываются при каждом запуске модуля. По умолчанию параметр filemode установлен в значение a (append, присоединение), то есть — в файл будут попадать записи из всех сеансов работы программы.
Я рекомендую filemode ставить значение "a" тк если забыть прочитать лог и сразу запустить программу то лог будет очищен.
После выполнения модуля можно будет увидеть, что в текущей рабочей директории был создан файл журнала, py_log.log.
Так как мы установили уровень логирования в значение info — в файл попадут записи с уровнем info и с более высокими уровнями
Записи в лог-файле имеют формат <logging-level>:<name-of-the-logger>:<message>. По умолчанию <name-of-the-logger>, имя логгера, установлено в root, так как мы пока не настраивали пользовательские логгеры.
Помимо базовой информации, выводимой в лог, может понадобится снабдить записи отметками времени, указывающими на момент вывода той или иной записи. Это упрощает анализ логов. Сделать это можно, воспользовавшись параметром конструктора format:
logging.basicConfig(level=logging.INFO,
filename="py_log.log",
filemode="w",
format="%(asctime)s %(levelname)s %(message)s")
logging.debug("A DEBUG Message")
logging.info("An INFO")
logging.warning("A WARNING")
logging.error("An ERROR")
logging.critical("A message of CRITICAL severity")
Более удобные логи
Для начала создадим директорию logs/ в папке проекта а далее изменим код на этот:
import logging
from datetime import date
date = date.today()
logging.basicConfig(level=logging.INFO,
filename=f"logs/{date}.log",
filemode="a",
format="%(asctime)s %(levelname)s %(message)s")
logging.debug("A DEBUG Message")
logging.info("An INFO")
logging.warning("A WARNING")
logging.error("An ERROR")
logging.critical("A message of CRITICAL severity")
Теперь каждый новый день будет создаваться новый файл лога чтобы легче было найти проблему, но и здесь есть проблема это то что имя файла это дата запуска, поэтому если программа будет работать более чем 24 часа, то дня будет 2 а лог 1.
Кодировка лога
Попробуем занести в лог русский текст:
import logging
from datetime import date
date = date.today()
logging.basicConfig(level=logging.INFO,
filename=f"logs/{date}.log",
filemode="a",
format="%(asctime)s %(levelname)s %(message)s")
logging.info("Русский текст")
В логе будет:
2023-12-19 18:07:38,147 INFO ������� �����
чтобы это исправить добавим кодировку "UTF-8" так
logging.basicConfig(level=logging.INFO,
filename=f"logs/{date}.log",
filemode="a",
format="%(asctime)s %(levelname)s %(message)s",
encoding = "UTF-8")
те добавим последнюю строчку. Теперь в логе:
2023-12-19 18:09:37,919 INFO Русский текст
Конфиг+лог
теперь объединим всё чтобы сделать строчку в конфиге debug=true
файл питона:
import logging
import configparser
from datetime import date
date = date.today()
config = configparser.ConfigParser()
config.read("config.ini")
if(config['log']['debug']=="true"):
logging.basicConfig(level=logging.DEBUG,
filename=f"logs/{date}.log",
filemode="a",
format="%(asctime)s %(levelname)s %(message)s",
encoding = "UTF-8")
else:
logging.basicConfig(level=logging.INFO,
filename=f"logs/{date}.log",
filemode="a",
format="%(asctime)s %(levelname)s %(message)s",
encoding = "UTF-8")
logging.info("Русский текст")
logging.debug("Русский текст")
Файл конфига:
[log]
debug=false
Теперь если debug=true то и сообщения отладки будут в логе иначе нет.
Подписывайтесь, ведь скоро часть 4 где мы объединим знания и создадим сайт на flask