Rejestrowanie w Pythonie - Wyłącz logowanie z importowanych modułów


107

Używam modułu rejestrowania Pythona i chciałbym wyłączyć komunikaty dziennika drukowane przez moduły innych firm, które importuję. Na przykład używam czegoś takiego:

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)

To wyświetla moje komunikaty debugowania, kiedy robię logger.debug ("moja wiadomość!"), Ale również wypisuje komunikaty debugowania z dowolnego modułu, który importuję (na przykład żądania i wiele innych rzeczy).

Chciałbym widzieć tylko komunikaty dziennika z modułów, które mnie interesują. Czy można tak ustawić moduł logowania?

Idealnie chciałbym móc nakazać rejestratorowi, aby drukował komunikaty z „ModuleX, ModuleY” i ignorował wszystkie inne.

Przyjrzałem się następującym, ale nie chcę wyłączać / włączać logowania przed każdym wywołaniem zaimportowanej funkcji: logowanie - jak zignorować zaimportowane logi modułów?

Odpowiedzi:


76

Problem polega na tym, że wywołanie getLoggerbez argumentów zwraca główny program rejestrujący, więc kiedy ustawiasz poziom na logging.DEBUGsiebie, ustawiasz również poziom dla innych modułów, które używają tego rejestratora.

Możesz rozwiązać ten problem, po prostu nie używając programu rejestrującego root. Aby to zrobić, podaj nazwę jako argument, na przykład nazwę swojego modułu:

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

spowoduje to utworzenie nowego programu rejestrującego, a tym samym nie spowoduje przypadkowej zmiany poziomu rejestrowania dla innych modułów.


Oczywiście musisz użyć logger.debugzamiast tego, logging.debugponieważ ta ostatnia jest wygodną funkcją, która wywołuje debugmetodę głównego rejestratora.

Jest to wspomniane w samouczku zaawansowanego rejestrowania . Pozwala także w prosty sposób dowiedzieć się, który moduł wywołał komunikat dziennika.


38
Tworzę rejestrator z __name__r, ale nadal widzę logi z zaimportowanych modułów. Próbuję skonfigurować logowanie za pomocą pliku konfiguracyjnego ini. Co mam w tym celu zrobić?
Durga Swaroop

8
Tworzenie loggera __name__również nie działało dla mnie. Może dlatego, że używam samodzielnego skryptu, a nie „modułu”? Pomogło mi skonfigurowanie logowania dla zaimportowanych modułów ( matpplotlibw moim przypadku) za pośrednictwem logging.getLogger("matplotlib").setLevel(logging.WARNING)i dla mojego skryptu przez logging.basicConfig.
bli

1
Chciałem tylko podkreślić wartość twojego wiersza „Oczywiście musisz użyć logger.debugzamiast logging.debug”. Łatwo popełnić błąd przy użyciu rejestrowania zamiast rejestratora, ale przywłaszcza sobie całą sprytną konfigurację, którą chcesz ustawić. Żyłem tym przez ostatnie kilka godzin!
timdadev

@bli Istnieje duża różnica między rejestrowaniem podczas tworzenia biblioteki a tworzeniem pliku wykonywalnego. Zasadniczo: jeśli piszesz moduł / pakiet przeznaczony do zaimportowania, nie powinieneś niczego konfigurować. Moduł powinien zawierać tylko logger = logging.getLogger('package.my_module')wywołania and your could logger.debug/warning, bez konfigurowania poziomów logowania lub programów obsługi. Kiedy piszesz tam aplikację binarną , powinieneś zdecydować o poziomie dla różnych dzienników i programów obsługi. Biblioteki zawierające konfigurację rejestrowania zawsze będą stanowić problem.
Bakuriu,

1
@IanS Nie, dlatego biblioteki nie powinny używać głównego programu rejestrującego. Otwórz raport o błędzie do tej biblioteki i powiedz im, aby logging.getLogger(__name__)zamiast tego użyli .
Bakuriu

48

Jeśli zamierzasz używać loggingpakietu pythona , powszechną konwencją jest definiowanie programu rejestrującego w każdym module, który go używa.

logger = logging.getLogger(__name__)

Robi to wiele popularnych pakietów Pythona, w tym requests. Jeśli pakiet używa tej konwencji, łatwo jest włączyć / wyłączyć rejestrowanie dla niego, ponieważ nazwa programu rejestrującego będzie taka sama jak nazwa pakietu (lub będzie dzieckiem tego rejestratora). Możesz nawet zarejestrować go w tym samym pliku, co inne rejestratory.

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)

16
Należy pamiętać, że gdy spróbujesz skonfigurować swoje loggery, jak w oficjalnym podstawowym samouczku, logging.basicConfig(...)wszystkie loggery będą teraz wyświetlać wyjście logging.lastResort(zaczynając od Pythona 3.2, który jest stderr), jeśli nie podano żadnej funkcji obsługi lub ustawionej funkcji obsługi. Więc nie używaj go, bo i tak będziesz nadal otrzymywać wszystkie komunikaty dziennika.
user136036

44

Nie jestem pewien, czy to jest właściwe, aby opublikować, ale utknąłem na długi czas i chciałem pomóc każdemu z tym samym problemem, ponieważ nie znalazłem go nigdzie indziej!

Otrzymywałem dzienniki debugowania z matplotlib, mimo że postępowałem zgodnie z dość prostą dokumentacją w zaawansowanym samouczku rejestrowania i rozwiązywaniu problemów . Inicjowałem mój logger w main()jednym pliku i importowałem funkcję do tworzenia wykresu z innego pliku (gdzie zaimportowałem matplotlib).

