Python logging log rotate

Ротация логов в Python

Author's picture

Недавно возникла задача сделать ротацию логов в веб-сервисе на Tornado. До начала работы казалось, что вопрос должен быть давно изучен и решение будет очевидным.

Но, как это часто случается, если требования несколько отличаются от стандартных, то приходится искать решение самостоятельно. Или читать документацию.

TL;DR

Если необходимо реализовать ротацию логов таким образом, чтобы файлы создавались ежедневно и имя файла соотвествовало формату %Y%m%d.%i.log , где %Y — год, %m — месяц, %d — день, %i — индекс текущего файла лога, то необходимо использовать TimedRotatingFileHandler и настраивать формат имени файла переопределяя свойства namer и suffix этого класса:

def get_filename(filename): # Получаем директорию, где расположены логи log_directory = os.path.split(filename)[0] # Расширением (с точкой) файла является значение suffix (у нас - %Y%m%d) (например .20181231). # Но точка нам не нужна, т.к. файл будет называться suffix.log (20181231.log) date = os.path.splitext(filename)[1][1:] # Сформировали имя нового лог-файла filename = os.path.join(log_directory, date) if not os.path.exists('<>.log'.format(filename)): return '<>.log'.format(filename) # Найдём минимальный индекс файла на текущий момент. index = 0 f = '<>.<>.log'.format(filename, index) while os.path.exists(f): index += 1 f = '<>.<>.log'.format(filename, index) return f rotation_logging_handler = TimedRotatingFileHandler('./logs/log.log', when='m', interval=1, backupCount=5) rotation_logging_handler.suffix = '%Y%m%d' rotation_logging_handler.namer = get_filename logger.addHandler(rotation_logging_handler) 

Чуть более подробно

Постановка задачи

Задача была следующая: необходимо каждый день создавать новый файл журнала. При этом файл должен иметь название годмесяцдень.log и, если приложение перезапускается, то текущий файл не должен удаляться, а должен создаваться новый файл. Например, с именем годмесяцдень.0.log , где 0 обозначал номер лог-файла за указанную дату: если сервис запущен 26 сентября 2018 года и два раза за этот день рестартовал, то в папке с логами должно быть три файла: 20180926.log , 20180926.0.log , 20180926.1.log .

Читайте также:  Шапка страницы таблицей html

Задача ротации логов в Python решается довольно просто: есть модуль logging и различные Handler ы, которые позволют реализовать ротацию. Например, RotatingFileHandler и TimedRotatingFileHandler . Первый создаёт новый файл при достижении некоторого заданного размера, а второй — по прошествии определённого времени. Первый под заданные требования не подходит и остаётся только TimedRotatingFileHandler .

TimedRotatingFileHandler

Первый подход

Задача казалась максимально простой и всё должно работать “из коробки”:

import logging as logging from logging.handlers import TimedRotatingFileHandler from time import sleep # Комбинация параметров when и interval задаёт период для создания нового файла логов. # В примере новый файл логов будет создаваться каждую (interval=1) минуту (when='m'). rotation_logging_handler = TimedRotatingFileHandler('./logs/log.log', when='m', interval=1, backupCount=5) logger = logging.getLogger() logger.addHandler(rotation_logging_handler) for i in range(121): sleep(1) print('current iteration: <>'.format(i)) logger.error('current iteration: <>'.format(i)) 

Запускаем, наблюдаем. Видим, что в папке logs создалось три файла: log.log и два файла log.log.дата-время . В простейшем случае этого достаточно, но перфекционизм не даёт мне права оставить это в таком виде. Хочу, чтобы имена файлов были в формате %Y%m%d.%i.log .

Вторая попытка

Окей, попробовали просто — получили не тот результат, которых хотелось. Будем усложнять пример. Судя по документации, у logger есть свойство suffix . Модифицируем приложение, добавив suffix с требуемым форматом %Y%m%d :

import logging as logging from logging.handlers import TimedRotatingFileHandler from time import sleep # Комбинация параметров when и interval задаёт период для создания нового файла логов. # В примере новый файл логов будет создаваться каждую (interval=1) минуту (when='m'). rotation_logging_handler = TimedRotatingFileHandler('./logs/log.log', when='m', interval=1, backupCount=5) rotation_logging_handler.suffix = '%Y%m%d' logger = logging.getLogger() logger.addHandler(rotation_logging_handler) for i in range(121): sleep(1) print('current iteration: <>'.format(i)) logger.error('current iteration: <>'.format(i)) 

Следим за ~руками~ логами: в папке logs появился новый файл с именем log.log.дата . Должно было быть два: по истечению первой и второй минут. Похоже, это не работает.

Здесь я ~психанул и~ решил забраться в исходники logger -а и посмотреть как формируется имя файла для лога. Нашёл интересное свойство namer . namer — это функция, которая может определить как именно должен называться файл лога. На самом деле в документации можно было найти эту же информацию.

Третья попытка

Доработаем пример с учётом новых знаний:

import logging as logging from logging.handlers import TimedRotatingFileHandler from time import sleep def get_filename(filename): # Получаем директорию, где расположены логи log_directory = os.path.split(filename)[0] # suffix - это расширение (с точкой) файла. # У нас - %Y%m%d. Например .20181231. # Точка нам не нужна, т.к. файл будет называться suffix.log (20181231.log) date = os.path.splitext(filename)[1][1:] # Сформировали имя нового лог-файла filename = os.path.join(log_directory, date) if not os.path.exists('<>.log'.format(filename)): return '<>.log'.format(filename) # Найдём минимальный индекс файла на текущий момент. index = 0 f = '<>.<>.log'.format(filename, index) while os.path.exists(f): index += 1 f = '<>.<>.log'.format(filename, index) return f rotation_logging_handler = TimedRotatingFileHandler(path, when=when_mode[when_mode_config], interval=1, backupCount=5) rotation_logging_handler.suffix = '%Y%m%d' rotation_logging_handler.namer = get_filename logger = logging.getLogger() logger.addHandler(rotation_logging_handler) for i in range(121): sleep(1) print('current iteration: <>'.format(i)) logger.error('current iteration: <>'.format(i)) 

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

Если вам понравилась статья, то можете зайти в мой telegram-канал. В канал попадают небольшие заметки о Python, .NET, Go.

Источник

Оцените статью