Найти в Дзене
Записки сисадмина

Pyhon. logging. Настраиваем логирование в скриптах.

"Боже, да что опять обвалилось? А самое главное - когда?" - если вы задавались таким вопросом, вам точно не хватало в тот момент логов. Не будем сейчас философствовать о том, как важно все журналировать, а перейдем сразу к делу. В Python за логи отвечает библиотека logging. В статье по настройке IIS я уже упоминал добавление логирования в класс коннектора. logging.basicConfig(filename=project_dir + '1c_error.log', level=logging.ERROR, format='%(asctime)s %(levelname)s %(name)s %(message)s') logger = logging.getLogger(__name__) Если мы хотим использовать логгер только в одном скрипте и писать только в один файл - этого будет достаточно. logging.basicConfig - задает все необходимые параметры логгера. filename - путь до файла, в который мы будем писать лог. level - уровень логирования (INFO, ERROR, WARNING, DEBUG) format - формат журнала. logging.getLogger - создание нового объекта логгера с уникальным именем. При первом запуске создастся файл "path_to_logfile.log", в
Оглавление

"Боже, да что опять обвалилось? А самое главное - когда?" - если вы задавались таким вопросом, вам точно не хватало в тот момент логов.

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

В Python за логи отвечает библиотека logging.

В статье по настройке IIS я уже упоминал добавление логирования в класс коннектора.

logging.basicConfig(filename=project_dir + '1c_error.log', level=logging.ERROR,
format='%(asctime)s %(levelname)s %(name)s %(message)s')
logger = logging.getLogger(__name__)

Если мы хотим использовать логгер только в одном скрипте и писать только в один файл - этого будет достаточно.

logging.basicConfig - задает все необходимые параметры логгера.

filename - путь до файла, в который мы будем писать лог.

level - уровень логирования (INFO, ERROR, WARNING, DEBUG)

format - формат журнала.

logging.getLogger - создание нового объекта логгера с уникальным именем.

-2

При первом запуске создастся файл "path_to_logfile.log", в него мы и будем писать все ошибки.

Записываем в лог сообщения

  • Добавим в конец нашего скрипта запись о том, что нужно залогировать сообщение с уровнем INFO.
  • Залезем чуть глубже в библиотеку и посмотрим, что мы можем передавать:
-3

INFO принимает в себя сообщение (которое мы укажем строкой) и любое количество именованных и неименованных аргументов.

  • Итак, запишем в лог сообщение "test":
-4

При запуске, в файле лога появится следующая строчка:

2025-04-17 14:10:11,836 INFO set_name test

Как мы и указывали в формате:

{точное время} {уровень ошибки} {имя логгера} {сообщение об ошибке, которое мы передали}

Спойлер: info, warning, debug, error и critical ведут себя абсолютно идентично. Разница в них только во внутренней настройке важности ошибки.

-5

В данной ситуации у нас всегда будет одно и то же сообщение в логе. Давайте усложним логику.

Передаем переменные в лог

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

  • Создадим простенький цикл, в котором будет меняться значение переменной, и запишем это в лог:
-6
  • Либо так:
-7
logger.info('variable value is %(var)s', {'var': variable})
  • После запуска, в логе мы увидим следующие сообщения:
-8
  • Давайте разберем оба формата записи:
  1. В первом случае мы передали неименованную переменную в лог.
  2. Место, куда поместится эта переменная обозначается с помощью %s.
  3. В случае передачи нескольких переменных, необходимо соблюдать их порядок:
-9
logger.info('list of variables: %s %s %s %s', variable1, variable2, variable3, variable4)

Конвертируется в:

'2025-04-17 14:54:55,263 INFO set_name list of variables: 1 2 3 4'

  1. Во втором случае мы передаем именованные переменные.
  2. Место, куда поместится переменная обозначается с помощью %(ключ)s.
  3. Порядок соблюдать не обязательно.
  4. Переменные передаются в виде словаря {указанный ключ: значение переменной}
-10

Обрабатываем исключения (exceptions)

Пожалуй, самый легко настраиваемый лог в нашей статье.

-11

Мы также можем передать в него необходимые нам переменные, но в поле msg (или просто первым аргументом) мы передаем сам Exception, а точнее - его Traceback.

  • Напишем простенькую конструкцию try/except:
-12
  • В случае ошибки, мы помещаем Traceback в переменную new_msg и передаем ее логгеру:
-13

Все практически идеально. Но.

Мы ведь не хотим сваливать абсолютно весь журнал в один файл. Огромный поток текста из разных модулей может стать проблемой в случае каких-либо ошибок. Что будем делать?

Создаем несколько логгеров для проекта

Давайте предположим, что нам нужно писать логи сразу в несколько файлов. Как наглядный пример - access.log и error.log у любого веб сервера типа nginx, или apache.

В случае, если мы в начале нашего скрипта укажем basicConfig, он применится ко всем логгерам сразу, что нам не подходит.

  • Создадим функцию, которая будет создавать логгер по полученным параметрам и возвращать нам объект логгера:
-14
def setup_logger(name, log_file, level=logging.INFO):
formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
handler = logging.FileHandler(log_file)
handler.setFormatter(formatter)
logger = logging.getLogger(name)
logger.setLevel(level)
logger.addHandler(handler)
return logger

В функцию мы принимаем имя логгера и имя лог файла. Уровень логирования по умолчанию - INFO.

  • Создадим два логгера для разных типов журналов:
-15
access_logger = setup_logger(name='access', log_file='access.log')
error_logger = setup_logger(name='error', log_file='error.log')

Все сообщения, записанные access_logger, будут отправляться в файл access.log, а сообщения от error_logger - в error.log.

  • Напишем простой цикл, в котором все четные числа будем писать в access.log, а нечетные - в error.log:
-16
  • После запуска у нас создастся два новых лог файла с содержанием:
-17
-18

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