Python LinuxWindowsmacOS

Python: обработчик logging пишет сообщения дважды после повторного импорта

  • Обновлено
  • Чтение ≈ 3 минуты

Краткий ответ

Логи Python печатаются дважды после повторного импорта: выявите повторные обработчики logging и распространение записей к корневому журналу.

В этой статье
  1. Что происходит и как распознать неисправность
  2. Как отличить причины
  3. Пошаговая диагностика и исправление
  4. Команды и примеры для проверки
  5. Как проверить результат и что делать дальше

#Что происходит и как распознать неисправность

Повторение одной записи лога обычно означает, что один экземпляр LogRecord проходит через несколько Handler, а не что Python выполнил бизнес-операцию дважды. Но сначала следует убедиться, что сама функция действительно вызвана один раз.

#Как отличить причины

Возможная причинаКак узнатьЧто делать
Handler добавляется при каждой инициализацииКод вызывает addHandler несколько раз для одного именованного логгера.len(logger.handlers) растёт после вызовов настройки.Настраивайте Handler в единой точке входа.
Дублирование через propagateУ дочернего логгера есть собственный Handler, а записи уходят также к родителю/root.Одно и то же событие видно в двух форматах.Выберите один уровень обработки, настройте propagate осознанно.
Повторный запуск модуля или обработчикаIDE, автоперезагрузка или регистрация подписчика запускают код дважды.Вместе с логами дважды меняются данные или растёт счётчик вызовов.Проверяйте сами вызовы и жизненный цикл приложения.

#Пошаговая диагностика и исправление

  1. Отличите двойную запись от двойного исполненияВоспроизведите проблему на неопасном действии и сравните временные метки, идентификаторы события и итоговые изменения данных. Если действие реально выполняется дважды, начните с проверки подписок/планировщика; настройка logging не исправит повторный вызов.
  2. Найдите точку конфигурации loggingОткройте проект в редакторе, выполните поиск по всем файлам (Ctrl+Shift+F в VS Code) выражений addHandler, basicConfig, dictConfig, fileConfig. Проверьте, не выполняются ли они внутри функций запроса, импорта или повторно запускаемого теста. Зафиксируйте список мест без немедленного удаления обработчиков.
  3. Проверьте обработчики нужного loggerВставьте на время в безопасную тестовую точку print(logger.name, len(logger.handlers), logger.propagate). После одного и двух вызовов настройки сравните число обработчиков. Если число растёт, источник повторной регистрации подтверждён. Верните код диагностики после исправления.
  4. Разместите настройку в точке входаВыберите одну функцию configure_logging, которую вызывает запускающий модуль до обработки задач, и перенесите туда создание Handler. Не создавайте новый FileHandler при каждом импорте модуля. Если программой управляет сервер, учитывайте, что он может уже настраивать logging, а изменение root будет влиять на чужие модули.
  5. Исправьте двойную передачу записи родителюЕсли логгер myapp содержит собственный Handler, а root также выводит запись, уберите лишний обработчик или обоснованно установите logger.propagate = False только для данного логгера. Не отключайте глобально logging и не применяйте propagate=False ко всем библиотекам: иначе можно потерять важные ошибки.
  6. Проверьте повторную инициализациюСмоделируйте два последовательных запуска configure_logging в тестовой среде и один вызов logger.info. Ожидается ровно одна запись на настроенный вывод; если FileHandler продолжает удерживать файл, проверьте закрытие обработчика при завершении приложения.
  7. Проверьте многопроцессную средуЕсли дубли остаются только с несколькими рабочими процессами веб-сервера, добавьте к формату %(process)d и убедитесь, что событие поступает единожды на процесс. Если это разные процессы, устраняйте повтор обработки, а не скрывайте сообщения. При необходимости верните сохранённую конфигурацию и передайте журналы без персональных данных.

#Команды и примеры для проверки

Python
Минимальный пример без дубля. В существующем проекте проверьте, кому принадлежит конфигурация и требуется ли обработка root.
import logging

logger = logging.getLogger("myapp")

def configure_logging():
    logger.setLevel(logging.INFO)
    if not logger.handlers:
        handler = logging.StreamHandler()
        handler.setFormatter(logging.Formatter("%(levelname)s %(name)s: %(message)s"))
        logger.addHandler(handler)
    logger.propagate = False  # здесь обработчик размещён у myapp

configure_logging()
configure_logging()
logger.info("одна запись")
Linux · Терминал (Bash)Команда
Версия Python нужна для отчёта о воспроизведении. Команда безопасна; вывод не содержит секретов.
python -V

#Как проверить результат и что делать дальше

После исправления одно событие должно порождать одну строку на каждом намеренно выбранном канале вывода; количество Handler не увеличивается после повторной настройки. Если повторяются действия, а не только текст логов, ищите вторичный запуск функции.

Источники и обновления

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