Introduzione: Observability nei Sistemi Distribuiti
L'observability e la capacità di comprendere lo stato interno di un sistema complesso analizzando i segnali che produce verso l'esterno. A differenza del monitoring tradizionale, che risponde a domande predefinite ("il server e attivo?", "la CPU supera l'80%?"), l'observability permette di esplorare domande sconosciute: perchè questa richiesta ha impiegato 3 secondi? Dove si e verificato il collo di bottiglia in un flusso che attraversa 12 microservizi?
Nei sistemi moderni basati su microservizi, container e architetture cloud-native, il monitoring tradizionale non e più sufficiente. Una singola richiesta utente può attraversare decine di servizi, code di messaggi e database prima di completarsi. Senza observability, diagnosticare problemi in questi ambienti diventa un esercizio di congetture.
In questa serie di 12 articoli, esploreremo l'observability moderna con OpenTelemetry (OTel), lo standard open source che sta unificando la raccolta di telemetria. Partiremo dai fondamenti per arrivare a implementazioni production-grade con Jaeger, Prometheus e Grafana.
Cosa Imparerai in Questo Articolo
- La differenza fondamentale tra monitoring e observability
- I Three Pillars dell'observability: Metrics, Logs e Traces
- Il concetto di cardinalita e il suo impatto sui costi
- Come i segnali di telemetria si correlano tra loro
- Il panorama degli strumenti di observability moderni
- perchè OpenTelemetry sta diventando lo standard de facto
Monitoring vs Observability: Una Distinzione Fondamentale
Il monitoring e una pratica consolidata che consiste nel raccogliere metriche predefinite e confrontarle con soglie stabilite. Quando una metrica supera la soglia, si attiva un alert. Questo approccio funziona bene per sistemi monolitici dove i failure mode sono noti e prevedibili.
L'observability, invece, parte dal presupposto che nei sistemi distribuiti i failure mode sono imprevedibili. Non puoi creare alert per problemi che non sai ancora di avere. L'observability ti permette di esplorare il comportamento del sistema in tempo reale, formulando domande ad hoc basate sui segnali disponibili.
Monitoring vs Observability in Pratica
| Aspetto | Monitoring | Observability |
|---|---|---|
| Approccio | Reattivo (alert su soglie) | Esplorativo (query ad hoc) |
| Domande | Predefinite e note | Sconosciute e dinamiche |
| Dati | Metriche aggregate | Metriche, log, tracce correlate |
| Debug | "Cosa e rotto?" | "perchè e rotto?" |
| Scalabilità | Dashboard statiche | Esplorazione interattiva |
Un'analogia utile: il monitoring e come un cruscotto dell'auto con spie predefinite (temperatura, olio, benzina). L'observability e come avere un sistema diagnostico completo che ti permette di analizzare qualsiasi parametro del motore in tempo reale, anche quelli che non sapevi di dover controllare.
I Three Pillars dell'Observability
L'observability si basa su tre tipi fondamentali di segnali di telemetria, chiamati Three Pillars: Metrics, Logs e Traces. Ognuno offre una prospettiva diversa sul comportamento del sistema e, insieme, forniscono una visione completa.
1. Metrics: Dati Numerici Aggregati
Le metrics sono valori numerici misurati nel tempo. Rappresentano lo stato del sistema in forma aggregata e sono il tipo di telemetria più efficiente in termini di storage e query. Le metriche rispondono alla domanda: "Quanto?"
# Esempio: definire metriche con OpenTelemetry Python SDK
from opentelemetry import metrics
meter = metrics.get_meter("my-service")
# Counter: valori che solo aumentano
request_counter = meter.create_counter(
name="http.server.request.count",
description="Numero totale di richieste HTTP",
unit="1"
)
# Histogram: distribuzione di valori
latency_histogram = meter.create_histogram(
name="http.server.request.duration",
description="Durata delle richieste HTTP",
unit="ms"
)
# UpDownCounter: valori che possono aumentare o diminuire
active_connections = meter.create_up_down_counter(
name="http.server.active_connections",
description="Connessioni attive correnti"
)
# Registrare valori
request_counter.add(1, {"http.method": "GET", "http.route": "/api/users"})
latency_histogram.record(45.2, {"http.method": "GET", "http.status_code": "200"})
active_connections.add(1)
Le metriche sono ideali per dashboard, alerting e trend analysis. Hanno un costo di storage fisso indipendente dal volume di traffico, rendendole perfette per il monitoring a lungo termine.
2. Logs: Eventi Discreti con Contesto
I logs sono record di eventi discreti con timestamp e payload testuale o strutturato. Sono il segnale di telemetria più familiare agli sviluppatori e rispondono alla domanda: "Cosa e successo?"
import logging
import json
# Log strutturato con contesto di tracing
logger = logging.getLogger("order-service")
def process_order(order_id, user_id):
logger.info(json.dumps({
"event": "order.processing.started",
"order_id": order_id,
"user_id": user_id,
"timestamp": "2026-02-17T10:30:00Z",
"trace_id": "abc123def456",
"span_id": "span789",
"service": "order-service",
"environment": "production"
}))
# ... logica di business ...
logger.info(json.dumps({
"event": "order.processing.completed",
"order_id": order_id,
"duration_ms": 234,
"trace_id": "abc123def456",
"span_id": "span789"
}))
I log strutturati (JSON) sono preferiti rispetto ai log testuali perchè permettono query efficienti
e correlazione automatica con tracce e metriche. Il pattern chiave e includere sempre
trace_id e span_id nei log per abilitare la correlazione.
3. Traces: Il Percorso delle Richieste
Le traces (tracce distribuite) rappresentano il percorso completo di una richiesta attraverso il sistema distribuito. Ogni trace e composta da span, dove ogni span rappresenta un'operazione all'interno di un servizio. Le tracce rispondono alla domanda: "Dove e successo?"
from opentelemetry import trace
tracer = trace.get_tracer("order-service")
def handle_order_request(request):
# Span root: rappresenta l'intera operazione
with tracer.start_as_current_span("process-order") as span:
span.set_attribute("order.id", request.order_id)
span.set_attribute("user.id", request.user_id)
# Span figlio: validazione
with tracer.start_as_current_span("validate-order") as child:
child.set_attribute("validation.rules_count", 5)
validate(request)
# Span figlio: pagamento
with tracer.start_as_current_span("process-payment") as child:
child.set_attribute("payment.method", "credit_card")
child.set_attribute("payment.amount", request.total)
charge_payment(request)
# Span figlio: notifica
with tracer.start_as_current_span("send-notification") as child:
child.set_attribute("notification.type", "email")
notify_user(request.user_id)
Le tracce sono fondamentali per il debugging nei sistemi distribuiti. Permettono di visualizzare esattamente dove il tempo viene speso, quale servizio ha causato un errore, e come le operazioni si relazionano tra loro attraverso il grafo di dipendenze.
Correlazione dei Segnali: Il Vero Potere dell'Observability
I tre pilastri diventano veramente potenti quando sono correlati tra loro. Un trace ID collega una traccia ai log generati durante quella richiesta. Le metriche con label di servizio permettono di aggregare i dati per gli stessi servizi che appaiono nelle tracce. Questa correlazione trasforma dati isolati in contesto operativo.
Flusso di Debug con Segnali Correlati
1. Alert dalla metrica: "La latenza P99 dell'endpoint /checkout supera 2 secondi"
2. Drill-down nelle tracce: filtri per endpoint /checkout con latenza > 2s, trovi che lo span payment-gateway impiega 1.8s
3. Esame dei log: usando il trace_id, trovi nei log del payment-gateway un timeout verso il provider esterno
4. Root cause: il provider di pagamento ha un problema di rete, causa retry che aumentano la latenza complessiva
Cardinalita: Il Nemico Silenzioso
La cardinalita e il numero di combinazioni uniche di valori che le label/attributi delle metriche possono assumere. E uno dei concetti più importanti nell'observability perchè impatta direttamente su costi di storage, performance delle query e scalabilità del sistema.
Ad esempio, una metrica http_requests_total con label method (4 valori),
status (5 valori) e endpoint (20 valori) produce 4 x 5 x 20 = 400 serie
temporali. Ma se aggiungi user_id con 100.000 utenti, esplodi a 40 milioni di serie.
Questo fenomeno si chiama cardinality explosion.
Regole d'Oro per la Cardinalita
- Mai usare user ID, session ID o request ID come label delle metriche
- Usare valori a bassa cardinalita: method, status_code, service_name, environment
- Spostare i dati ad alta cardinalita nelle tracce (attributi degli span) o nei log
- Monitorare il numero di serie temporali attive nel backend di metriche
- Limitare le label a un massimo di 5-7 per metrica con valori bounded
Il Panorama degli Strumenti di Observability
Il mercato degli strumenti di observability e ricco e in continua evoluzione. Possiamo classificarli in tre categorie principali:
Soluzioni Commercial SaaS
Piattaforme come Datadog, New Relic, Dynatrace e Splunk offrono soluzioni all-in-one con metriche, log, tracce, profiling e APM integrati. Sono facili da adottare ma possono diventare costose con volumi elevati di telemetria.
Stack Open Source
Lo stack open source più comune combina Prometheus (metriche), Jaeger o Tempo (tracce), Loki (log) e Grafana (visualizzazione). Offre massimo controllo e zero costi di licenza, ma richiede competenze operative per il deployment e la manutenzione.
OpenTelemetry: Lo Standard Unificante
OpenTelemetry non e un backend di observability, ma uno standard per la raccolta e l'esportazione di telemetria. Fornisce SDK per ogni linguaggio, un collector per il routing dei dati e semantic conventions per uniformare i nomi degli attributi. OTel e vendor-neutral: puoi instrumentare il codice una volta e inviare i dati a qualsiasi backend compatibile.
perchè OpenTelemetry e il Futuro
OpenTelemetry e il secondo progetto più attivo della Cloud Native Computing Foundation (CNCF) dopo Kubernetes. Con oltre 1.000 contributori e il supporto di tutti i principali vendor di observability, OTel sta diventando lo standard de facto per la telemetria. Instrumentare con OTel oggi significa avere la liberta di cambiare backend domani senza modificare il codice applicativo.
Metriche Chiave per l'Observability
Indipendentemente dagli strumenti scelti, ci sono metriche fondamentali che ogni sistema dovrebbe monitorare. Il framework RED (Rate, Errors, Duration) e il più usato per i servizi, mentre il framework USE (Utilization, Saturation, Errors) si applica alle risorse infrastrutturali.
# Esempio: alert rules basate su RED method (Prometheus)
groups:
- name: red-alerts
rules:
# Rate: richieste al secondo
- alert: HighRequestRate
expr: rate(http_requests_total[5m]) > 1000
for: 5m
labels:
severity: warning
annotations:
summary: "Tasso di richieste elevato"
# Errors: tasso di errori
- alert: HighErrorRate
expr: |
rate(http_requests_total{status_code=~"5.."}[5m])
/ rate(http_requests_total[5m]) > 0.05
for: 2m
labels:
severity: critical
annotations:
summary: "Tasso di errori superiore al 5%"
# Duration: latenza P99
- alert: HighLatency
expr: |
histogram_quantile(0.99,
rate(http_request_duration_seconds_bucket[5m])
) > 2
for: 5m
labels:
severity: warning
annotations:
summary: "Latenza P99 superiore a 2 secondi"
Conclusioni e Prossimi Passi
L'observability non e un prodotto da acquistare, ma una proprietà del sistema che si costruisce attraverso instrumentazione accurata e correlazione dei segnali. I Three Pillars (Metrics, Logs, Traces) forniscono tre prospettive complementari che, insieme, permettono di diagnosticare qualsiasi problema in un sistema distribuito.
La chiave per un'observability efficace e la correlazione: collegare metriche, log e tracce tramite identificatori condivisi (trace ID, service name) per poter navigare fluidamente da un alert a una traccia, da una traccia ai log rilevanti, e viceversa.
Nel prossimo articolo approfondiremo OpenTelemetry, analizzando la sua architettura, la distinzione tra API e SDK, il protocollo OTLP e le semantic conventions che standardizzano la nomenclatura della telemetria.







