Logging Ibrido per Workflow: Guida Completa
Logging Ibrido per Workflow: Guida Completa
In questo tutorial, esploreremo come implementare un sistema di logging ibrido per workflow complessi. Imparerai a combinare i vantaggi del logging strutturato (machine-readable) e non strutturato (human-readable) per ottenere flessibilità e facilità di debugging in ambienti di produzione. Affronteremo concetti di base, implementazione pratica con Python e librerie come logging
e structlog
, esempi avanzati e esercizi per consolidare le tue conoscenze. Questo tutorial è adatto sia a sviluppatori junior che senior che desiderano migliorare le proprie competenze di logging in contesti di workflow distribuiti.
Logging Strutturato vs. Non Strutturato
Il logging è essenziale per monitorare e diagnosticare problemi in qualsiasi applicazione software. Esistono due approcci principali al logging: strutturato e non strutturato.
- Logging non strutturato: Utilizza stringhe di testo semplici per registrare eventi. È facile da implementare, ma difficile da analizzare automaticamente. Un esempio potrebbe essere
logger.info("Avvio del processo con ID: " + str(process_id))
. - Logging strutturato: Utilizza formati machine-readable come JSON per registrare eventi. È più difficile da implementare inizialmente, ma offre una maggiore flessibilità per l'analisi e l'aggregazione dei log. Un esempio potrebbe essere
logger.info("process_started", process_id=process_id)
.
Un sistema di logging ibrido combina questi due approcci per ottenere il meglio da entrambi i mondi: la leggibilità per gli umani e l'analizzabilità per le macchine.
Questo diagramma illustra come il logging ibrido combina i vantaggi di entrambi gli approcci.
Implementazione di Base con Python `logging`
Iniziamo con un esempio di base di come implementare un sistema di logging ibrido utilizzando la libreria standard logging
di Python. Combineremo formattazione di stringhe tradizionali con dizionari per aggiungere contesto strutturato.
import logging
# Configura il logger
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
# Crea un handler per la console
ch = logging.StreamHandler()
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
ch.setFormatter(formatter)
logger.addHandler(ch)
def elabora_dati(dati, process_id):
"""Elabora i dati e registra eventi con informazioni strutturate."""
logger.info(f"Inizio elaborazione dati per il processo {process_id}", extra={"process_id": process_id, "dati_size": len(dati)})
# Qui andrebbe la logica di elaborazione
risultato = len(dati) * 2 # Esempio di elaborazione
logger.info(f"Elaborazione completata per il processo {process_id}. Risultato: {risultato}", extra={"process_id": process_id, "risultato": risultato})
return risultato
# Esempio di utilizzo
dati = [1, 2, 3, 4, 5]
process_id = "processo_A123"
risultato = elabora_dati(dati, process_id)
print(f"Risultato finale: {risultato}")
In questo esempio, usiamo il parametro extra
del metodo logger.info()
per aggiungere informazioni strutturate (un dizionario) ai nostri log. Anche se il messaggio principale è una stringa, il dizionario extra
può essere elaborato da strumenti di logging e analisi per estrarre informazioni specifiche.
Utilizzo di `structlog` per Logging Avanzato
structlog
è una libreria Python che semplifica la creazione di log strutturati. Offre una maggiore flessibilità e performance rispetto alla libreria logging
standard. Installare con pip install structlog
.
import structlog
import logging
import sys
# Configurazione di structlog
structlog.configure(
processors=[
structlog.stdlib.add_log_level,
structlog.stdlib.add_logger_name,
structlog.stdlib.PositionalArgumentsFormatter(),
structlog.processors.StackInfoRenderer(),
structlog.processors.format_exc_info,
structlog.processors.TimeStamper(fmt="iso"),
structlog.stdlib.ProcessorFormatter.wrap_for_formatter,
],
context_class=dict,
logger_factory=structlog.stdlib.LoggerFactory(),
wrapper_class=structlog.stdlib.BoundLogger,
)
# Configurazione del formatter per la console
formatter = structlog.stdlib.ProcessorFormatter(
processor=structlog.dev.ConsoleRenderer(),
foreign_preprocessors=[
structlog.stdlib.add_log_level,
structlog.stdlib.add_logger_name,
]
)
# Configurazione del gestore di logging standard
handler = logging.StreamHandler(sys.stdout)
handler.setFormatter(formatter)
root_logger = logging.getLogger()
root_logger.addHandler(handler)
root_logger.setLevel(logging.INFO)
# Ottenere il logger structlog
log = structlog.get_logger()
def elabora_dati_structlog(dati, process_id):
"""Elabora i dati e registra eventi con informazioni strutturate usando structlog."""
log.info("Inizio elaborazione dati", process_id=process_id, dati_size=len(dati))
# Qui andrebbe la logica di elaborazione
risultato = len(dati) * 2 # Esempio di elaborazione
log.info("Elaborazione completata", process_id=process_id, risultato=risultato)
return risultato
# Esempio di utilizzo
dati = [1, 2, 3, 4, 5]
process_id = "processo_B456"
risultato = elabora_dati_structlog(dati, process_id)
print(f"Risultato finale: {risultato}")
Questo esempio mostra come configurare structlog
per produrre log strutturati e leggibili. Notare come le informazioni di contesto sono passate direttamente come argomenti alla funzione log.info()
. structlog
gestisce automaticamente la formattazione e l'output dei log.
Integrazione con Workflow Orchestrators
Per workflow complessi, è comune utilizzare orchestratori come Apache Airflow, Prefect o Dagster. L'integrazione del logging ibrido con questi strumenti richiede un approccio specifico.
Ad esempio, con Airflow, si può configurare un logger personalizzato per ogni task che scriva sia log strutturati (ad esempio, in JSON) che log non strutturati (per la console di Airflow). Questi log possono essere poi aggregati e analizzati utilizzando strumenti come Elasticsearch, Grafana o Splunk.
# Esempio concettuale con Airflow
from airflow.decorators import task
import structlog
@task
def elabora_dati_task(dati, process_id):
"""Task Airflow che utilizza structlog per il logging."""
log = structlog.get_logger()
log.info("Inizio elaborazione dati", process_id=process_id, dati_size=len(dati))
# Qui andrebbe la logica di elaborazione
risultato = len(dati) * 2 # Esempio di elaborazione
log.info("Elaborazione completata", process_id=process_id, risultato=risultato)
return risultato
Questo frammento di codice dimostra come si potrebbe integrare structlog
in un task Airflow. È fondamentale configurare correttamente gli handler di logging per catturare e centralizzare i log generati dai task.
Esercizi Pratici
Esercizio 1: Logging di Errori
Non specificataModifica l'esempio elabora_dati
per includere la gestione degli errori. Se si verifica un'eccezione, registra un messaggio di errore con informazioni dettagliate sull'eccezione (tipo, messaggio, traceback).
Esercizio 2: Logging con Livelli Diversi
Non specificataModifica l'esempio elabora_dati_structlog
per utilizzare diversi livelli di log (DEBUG, INFO, WARNING, ERROR) a seconda della situazione. Ad esempio, registra un messaggio DEBUG all'inizio della funzione, un messaggio WARNING se i dati in input sono vuoti e un messaggio ERROR se si verifica un errore.
Esercizio 3: Integrazione con File di Configurazione
Non specificataCrea un file di configurazione (ad esempio, in formato YAML) che definisca le impostazioni del logger (livello di log, formato, handler). Modifica l'esempio elabora_dati_structlog
per caricare la configurazione del logger da questo file.
Commenti 0
Nessun commento ancora. Sii il primo a dire la tua!
I commenti sono moderati e saranno visibili dopo l'approvazione.