programowanieProgramista backend Python

Jak działa logowanie w Pythonie za pomocą modułu logging? Jakie poziomy logowania istnieją i jak prawidłowo konfigurować logger w projekcie składającym się z wielu modułów? Podaj przykłady, wyjaśnij subtelności i powszechne błędy.

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź.

Moduł logging to standardowe narzędzie Pythona do prowadzenia logów. Wprowadza hierarchię loggerów i obsługuje poziomy logowania (severity levels): DEBUG, INFO, WARNING, ERROR, CRITICAL. Prawidłowe użycie pozwala na centralne zarządzanie wyjściem, zapisywanie logów do plików, wysyłanie ich e-mailem, filtrowanie według poziomu itp.

Główna idea to tworzenie loggerów z nazwami (logging.getLogger(__name__)) w każdym module, nie tworzenie globalnego loggera root od nowa w każdym miejscu. Konfiguracja (format, handlerzy, poziom) jest wykonywana centralnie na starcie aplikacji.

Przykład konfiguracji:

import logging logging.basicConfig(format='%(levelname)s:%(name)s:%(message)s', level=logging.INFO) logger = logging.getLogger(__name__) def foo(): logger.info('Informacyjna wiadomość') logger.error('Błąd!') foo()

Pytanie z haczykiem.

Dlaczego nie można wywoływać logging.basicConfig() w każdym module? Co się stanie, jeśli to zrobimy?

Odpowiedź: logging.basicConfig() konfiguruje logger root tylko raz na sesję Pythona. Powtórne wywołania, jeśli logger root został już zainicjowany, będą ignorowane. W rezultacie, jeśli różne moduły próbują wywołać basicConfig() z własnymi formatami/poziomami — zadziała tylko ten pierwszy!

Przykłady rzeczywistych błędów z powodu nieznajomości subtelności tematu.


Historia

W dużym projekcie każdy programista konfigurował logging według własnych upodobań przez basicConfig i lokalne handlerzy. Z tego powodu niektóre logi w ogóle się nie wyświetlały, inne były duplikowane dziesięciokrotnie, a wiadomości z różnych modułów nie zmieściły się w jednym pliku.


Historia

Podczas migracji usługi internetowej na wielopoziomowe logowanie zapomniano wskazać nazwę loggera przez getLogger(__name__), wszędzie pisano do loggera root. W rezultacie niemożliwe było ustalenie, skąd pochodził konkretny log.


Historia

Używano funkcji logger.error() do zapisywania wszystkich wiadomości, nawet tych niebędących błędami. W rezultacie automatyczne systemy monitorowania ciągle "podnosiły alarm", ponieważ widziały wysoki poziom błędów, chociaż były to zwykłe wiadomości debug/info.