Знакомая ситуация: бот вроде бы работает. Люди тыкают кнопки, получают ответы, всё хорошо. Но иногда случается странное.
Прилетает сообщение в поддержку: «Бот не отвечает». Или ещё хуже: «Бот выдал какую‑то ошибку и всё». Вы бежите к терминалу, поднимаете историю… и ничего не видите. Ни ошибки, ни стека, ни даже намёка на то, где именно всё сломалось.
Вы просто слепы.
Без нормальных логов вы не понимаете ровно ничего: обработчик не сработал, API молчит, база данных упала или вы просто забыли зарегистрировать хендлер. Спойлер: я через это проходил, и не раз.
Почему print() не работает
В учебных проектах все пишут так:
print("user clicked button")
Выглядит безобидно. Но когда ваш бот оказывается в продакшене — это просто мусор. Давайте разберёмся, почему.
Когда вы ставите print(), вы лишаете себя контекста. Вы не видите, в какое время это случилось. Вы не знаете, какой пользователь нажал эту кнопку. Вы не можете отфильтровать тысячи сообщений и найти только ошибки. А если бот живёт в докере — этот print просто растворится в общем потоке логов без всякой надежды на поиск.
print() годится только для отладки «на коленке», когда вы сидите и смотрите за ботом вживую. Для продакшена это зло.
Базовая настройка логов (минимум)
В Python есть встроенный модуль logging. И вот вам минимальная конфигурация, которая уже даст вам 80 пользы без лишних танцев с бубном:
import logginglogging.basicConfig( level=logging.INFO, format="%(asctime)s | %(levelname)s | %(name)s | %(message)s")
Что тут происходит. level=logging.INFO значит, что вы видите информационные сообщения и ошибки, но не тонете в DEBUG-шуме. Формат строки добавляет время, уровень, имя модуля и само сообщение. Всё. Уже этого достаточно, чтобы читать логи как историю, а не как кашу.
Где логировать в боте (самое важное)
Логировать всё подряд не надо. Логировать надо то, что помогает понять жизненный цикл запроса.
Входящие события
Каждое сообщение от пользователя должно оставлять след. Я всегда пишу так:
logging.info(f"user={message.from_user.id} text={message.text}")
Теперь я вижу, что конкретный пользователь делал. Когда приходит жалоба «бот не работает», я открываю логи и говорю: «Смотри, последнее сообщение от этого пользователя — ‘купить билет’, после чего бот позвонил в API и ответа не получил». Без этой строчки я даже не узнаю, дошло ли сообщение до бота.
Действия бота
Бот что‑то делает — бот логирует это. Просто и без sneakers.
logging.info("sending profile menu")logging.info("calling payment gateway")logging.info("user subscribed - sending welcome message")
Вы сразу начинаете понимать, на каком этапе бот зависает. Не «где‑то там сломалось», а конкретно «упал при вызове платёжного шлюза».
Ошибки
Вот это конструкция, которую я использую всегда и везде. Просто запомните её:
try: result = some_function()except Exception as e: logging.exception("error in handler")
logging.exception() сам добавляет стек вызовов в сообщение. Вы увидите не «ошибка», а точную строчку, где всё полетело, с файлом и номером строки.
Реальные кейсы с моей практики
Самый ценный блок — реальные истории, когда логи меня реально спасали.
Кейс 1: бот молчит
Пользователь пишет «/start», бот ничего не отвечает. Открываю логи:
INFO | user=123 text=/start
И дальше — пустота. Ни «sending profile menu», ни ошибок, ничего.
Вывод: обработчик команды /start либо не зарегистрирован, либо фильтр сломался, либо событие ушло куда‑то не туда. Логи сузили поиск с «где‑то сломалось» до «проблема в диспетчеризации». Это уже победа.
Кейс 2: бот падает с грохотом
Пользователь что‑то делает, и бот вылетает с ошибкой. Смотрю в логи:
ERROR | KeyError: 'user_id' File "handlers/order.py", line 34, in handle_order user_id = data['user_id']
Всё. Вопрос закрыт. В данных не было ключа user_id, а я его тупо дёрнул без проверки. Лог указал на конкретную строчку — починил за минуту.
Кейс 3: бот двойнит сообщения
Бот отправляет одно и то же сообщение дважды. Смотрю логи:
INFO | handler called for user=123INFO | handler called for user=123
Вывод: обработчик вызывается дважды. Значит, роутер зарегистрирован два раза — например, подключил модуль два раза или middleware настроил криво.
Логи и асинхронность: ловушка для новичков
Это классика. Бот на asyncio, внутри хендлера кто‑то пишет:
time.sleep(1)
И всё. Весь event loop засыпает. Логи перестают писаться. Бот перестаёт отвечать другим пользователям.
Правильный вариант — await asyncio.sleep(1). Если вы видите в логах длинную паузу между записями и никаких объяснений — ищите блокирующий код. Он всегда найдётся, проверено.
Логи в продакшене — взрослая жизнь
В разработке вы смотрите логи в терминале и радуетесь. В продакшене этот номер не проходит.
Куда писать. Самый простой способ — в файл. Добавляем второй обработчик:
file_handler = logging.FileHandler("bot.log")file_handler.setFormatter(logging.Formatter("%(asctime)s | %(levelname)s | %(message)s"))logger.addHandler(file_handler)
Если у вас Docker — логи пишутся в stdout и stderr, а Docker сам разберётся, куда их направить.
Но есть одна проблема. Файл bot.log со временем разрастается до гигабайт. И вы об этом вспоминаете в самый неподходящий момент. Нужна ротация.
from logging.handlers import RotatingFileHandlerhandler = RotatingFileHandler( "bot.log", maxBytes=10_000_000, # 10 мегабайт backupCount=5)
Теперь у вас хранится пять файлов по 10 мегабайт. Старые логи не теряются, но диск не забивается.
Как читать логи по‑человечески
Просто «открыть файл и листать» — неправильно. Нужно уметь искать, и аккуратно.
Ищите по user_id. Пришла жалоба от пользователя 12345. Открываете лог и ищете user=12345. Смотрите всю последовательность: что он нажал, что бот ответил, была ли ошибка.
Ищите по ERROR. Это все места, где бот сломался. Если ошибок много — у вас системная проблема, и пора что‑то с этим делать.
Лог — это история. Вы должны восстановить хронологию: запрос пришёл → бот начал обработку → бот вызвал внешний API → API молчит → бот упал.
Что логировать нельзя (не повторяйте мои ошибки)
Очень важный блок. Не всё, что можно вывести в лог, нужно выводить. Я наступал на эти грабли, вы не наступайте.
Токены. Никогда — слышите, никогда? — не пишите в лог bot_token, api_key, access_token. Один случайно сохранённый лог‑файл, и ваши ключи уплывают в открытый доступ.
Пароли. Даже если пользователь сам ввёл пароль в сообщение — не логируйте тело сообщения полностью. Логируйте факт отправки, но не содержимое. Правда, пожалейте людей.
Персональные данные. Телефоны, адреса, номера карт. Логи могут попасть в Sentry, в чаты поддержки, в системы сбора логов. Это уже зона ответственности и комплаенса. Один слив — и вы пишете объяснительную:)
Продвинутый уровень (когда стало тесно)
Если базового подхода уже мало, можно пойти дальше. Но это уже для тех, кому мало.
JSON‑логи. Вместо текстовой строки вы пишете JSON с полями timestamp, level, user_id, message. Это позволяет подключать централизованные системы сбора логов — ELK, Loki, которые умеют индексировать и быстро искать по любому полю. Красота, но сложновато для старта.
Correlation ID. Каждому входящему запросу назначается уникальный идентификатор, который бежит через все вызовы. В логах появляется поле request_id=abc-123. Вы можете собрать все записи по одному запросу, даже если они разбросаны по разным модулям. Выглядит как магия, но работает.
Интеграция с Sentry. Sentry автоматически собирает исключения, контекст, окружение. Вы ставите sentry_sdk.init(), и все падения бота оказываются в веб‑интерфейсе с полным стеком и контекстом. Бесплатно для небольших проектов.
Ключевая мысль, которую я хочу донести
Логи — это не «для галочки». Это не формальность и не свалка отладочного текста. Логи — это единственный инструмент, который поможет вам понять, что происходит с ботом, когда вы спите и видите десятый сон.
Когда ваш продакшен‑бот падает в два часа ночи, логи становятся единственным свидетелем происходящего. Правильные логи дают контекст. Они показывают не просто «упало», а «упало вот тут, с такими‑то данными, перед таким‑то действием».
Без логов вы слепы. С логами вы берёте файл bot.log, находите строчку с ERROR и через пару минут уже понимаете, где править код.
Настройте логи прямо сейчас. Это займёт пятнадцать минут, но сэкономит вам несколько ночей дебага.
ссылка на оригинал статьи https://habr.com/ru/articles/1029992/