U mnie zadziałało ustawienie poziomu matplotlib przed jego zaimportowaniem, a nie później, jak w przypadku innych modułów w moim głównym pliku. Wydawało mi się to sprzeczne z intuicją, więc jeśli ktoś ma wgląd w to, jak ustawić konfigurację dla loggera, który nie został jeszcze zaimportowany, byłbym ciekawy, jak to działa. Dzięki!

W moim głównym pliku:

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

def main():
  ...

W moim plot.pypliku:

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

def generatePlot():
  ...

Wystąpił błąd: obiekt „Logger” nie ma atrybutu „DEBUG”. logger.DEBUGpowinno byćlogging.DEBUG
foxiris

Dzięki! To naprawdę pomaga! Ustawiam poziom logowania matplotlib po mojej głównej konfiguracji logowania i przed poleceniem, które zaimportuje matplotlib. Rozwiązany!
gph

Mam ustawiony na rejestrowanie matplotlibsię WARNING po ja importowany moduł, ponieważ dodanie go przed importem dałoby niestrzępiącą błąd. Nadal mi to działało. Używam matplotlib==3.3.2w Pythonie 3.7, jeśli to pomaga.
Koniec 2 i koniec

10

Spowoduje to wyłączenie wszystkich istniejących programów rejestrujących, takich jak te utworzone przez zaimportowane moduły, przy jednoczesnym korzystaniu z głównego programu rejestrującego (i bez konieczności ładowania zewnętrznego pliku).

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

Pamiętaj, że musisz najpierw zaimportować wszystkie moduły, których nie chcesz rejestrować! W przeciwnym razie nie będą one traktowane jako „istniejące rejestratory”. Następnie wyłączy wszystkie rejestratory z tych modułów. Może to prowadzić do pominięcia ważnych błędów!

Aby uzyskać bardziej szczegółowe przykłady przy użyciu powiązanych opcji konfiguracji, zobacz https://gist.github.com/st4lk/6287746 , a tutaj jest (częściowo działający) przykład użycia YAML do konfiguracji z coloredlogbiblioteką.


Jakie jest Twoje pytanie?
user1767754

1
Działa to na requestprzykład, ale nie zadziała, gdy zaimportowane moduły utworzą swoje loggery w swojej klasie, do której zadzwonisz później, tak jak APSchedulerrobi to podczas rozmowy BackgroundScheduler.BackgroundScheduler(). Zobacz tutaj rozwiązanie: stackoverflow.com/a/48891485/2441026
user136036

Działa to w moim przypadku z plikiem konfiguracyjnym yaml
user4015990

Pamiętaj, że musisz import logging.config, nie tylko logowanie.
user1717828

9

@Bakuriu dość elegancko wyjaśnia funkcję. I odwrotnie, możesz użyć tej getLogger()metody, aby odzyskać i ponownie skonfigurować / wyłączyć niechciane programy rejestrujące.

Chciałem też dodać logging.fileConfig()metodę akceptującą wywoływany parametr, disable_existing_loggersktóry wyłączy wcześniej zdefiniowane rejestratory (tj. W zaimportowanych modułach).


4

Możesz użyć czegoś takiego:

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

Spowoduje to ustawienie poziomu dziennika mojego własnego modułu na DEBUG, jednocześnie uniemożliwiając importowanemu modułowi korzystanie z tego samego poziomu.

Uwaga: "imported_module"można go zastąpić imported_module.__name__(bez cudzysłowów) i "my_own_logger_name"można go zastąpić, __name__jeśli wolisz to zrobić.


1

Miałem ten sam problem. Mam plik logging_config.py, który importuję do wszystkich innych plików py. W pliku logging_config.py ustawiam poziom logowania root loggera na ERROR (domyślnie jest to ostrzeżenie):

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

W innych modułach importuję logging_config.py i deklaruję nowy logger i ustawiam jego poziom na debugowanie:

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

W ten sposób wszystko, co loguję w moich plikach py, jest rejestrowane, ale rzeczy rejestrowane na poziomie debugowania i informacji przez zaimportowane moduły, takie jak urllib, request, boto3 itp. Nie są rejestrowane. Jeśli wystąpi jakiś błąd w tym module importu, jest on rejestrowany, ponieważ ustawiłem poziom rejestratorów głównych na ERROR.


1

Inną rzeczą do rozważenia jest właściwość propagate klasy Logger.

Na przykład biblioteka py-suds do obsługi wywołań mydła, nawet ustawiona na 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)

logs loguje o module o nazwie sxbasics.py tworzenie dużej ilości logów

wprowadź opis obrazu tutaj

że ponieważ propagacja dzienników jest domyślnie ustawiona na True, a ustawienie to False, zamiast tego odzyskałem 514 MB dzienników.

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)

1

Po prostu zrobienie czegoś takiego rozwiązuje problem:

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

UWAGA : Gdziekolwiek umieszczasz tę linię, upewnij się, że wszystkie importy w całym projekcie są wykonywane w samym pliku. :)


0

W moim przypadku jedyne, co pomogło, to wymuszenie propagateatrybutu niechcianego loggera False, tj

logging.getLogger("module").propagate = False

Jeśli wynikiem tego jest fałsz, komunikaty rejestrowania nie są przekazywane do programów obsługi nadrzędnych programów rejestrujących.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.