modulo 20 di 24 / Ore 41-60

Moduli 17-24 / Checkpoint 60 ore

Osservabilità: capire cosa è successo

Come costruire osservabilità minima nel Registro con un logger semplice: eventi da registrare, struttura dei log, come ricostruire cosa è successo leggendo la sequenza degli eventi.

25 minArchitettura del software

Osservabilità: capire cosa è successo

Questa lezione appartiene al Modulo 20 — Logging e osservabilità minima.

Obiettivo della lezione

Costruire nel Registro la capacità di ricostruire cosa è successo leggendo i log — senza dover riaprire il codice, senza dover riprodurre il problema, guardando solo la sequenza di eventi registrati.

Alla fine i log del Registro raccontano una storia leggibile: quale azione è stata tentata, qual è stato il risultato, quale errore è comparso.

Perché serve nel percorso

Senza osservabilità, quando qualcosa va storto devi ricreare il problema passo per passo. Con i log giusti, puoi leggere cosa è successo e capire dove si trova il problema in pochi secondi.

Nel Registro del laboratorio

Scenario di esempio — un prestito fallisce:

Con i log corretti, la console mostra questa sequenza:

[INFO] ui_prestito_richiesto { strumentoId: "str-003", studenteId: "stu-042" }
[INFO] caso_uso_registra_prestito_avviato { strumentoId: "str-003" }
[WARN] registra_prestito_strumento_guasto { strumentoId: "str-003", stato: "guasto" }
[ERROR] ui_registra_prestito_fallita { codice: "STRUMENTO_GUASTO_NON_PRESTABILE" }

Leggendo questa sequenza sai esattamente cosa è successo: l’utente ha tentato di prestare lo strumento “str-003”, il caso d’uso ha trovato lo strumento ma era guasto, l’operazione è stata rifiutata e la UI ha mostrato l’errore.

Scenario di successo:

[INFO] ui_prestito_richiesto { strumentoId: "str-001", studenteId: "stu-015" }
[INFO] caso_uso_registra_prestito_avviato { strumentoId: "str-001" }
[INFO] prestito_registrato { strumentoId: "str-001", prestitoId: "pre-007" }
[INFO] ui_prestito_confermato { prestitoId: "pre-007" }

Chiaro, sequenziale, senza dati personali (studenteId è un ID anonimo).

Procedura guidata

  1. Identifica gli eventi principali del Registro: quali azioni avvengono, quali errori possono comparire.
  2. Assegna un nome a ogni evento (formato: contesto_azione_esito).
  3. Aggiungi i log al codice usando il logger creato nella lezione precedente.
  4. Usa il Registro normalmente e leggi la console: la sequenza racconta la storia?
  5. Simula un errore e verifica che la causa sia leggibile dai log.

Attività in classe

Definisci gli eventi da loggare per l’operazione “chiudi prestito”:

## Piano di logging per chiudiPrestito

Evento 1 — avvio operazione nella UI:
  nome:
  dati minimi:

Evento 2 — caso d'uso avviato:
  nome:
  dati minimi:

Evento 3 — successo:
  nome:
  dati minimi:

Evento 4 — errore PRESTITO_NON_TROVATO:
  nome:
  dati minimi:

Evento 5 — errore PRESTITO_GIA_CHIUSO:
  nome:
  dati minimi:

Poi implementa i log e verifica che la sequenza sia leggibile in console.

Spiegazione guidata per studiare

L’osservabilità minima richiede tre tipi di eventi:

1. Evento di input — cosa è arrivato nel sistema:

logger.info("ui_chiudi_prestito_richiesto", { prestitoId });

2. Evento di processo — cosa ha fatto il sistema:

logger.info("prestito_chiuso", { prestitoId, chiusura: new Date().toISOString() });
// oppure in caso di errore:
logger.warn("chiudi_prestito_gia_chiuso", { prestitoId });

3. Evento di output — cosa ha mostrato la UI:

logger.info("ui_chiudi_prestito_confermato", { prestitoId });
// oppure:
logger.error("ui_chiudi_prestito_fallita", err, { prestitoId });

Con questi tre tipi, puoi ricostruire ogni operazione del Registro.

Struttura dei nomi degli eventi:

[contesto]_[azione]_[esito opzionale]

ui_prestito_richiesto
caso_uso_registra_prestito_avviato
prestito_registrato
ui_prestito_confermato
ui_prestito_fallita

