Logowanie
Logowanie to kluczowy element każdego programu, który pozwala na rejestrowanie zdarzeń zachodzących podczas jego działania. Może być używane do monitorowania aplikacji, diagnostyki błędów oraz zbierania informacji potrzebnych do analizy zachowania programu.
Korzystanie z funkcji print
do debugowania jest całkiem wygodne, jednak szybko może stać się niewystarczające w bardziej zaawansowanych projektach. Python dostarcza name pakiet logging
, który pozwala modyfikować podstawową konfigurację logowania, ale także budować własne loggery
.
W logowaniu mamy poziomy (DEBUG
, INFO
, WARNING
, ERROR
, CRITICAL
), które pozwalają określić priorytet i character komunikatu. W zależności od potrzeb, możliwe jest także określanie z jaką szczegółowością zapisywane są komunikaty.
Dzięki logowaniu możemy przekazywać komunikaty nie tylko do konsoli, ale także zapisywać je bezpośrednio do pliku.
Podstawowa konfiguracja
Korzystanie z biblioteki logging
jest bardzo proste, poniżej prosty skrypt:
import logging
# Modyfikacja podstawowej konfiguracji
logging.basicConfig(level=logging.DEBUG,
format='%(asctime)s - %(levelname)s - %(message)s')
# Zgłaszanie komunikatów na różnych poziomach
logging.debug("Szczegółowy komunikat debug.")
logging.info("Informacyjny komunikat.")
logging.warning("Ostrzeżenie!")
logging.error("Wystąpił błąd.")
logging.critical("Błąd krytyczny!")
Skierowanie komunikatów do pliku:
import logging
logging.basicConfig(filename='app.log',
filemode='w',
level=logging.WARNING,
format='%(asctime)s - %(levelname)s - %(message)s')
logging.info("To nie zostanie zapisane, bo poziom jest zbyt niski.")
logging.error("To zostanie zapisane w pliku.")
Tworzenie własnych loggerów
Tworzenie własnych loggerów pozwala na większą kontrolę nad procesem logowania w aplikacjach wielomodułowych. Dzięki temu można:
- Oddzielić logi różnych modułów – każdy moduł może mieć swojego loggera, co ułatwia debugowanie.
- Zarządzać logowaniem niezależnie w każdym module – np. można ustawić inne poziomy logowania dla różnych części aplikacji.
- Zapewnić czytelność i porządek – logi z każdego modułu są łatwe do odróżnienia dzięki nazwie loggera.
Dobrą praktyką jest przypisywanie loggera
do każdego modułu, np. za pomocą:
from .utils import get_logger
_logger = get_logger(__name__)
Tylko oczywiście konieczna jest implementacja takiej funkcji, np. w module utils
:
import logging
from pathlib import Path
def get_logger(
name: str,
log_level: int | str = logging.INFO,
log_file: Path = Path("application.log")
) -> logging.Logger:
"""Builds a `Logger` instance with provided name and log levels for stream and file.
Args:
name: The name for the logger.
log_level: The default log level for the logger.
log_file: Path to the file where logs should be saved.
Returns:
The logger.
"""
logger = logging.getLogger(name)
logger.setLevel(log_level)
# Avoid adding multiple handlers if the logger already has them
if not logger.handlers:
# Formatter for both handlers
formatter = logging.Formatter(
fmt="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)
# Stream handler
stream_handler = logging.StreamHandler()
stream_handler.setLevel(logging.INFO) # Set level for stream
stream_handler.setFormatter(formatter)
logger.addHandler(stream_handler)
# File handler
file_handler = logging.FileHandler(log_file, mode="a")
file_handler.setLevel(logging.ERROR) # Set level for file
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)
return logger
Zadania
- W pliku
./src/zajecia08/utils/logging.py
stwórz funkcjęget_logger
do tworzenia własnych loggerów. Zwracany logger ma posiadać zarówno logowanie strumieniowe jak i do pliku. - Zaprezentuj działanie tej funkcji wykorzystujać
_logger = get_logger(...)
w co najmniej 2 modułach logując informacje na różnych poziomach.