Python Logging - вимкнути журналювання з імпортованих модулів


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" і ігнорувати всі інші.

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

Відповіді:


70

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

Ви можете вирішити це, просто не використовуючи кореневий реєстратор. Для цього просто передайте ім'я як аргумент, наприклад ім'я вашого модуля:

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

це створить новий реєстратор і, отже, мимоволі не змінить рівень реєстрації для інших модулів.


Очевидно, вам доведеться використовувати logger.debugзамість, logging.debugоскільки остання є зручною функцією, яка викликає debugметод кореневого реєстратора.

Про це згадується в Підручнику з розширеного журналювання . Це також дозволяє знати, який модуль спростував повідомлення журналу.


37
Я створюю реєстратор з __name__r, але я все ще бачу журнали з імпортованих модулів. Я намагаюся налаштувати ведення журналу за допомогою файлу конфігурації ini. Що мені для цього робити?
Durga Swaroop,

8
Створення реєстратора за допомогою __name__також не спрацювало для мене. Може тому, що я використовую автономний сценарій, а не "модуль"? Мені вдалося налаштувати ведення журналу для імпортованих модулів ( matpplotlibу моєму випадку) через logging.getLogger("matplotlib").setLevel(logging.WARNING)і для мого сценарію через logging.basicConfig.
бли

1
Я просто хотів виділити значення вашого рядка "Очевидно, що ви повинні використовувати logger.debugзамість logging.debug". Це легка помилка, використовуючи реєстрацію, а не реєстратор, але вона узурпує всю розумну конфігурацію, яку ви хочете налаштувати. Останні пару годин я прожив цим!
timdadev

@bli Існує велика різниця між веденням журналу при розробці бібліотеки та розробкою виконуваного файлу. В основному: якщо ви пишете модуль / пакет, призначений для імпорту, не слід нічого налаштовувати. Модуль повинен містити лише logger = logging.getLogger('package.my_module')виклики та ваші можливості logger.debug/warning, без налаштування рівнів реєстрації та обробників. Коли ви пишете там двійковий додаток , вам слід визначити рівень для різних журналів та обробників. Бібліотеки, які містять конфігурацію ведення журналу, завжди будуть проблемою.
Бакуріу

1
@IanS Ні, ось чому бібліотеки не повинні використовувати кореневий реєстратор. Будь ласка, відкрийте звіт про помилку в цій бібліотеці та скажіть їм використовувати logging.getLogger(__name__)замість цього.
Бакуріу

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():
  ...

Я отримав помилку: об'єкт "Logger" не має атрибута "DEBUG". logger.DEBUGповинно бутиlogging.DEBUG
foxiris

Дякую! Це справді допомагає! Я встановлюю рівень реєстрації matplotlib після моєї основної конфігурації ведення журналу та перед командою, яка імпортує matplotlib. Вирішено!
GPH

Я встановив для журналювання matplotlibзначення WARNING після імпорту модуля, оскільки додавання його перед імпортом видасть помилку lint. Це все ще працювало для мене. Я використовую matplotlib==3.3.2в Python 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бібліотекою.


Яке ваше питання?
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 я встановив рівень реєстрації кореневого реєстратора на ПОМИЛКА (за замовчуванням це попередження):

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.


0

Інша річ, яку слід врахувати, - властивість розповсюдження класу Logger.

Наприклад, бібліотека py-pene для обробки викликів мила, навіть поміщена в 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 createg величезна кількість журналів

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

що оскільки розповсюдження журналів за замовчуванням є True, встановивши значення False, я відновив 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)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.