#Что происходит и как распознать неисправность
Повторение одной записи лога обычно означает, что один экземпляр LogRecord проходит через несколько Handler, а не что Python выполнил бизнес-операцию дважды. Но сначала следует убедиться, что сама функция действительно вызвана один раз.
#Как отличить причины
| Возможная причина | Как узнать | Что делать |
|---|---|---|
| Handler добавляется при каждой инициализацииКод вызывает addHandler несколько раз для одного именованного логгера. | len(logger.handlers) растёт после вызовов настройки. | Настраивайте Handler в единой точке входа. |
| Дублирование через propagateУ дочернего логгера есть собственный Handler, а записи уходят также к родителю/root. | Одно и то же событие видно в двух форматах. | Выберите один уровень обработки, настройте propagate осознанно. |
| Повторный запуск модуля или обработчикаIDE, автоперезагрузка или регистрация подписчика запускают код дважды. | Вместе с логами дважды меняются данные или растёт счётчик вызовов. | Проверяйте сами вызовы и жизненный цикл приложения. |
#Пошаговая диагностика и исправление
- Отличите двойную запись от двойного исполненияВоспроизведите проблему на неопасном действии и сравните временные метки, идентификаторы события и итоговые изменения данных. Если действие реально выполняется дважды, начните с проверки подписок/планировщика; настройка logging не исправит повторный вызов.
- Найдите точку конфигурации loggingОткройте проект в редакторе, выполните поиск по всем файлам (
Ctrl+Shift+Fв VS Code) выраженийaddHandler,basicConfig,dictConfig,fileConfig. Проверьте, не выполняются ли они внутри функций запроса, импорта или повторно запускаемого теста. Зафиксируйте список мест без немедленного удаления обработчиков. - Проверьте обработчики нужного loggerВставьте на время в безопасную тестовую точку
print(logger.name, len(logger.handlers), logger.propagate). После одного и двух вызовов настройки сравните число обработчиков. Если число растёт, источник повторной регистрации подтверждён. Верните код диагностики после исправления. - Разместите настройку в точке входаВыберите одну функцию configure_logging, которую вызывает запускающий модуль до обработки задач, и перенесите туда создание Handler. Не создавайте новый FileHandler при каждом импорте модуля. Если программой управляет сервер, учитывайте, что он может уже настраивать logging, а изменение root будет влиять на чужие модули.
- Исправьте двойную передачу записи родителюЕсли логгер
myappсодержит собственный Handler, а root также выводит запись, уберите лишний обработчик или обоснованно установитеlogger.propagate = Falseтолько для данного логгера. Не отключайте глобально logging и не применяйте propagate=False ко всем библиотекам: иначе можно потерять важные ошибки. - Проверьте повторную инициализациюСмоделируйте два последовательных запуска configure_logging в тестовой среде и один вызов
logger.info. Ожидается ровно одна запись на настроенный вывод; если FileHandler продолжает удерживать файл, проверьте закрытие обработчика при завершении приложения. - Проверьте многопроцессную средуЕсли дубли остаются только с несколькими рабочими процессами веб-сервера, добавьте к формату
%(process)dи убедитесь, что событие поступает единожды на процесс. Если это разные процессы, устраняйте повтор обработки, а не скрывайте сообщения. При необходимости верните сохранённую конфигурацию и передайте журналы без персональных данных.
#Команды и примеры для проверки
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("одна запись")python -V#Как проверить результат и что делать дальше
После исправления одно событие должно порождать одну строку на каждом намеренно выбранном канале вывода; количество Handler не увеличивается после повторной настройки. Если повторяются действия, а не только текст логов, ищите вторичный запуск функции.