Ведение журнала Python - отключить ведение журнала из импортированных модулей

100

Я использую модуль ведения журнала Python и хочу отключить сообщения журнала, выводимые сторонними модулями, которые я импортирую. Например, я использую что-то вроде следующего:

logger = logging.getLogger()
logger.setLevel(level=logging.DEBUG)
fh = logging.StreamHandler()
fh_formatter = logging.Formatter('%(asctime)s %(levelname)s %(lineno)d:%(filename)s(%(process)d) - %(message)s')
fh.setFormatter(fh_formatter)
logger.addHandler(fh)

Он распечатывает мои отладочные сообщения, когда я выполняю logger.debug («мое сообщение!»), Но он также распечатывает отладочные сообщения из любого модуля, который я импортирую (например, запросы и ряд других вещей).

Я хотел бы видеть только сообщения журнала от интересующих меня модулей. Можно ли сделать так, чтобы модуль ведения журнала делал это?

В идеале я хотел бы иметь возможность указывать регистратору, что он должен печатать сообщения из «ModuleX, ModuleY» и игнорировать все остальные.

Я посмотрел на следующее, но я не хочу отключать / включать ведение журнала перед каждым вызовом импортированной функции: ведение журнала - как игнорировать журналы импортированного модуля?

слепой снегоход
источник

Ответы:

69

Проблема в том, что при вызове getLoggerбез аргументов возвращается корневой регистратор, поэтому, когда вы устанавливаете уровень, logging.DEBUGвы также устанавливаете уровень для других модулей, которые используют этот регистратор.

Вы можете решить эту проблему, просто не используя корневой регистратор. Для этого просто передайте имя в качестве аргумента, например имя вашего модуля:

logger = logging.getLogger('my_module_name')
# as before

это создаст новый регистратор и, таким образом, не будет непреднамеренно изменять уровень ведения журнала для других модулей.


Очевидно, вы должны использовать logger.debugвместо, logging.debugпоскольку последняя является удобной функцией, которая вызывает debugметод корневого регистратора.

Об этом упоминается в Advanced Logging Tutorial . Это также позволяет вам просто узнать, какой модуль инициировал сообщение журнала.

Бакуриу
источник
37
Я создаю регистратор с __name__r, но я все еще вижу журналы импортированных модулей. Я пытаюсь настроить ведение журнала с помощью файла конфигурации ini, что мне для этого делать?
Дурга Сваруп
8
Создание логгера с __name__тоже у меня не получилось. Может потому, что я использую автономный скрипт, а не "модуль"? Что сработало для меня, так это настроить ведение журнала для импортированных модулей ( matpplotlibв моем случае) через logging.getLogger("matplotlib").setLevel(logging.WARNING)и для моего скрипта через logging.basicConfig.
bli
1
Я просто хотел подчеркнуть значение вашей строки «Очевидно, вы должны использовать logger.debugвместо logging.debug». Легко сделать ошибку, используя ведение журнала вместо регистратора, но это узурпирует всю умную конфигурацию, которую вы хотите настроить. Я провел последние пару часов, живя в этом!
timdadev
@bli Существует большая разница между ведением журнала при разработке библиотеки и при разработке исполняемого файла. В основном: если вы пишете модуль / пакет, предназначенный для импорта, вам не нужно ничего настраивать. Модуль должен содержать только logger = logging.getLogger('package.my_module')вызовы и ваши could logger.debug/warning, без настройки уровней ведения журнала или обработчиков. При записи двоичного приложения там вы должны определить уровень для различных журналов и обработчиков. Библиотеки, содержащие конфигурацию журналирования, всегда будут проблемой.
Бакуриу
В моем случае пакет, который я импортирую, использует корневой регистратор (через logging.info). Есть ли способ специально отключить корневые журналы из этого пакета?
IanS
45

Если вы собираетесь использовать loggingпакет python , общепринято определять регистратор в каждом модуле, который его использует.

logger = logging.getLogger(__name__)

Многие популярные пакеты python делают это, в том числе requests. Если пакет использует это соглашение, легко включить / отключить ведение журнала для него, потому что имя регистратора будет таким же, как имя пакета (или будет дочерним элементом этого регистратора). Вы даже можете записать его в тот же файл, что и другие ваши регистраторы.

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

requests_logger = logging.getLogger('requests')
requests_logger.setLevel(logging.DEBUG)

handler = logging.StreamHandler()
handler.setLevel(logging.DEBUG)
logger.addHandler(handler)
requests_logger.addHandler(handler)
Брендан Абель
источник
15
Имейте в виду, что когда вы пытаетесь настроить свои регистраторы, как в официальном базовом руководстве, logging.basicConfig(...)все регистраторы теперь будут выводиться либо на logging.lastResort(начиная с Python 3.2, который является stderr), если обработчик не был указан, либо на обработчик, который вы установили. Так что не используйте его, иначе вы все равно продолжите получать все сообщения журнала.
user136036
44

Не уверен, уместно ли это для публикации, но я застрял на долгое время и хотел помочь кому-нибудь с той же проблемой, так как нигде больше не нашел!

Я получал журналы отладки из matplotlib, несмотря на то, что следовал довольно простой документации в расширенном руководстве по ведению журнала и устранению неполадок . Я запускал свой логгер в main()одном файле и импортировал функцию для создания графика из другого файла (куда я импортировал matplotlib).

