Работа с логами: модуль logging и настройка вывода

Зачем нужны логи? 🧐

Логи — это хлеб насущный для любого разработчика. Они помогают:

  • Отслеживать работу программы в реальном времени 🕵️‍♂️
  • Находить ошибки даже в продакшене 🔍
  • Анализировать производительность системы ⚡

Без логов мы как слепые котята в темной комнате — тыкаемся наугад!

Базовый пример: logging за 30 секунд ⏱️

Вот минимальный рабочий пример, который можно добавить в любой проект:

import logging

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

logger.debug("Это сообщение не появится")
logger.info("Привет, это информационное сообщение!")
logger.warning("А это уже тревожный звоночек")

Вывод:

INFO:__main__: Привет, это информационное сообщение!
WARNING:__main__: А это уже тревожный звоночек

Уровни логирования: от шепота до крика 📢

Модуль logging предлагает 5 уровней важности:

  1. DEBUG — детальная отладочная информация (10)
  2. INFO — подтверждение, что всё работает (20)
  3. WARNING — что-то пошло не так, но программа работает (30)
  4. ERROR — серьёзная проблема, часть функционала сломана (40)
  5. CRITICAL — программа не может продолжать работу (50)
logger.log(logging.DEBUG, "Подробности для разработчиков")
logger.log(45, "Можно даже создать свой уровень!")  # Между ERROR и CRITICAL

Настройка формата вывода 🎨

Стандартный вывод скучен? Давайте кастомизировать!

logging.basicConfig(
    format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
    datefmt="%d-%m-%Y %H:%M:%S",
    level=logging.DEBUG
)

logger.info("Теперь сообщения выглядят круче!")

Вывод:

12-01-2024 14:30:45 - __main__ - INFO - Теперь сообщения выглядят круче!

Доступные параметры формата:

  • %(asctime)s — время события
  • %(levelname)s — уровень важности
  • %(message)s — текст сообщения
  • %(name)s — имя логгера
  • %(filename)s — имя файла, откуда вызван лог

Куда писать логи? Файлы, консоль и не только 📂

Запись в файл

logging.basicConfig(
    filename="app.log",
    filemode="a",  # append (по умолчанию)
    level=logging.INFO
)

Одновременно в файл и консоль

logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)

# Создаем обработчики
console_handler = logging.StreamHandler()
file_handler = logging.FileHandler("debug.log")

# Добавляем обработчики к логгеру
logger.addHandler(console_handler)
logger.addHandler(file_handler)

logger.info("Сообщение пойдет в оба места!")

Фильтрация и ротация логов 🌀

Фильтруем сообщения

class NoSpamFilter(logging.Filter):
    def filter(self, record):
        return "спам" not in record.getMessage()

logger.addFilter(NoSpamFilter())
logger.info("Это нормальное сообщение")  # Будет выведено
logger.info("Это сообщение-спам")  # Будет проигнорировано

Ротация логов (чтобы не засорять диск)

from logging.handlers import RotatingFileHandler

handler = RotatingFileHandler(
    "app.log",
    maxBytes=1024*1024,  # 1 MB
    backupCount=5  # Храним 5 архивных копий
)
logger.addHandler(handler)

Продвинутые техники 🚀

Логирование исключений

try:
    1 / 0
except Exception:
    logger.exception("Произошла ошибка:")  # Автоматически добавит traceback

Вывод:

ERROR:__main__: Произошла ошибка:
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
ZeroDivisionError: division by zero

Логирование в многопоточных приложениях

from concurrent.futures import ThreadPoolExecutor
import threading

def worker():
    logger.info(f"Поток {threading.get_ident()} начал работу")

with ThreadPoolExecutor(max_workers=3) as executor:
    executor.submit(worker)
    executor.submit(worker)

Лучшие практики от Данилы Бежина 🏆

  1. Не изобретайте велосипед — используйте logging вместо print
  2. Разные логгеры для разных модулейlogger = logging.getLogger(__name__)
  3. Настройка через код, а не конфиги — так понятнее и гибче
  4. DEBUG только при разработке — в продакшене INFO или выше
  5. Логируйте контекст — "Не удалось сохранить пользователя ID=42" лучше чем "Ошибка сохранения"

Больше полезных фишек — в плейлисте по логированию на канале Данилы.


Реальный пример: логирование веб-запросов 🌐

from flask import Flask
import logging
from logging.handlers import RotatingFileHandler

app = Flask(__name__)

# Настройка логирования
handler = RotatingFileHandler('web.log', maxBytes=10000, backupCount=3)
handler.setFormatter(logging.Formatter(
    '%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]'
))
app.logger.addHandler(handler)
app.logger.setLevel(logging.INFO)

@app.route('/')
def home():
    app.logger.info('Получен запрос к главной странице')
    return "Добро пожаловать!"

if __name__ == '__main__':
    app.run()

Теперь все запросы будут аккуратно сохраняться в web.log с деталями!

Скрыть рекламу навсегда

🌱 Индвидидулаьные занятия

Индивидуальные онлайн-занятия по программированию для детей и подростков

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

🚀 Записаться на занятие