Cosa includere nei dati:

  • sempre: gli ID degli oggetti coinvolti (strumentoId, prestitoId);
  • per gli errori: il codice errore;
  • mai: nomi di studenti, contenuto completo degli oggetti.

Esempio completo del flusso registraPrestito:

// src/presentation/azioniPrestito.js
document.getElementById("btn-presta").addEventListener("click", () => {
  const strumentoId = document.getElementById("strumento-selezionato").value;
  const studenteId = document.getElementById("studente-id").value;

  logger.info("ui_prestito_richiesto", { strumentoId }); // log input

  try {
    const risultato = registraPrestito({ strumentoId, studenteId }, archivioStrumenti, archivioPrestiti);
    logger.info("ui_prestito_confermato", { prestitoId: risultato.id }); // log output successo
    mostraMessaggio(elem, "Prestito registrato.", "successo");
  } catch (err) {
    logger.error("ui_prestito_fallita", err, { strumentoId }); // log output errore
    mostraMessaggio(elem, traduciErrore(err.message), "errore");
  }
});

// src/application/registraPrestito.js
export function registraPrestito({ strumentoId, studenteId }, archivioStrumenti, archivioPrestiti) {
  logger.info("caso_uso_registra_prestito_avviato", { strumentoId }); // log processo

  const strumento = archivioStrumenti.trovaPerId(strumentoId);
  if (!strumento) {
    logger.warn("registra_prestito_strumento_non_trovato", { strumentoId });
    throw new Error("STRUMENTO_NON_TROVATO");
  }
  if (strumento.stato !== "disponibile") {
    logger.warn("registra_prestito_strumento_non_disponibile", { strumentoId, stato: strumento.stato });
    throw new Error(`STRUMENTO_${strumento.stato.toUpperCase()}_NON_PRESTABILE`);
  }

  const prestito = { id: crypto.randomUUID(), strumentoId, studenteId, apertura: new Date(), chiusura: null };
  archivioPrestiti.salva(prestito);
  archivioStrumenti.aggiorna({ ...strumento, stato: "in_prestito" });

  logger.info("prestito_registrato", { strumentoId, prestitoId: prestito.id }); // log successo
  return prestito;
}

Passo per passo nel progetto

  1. Elenca tutte le operazioni del Registro: registra strumento, cerca, presta, chiudi, segnala guasto.
  2. Per ogni operazione: definisci 3 eventi (input, processo, output).
  3. Aggiungi i log nel codice — partendo dal caso d’uso più usato.
  4. Usa il Registro e verifica che la console mostri la sequenza corretta.
  5. Simula un errore e verifica che la causa sia leggibile.

Prova di comprensione

  • Cosa si intende per “ricostruire cosa è successo dai log”?
  • Perché loggare strumentoId e non il nome dello strumento?
  • Qual è la differenza tra logger.warn e logger.error nel Registro?
  • Come si verifica che i log siano sufficienti senza aprire il codice?

Errori da evitare

  • loggare oggetti completi (JSON.stringify(strumento)) — contiene dati non necessari;
  • usare nomi di eventi generici (errore, ok, fatto) — non permettono di capire cosa è successo;
  • loggare solo gli errori e non i successi — non si capisce se il sistema funziona normalmente;
  • loggare troppo — 20 messaggi per ogni click rendono la console inutile.

Prodotto da consegnare

I log per tutte le operazioni principali del Registro (registra strumento, registra prestito, chiudi prestito, segnala guasto) con eventi di input, processo e output. La console mostra una sequenza leggibile dopo 5 minuti di uso normale.

Checklist di chiusura

  • ogni operazione principale ha almeno 3 eventi loggati (input, processo, output)
  • i nomi degli eventi seguono il formato [contesto]_[azione]_[esito]
  • i log non contengono nomi di studenti o oggetti completi
  • la sequenza dei log in console racconta la storia dell’operazione
  • gli errori includono il codice errore come contesto

Risultato atteso

Ho aggiunto log strutturati per registraPrestito e chiudiPrestito.
La console mostra la sequenza ui_richiesto → caso_uso_avviato → prestito_registrato.
In caso di errore la sequenza mostra la causa specifica (strumento guasto, prestito chiuso).
I log non contengono dati personali degli studenti.
Il prossimo passo sarebbe aggiungere log per registraStrumento e cercaStrumenti.