


Для логирования в Python создайте логгер модуля через getLogger(__name__), а вывод настройте один раз в точке входа приложения. Если сообщение исчезает, проверьте уровни логгера и обработчика. Если повторяется, проверьте обработчики и передачу записи родительскому логгеру через propagate.
На Mentoring.digital разберём небольшой пример: скрипт импорта сообщает о начале работы, отклонённых строках и ошибке чтения файла. Его журнал должен помогать диагностике, не раскрывая содержимое документов.
Сохраните пример в отдельный файл и запустите новым процессом Python. В интерактивной среде или фреймворке обработчики могут быть настроены раньше, поэтому повторный вызов basicConfig не обязательно изменит вывод.
import logging
logger = logging.getLogger(__name__)
def run_import():
logger.debug("Preparing import")
logger.info("Import started: rows=%s", 3)
logger.warning("Skipped rows: %s", 1)
if __name__ == "__main__":
logging.basicConfig(
level=logging.INFO,
format="%(levelname)s %(name)s %(message)s",
)
run_import()
Здесь видны INFO и WARNING, а DEBUG отсекается. Аргументы передаются отдельно от строки: форматирование сообщения выполняет logging. Значения 3 и 1 служат учебными данными, а не результатом реального импорта.
Уровень отражает последствие события. Не стоит помечать все записи как ERROR ради заметности: тогда поиск настоящей проблемы станет сложнее. Состав полей тоже важен: идентификатор операции обычно полезнее полного пользовательского запроса.
Проверяйте цепочку по порядку: разрешён ли уровень для логгера, есть ли обработчик, пропускают ли запись его уровень и фильтры. У дочернего логгера значение NOTSET означает наследование эффективного уровня от предков.
Частая причина дублей: обработчик добавлен и дочернему логгеру, и корневому, а propagate=True оставлен включённым. Одна запись доходит до обоих обработчиков. Обычно удобнее держать вывод на корневом логгере. Если дочернему действительно нужен независимый вывод, его передачу наверх отключают осознанно.
Не добавляйте обработчик внутри функции, вызываемой для каждой строки: при повторных вызовах их число может увеличиваться. Библиотека также не должна принудительно перенастраивать журнал всего приложения.
try:
with open("input.csv", encoding="utf-8") as source:
first_line = source.readline()
except OSError:
logger.exception("Cannot read import file")
logger.exception внутри обработчика исключения добавляет traceback. Решение продолжать работу или передать исключение выше принимает приложение; запись в журнал сама по себе не устраняет ошибку.
Проверьте обычный запуск, отсутствующий файл и два последовательных вызова функции. Для каждого события должна появляться ожидаемая запись без случайного размножения. Пароли, токены и полное содержимое документов в журнал не включайте.
Системно освоить обработку ошибок помогут курсы Python с практикой. Схему логирования конкретного сервиса можно разобрать с наставником по Python.