Что сработало для меня, так это установка уровня matplotlib перед его импортом, а не после, как это было для других модулей в моем основном файле. Мне это показалось нелогичным, поэтому, если кто-то знает, как можно настроить конфигурацию для регистратора, который еще не был импортирован, мне было бы любопытно узнать, как это работает. Благодарность!

В моем основном файле:

import logging
import requests
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
logging.getLogger('requests').setLevel(logging.DEBUG)

def main():
  ...

В моем plot.pyфайле:

import logging
logging.getLogger('matplotlib').setLevel(logging.WARNING)
import matplotlib.pyplot as plt

def generatePlot():
  ...
Финн
источник
Я получил ошибку: объект «Регистратор» не имеет атрибута «DEBUG». logger.DEBUGдолжно бытьlogging.DEBUG
foxiris 06
Благодарность! Это действительно помогает! Я устанавливаю уровень ведения журнала matplotlib после моей основной конфигурации ведения журнала и перед командой, которая будет импортировать matplotlib. Решено!
GPH
Я установил протоколирование , matplotlibчтобы WARNING после того, как я импортировал модуль, так как добавление его перед импортом даст пух-ошибку. У меня все еще работало. Я использую matplotlib==3.3.2Python 3.7, если это помогает.
Конец 2-Конец
9

@Bakuriu довольно элегантно объясняет функцию. И наоборот, вы можете использовать этот getLogger()метод для получения и перенастройки / отключения нежелательных регистраторов.

Я также хотел добавить, что logging.fileConfig()метод принимает параметр, вызываемый, disable_existing_loggersкоторый отключит любые ранее определенные средства ведения журнала (т.е. в импортированных модулях).

апекс-мем-лорд
источник
9

Это отключает все существующие регистраторы, например, созданные импортированными модулями, при этом по-прежнему используется корневой регистратор (и без необходимости загружать внешний файл).

logging.config.dictConfig({
    'version': 1,
    'disable_existing_loggers': True,
})

Обратите внимание, что вам нужно сначала импортировать все модули, которые вы не хотите регистрировать! В противном случае они не будут считаться «существующими регистраторами». Затем он отключит все регистраторы из этих модулей. Это может привести к тому, что вы также упустите важные ошибки!

Более подробные примеры использования связанных параметров для конфигурации см. На https://gist.github.com/st4lk/6287746 , а вот (частично рабочий) пример использования YAML для настройки с coloredlogбиблиотекой.

avv
источник
Каков твой вопрос?
user1767754 07
1
Это работает, requestнапример, но не будет работать, когда импортированные модули создают свои регистраторы внутри своего класса, который вы вызовете позже, как это APSchedulerпроисходит при вызове BackgroundScheduler.BackgroundScheduler(). См. Здесь решение: stackoverflow.com/a/48891485/2441026
user136036
Это работает для моего случая с файлом конфигурации
yaml
4

Вы можете использовать что-то вроде:

logging.getLogger("imported_module").setLevel(logging.WARNING)
logging.getLogger("my_own_logger_name").setLevel(logging.DEBUG)

Это установит уровень журнала моего собственного модуля на DEBUG, не позволяя импортированному модулю использовать тот же уровень.

Примечание: "imported_module"можно заменить на imported_module.__name__(без кавычек), а "my_own_logger_name"можно заменить на, __name__если вы предпочитаете это делать.

Камику
источник
1

У меня такая же проблема. У меня есть файл logging_config.py, который я импортирую во все другие файлы py. В файле logging_config.py я установил уровень ведения журнала корневого журнала на ERROR (по умолчанию это предупреждение):

logging.basicConfig(
    handlers=[
        RotatingFileHandler('logs.log',maxBytes=1000, backupCount=2),
        logging.StreamHandler(), #print to console
    ],
    level=logging.ERROR
)

В других модулях я импортирую logging_config.py, объявляю новый регистратор и устанавливаю его уровень на отладку:

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

Таким образом, все, что я регистрирую в своих файлах py, регистрируется, но все, что регистрируется на уровне отладки и информации импортированными модулями, такими как urllib, request, boto3 и т. Д., Не регистрируется. Если в этом модуле импорта есть какая-то ошибка, то она регистрируется, поскольку я установил уровень корневых регистраторов на ERROR.

Aseem
источник
0

Еще одна вещь, которую следует учитывать, - это свойство распространения класса Logger.

Например, библиотека py-suds для обработки вызовов мыла, даже помещенная в ERROR

logging.getLogger('suds.client').setLevel(logging.ERROR)
logging.getLogger('suds.transport').setLevel(logging.ERROR)
logging.getLogger('suds.xsdschema').setLevel(logging.ERROR)
logging.getLogger('suds.wsdl').setLevel(logging.ERROR)

журналы журналов о модуле sxbasics.py создание огромного количества журналов

введите описание изображения здесь

что, поскольку распространение журналов по умолчанию True, вместо этого я восстановил 514 МБ журналов.

import logging
logging.getLogger("suds").propagate = False
logging.getLogger('suds.client').setLevel(logging.ERROR)
logging.getLogger('suds.transport').setLevel(logging.ERROR)
logging.getLogger('suds.xsdschema').setLevel(logging.ERROR)
logging.getLogger('suds.wsdl').setLevel(logging.ERROR)
Андреа Бизелло
источник