modulo 09 / lezione 49

Performance, memoria e osservabilità

Log strutturati, metriche, trace, SLO

Costruire segnali utili in produzione senza esporre dati sensibili. Introdurre SLI, SLO ed error budget.

60 min Debugging

In parole semplici

In produzione non hai DevTools. Hai solo i segnali che hai progettato in anticipo: log strutturati, metriche, trace. Senza, ogni incidente è cieco. Con, si indaga in pochi minuti.

Le SLO (Service Level Objectives) trasformano queste metriche in accordi misurabili sulla qualità del servizio.

Parole nuove

  • log strutturato: log in formato chiave-valore (JSON), non testo libero.
  • metrica: misura numerica nel tempo (counter, gauge, histogram).
  • trace: percorso completo di una richiesta attraverso più servizi.
  • correlation id: id che lega log, metriche e trace di una stessa richiesta.
  • SLI / SLO / error budget: indicatori, obiettivi, margine di tolleranza.

Cosa impari

  • Scrivere log strutturati in JSON.
  • Tipi di metriche e loro uso (RED, USE).
  • Tracciare una richiesta end-to-end.
  • Definire SLO ed error budget.

Log strutturati

Anti-pattern:

console.log("Utente " + userId + " ha pagato " + importo + " in " + duration + "ms");

Difficile da:

  • Filtrare.
  • Aggregare.
  • Cercare per campo.

Pattern strutturato:

logger.info({
  event: "pagamento_completato",
  user_id: userId,
  importo,
  duration_ms: duration,
  correlation_id: correlationId,
});

Output JSON:

{"level":"info","timestamp":"2025-05-16T14:30:00Z","event":"pagamento_completato","user_id":42,"importo":99.9,"duration_ms":234,"correlation_id":"abc-123"}

Sistemi di logging (Datadog, Loki, CloudWatch) parsano JSON e permettono query:

event="pagamento_completato" AND duration_ms > 1000

Librerie: pino (Node), winston (Node), structlog (Python), Logback+ Logstash encoder (Java).

Cosa NON loggare

Mai:

  • Password, token, chiavi API.
  • Numeri di carta di credito completi (PCI DSS).
  • Dati personali sensibili senza esplicita ragione (GDPR).

Pattern: maschera al confine.

function maskCard(num) {
  return "****" + num.slice(-4);
}
logger.info({ event: "pagamento", card: maskCard(card) });

Livelli

debug    → dettaglio per dev
info     → eventi normali
warn     → situazione strana
error    → errore gestito
fatal    → errore catastrofico (terminazione)

In produzione di solito info+. In dev debug+.

Metriche: tre tipi

Counter

Aumenta monotonicamente. “Quante chiamate ha ricevuto l’endpoint X?”

metrics.counter("http_requests_total", { endpoint: "/utenti", status: 200 }).inc();

Gauge

Valore corrente che sale e scende. “Quante connessioni DB attive?”

metrics.gauge("db_connections_active").set(currentValue);

Histogram

Distribuzione di valori (latenza, dimensione payload).

metrics.histogram("http_request_duration_seconds", { endpoint: "/utenti" }).observe(0.234);

Permette di calcolare p50, p95, p99.

Pattern RED e USE

RED (per servizi)

  • Rate: richieste/secondo.
  • Errors: errori/secondo.
  • Duration: distribuzione del tempo di risposta.

USE (per risorse: CPU, RAM, disco)

  • Utilization: % usato.
  • Saturation: coda di lavoro pendente.
  • Errors: errori.

Coprire entrambi dà visibilità completa.

Trace distribuiti

Una richiesta che attraversa 5 servizi è difficile da debuggare con log isolati. Il trace la racconta come un albero:

[gateway 250ms]
  └── [auth-service 30ms]
  └── [users-service 180ms]
        └── [postgres 150ms]
              └── [SELECT utenti WHERE id=42]

Standard: OpenTelemetry. Strumenti: Jaeger, Zipkin, Honeycomb, Datadog APM.

In codice:

import { trace } from "@opentelemetry/api";

const tracer = trace.getTracer("users-service");
const span = tracer.startSpan("fetch-user");
try {
  // operazione
  span.setAttribute("user.id", id);
} finally {
  span.end();
}

I trace e i log si correlano via correlation id (anche detto trace id).

Correlation id

Ogni richiesta riceve un id univoco (header X-Request-ID o traceparent). Propagato in:

  • Log.
  • Trace.
  • Errori (Sentry).

Diagnosi: ho un errore in Sentry → copio trace_id → query in Loki → vedo tutti i log di quella richiesta in tutti i servizi.

SLI, SLO, error budget

SLI (Service Level Indicator)

Misura concreta. Esempio: ”% di richieste in < 500ms”.

SLO (Service Level Objective)

Target dell’SLI. Esempio: “99% delle richieste < 500ms in 30 giorni”.

Error budget

Margine di errore tollerato. Se SLO = 99.9% disponibilità, error budget = 0.1% = ~43 minuti/mese di downtime.

Usato per decidere:

  • Se il budget è consumato, si stoppa il rilascio di feature nuove e si lavora su stabilità.
  • Se il budget è avanzato, si può rischiare di più.

Trasforma decisioni “sentite” in decisioni “numerate”.

Esercizio guidato

In un piccolo server:

  1. Sostituisci console.log con pino in JSON.
  2. Aggiungi un middleware che genera un correlation id e lo mette in ogni log della richiesta.
  3. Aggiungi un counter per richieste e un histogram per latenza (usa prom-client).
  4. Espone /metrics in formato Prometheus.
  5. Definisci uno SLO: “p95 < 200ms” e verificalo nei tuoi dati.

Errori frequenti del principiante

  • Log testuali in produzione. Impossibili da aggregare.
  • Log con dati sensibili. Compliance violata, multe.
  • Metriche senza histogram. Vedi la media ma non i p95/p99.
  • SLO senza error budget. Hai un numero, non una decisione.