modulo 20 di 24 / Ore 41-60

Moduli 17-24 / Checkpoint 60 ore

Log tecnici e messaggi per l'utente

Come separare i log tecnici (console.error con dettaglio) dai messaggi per l'utente (UI leggibile) nel Registro — con un wrapper logger minimale e esempi di uso corretto.

25 min Architettura del software

Log tecnici e messaggi per l’utente

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

Obiettivo della lezione

Separare in modo netto i log tecnici (destinati allo sviluppatore che analizza la console) dai messaggi per l’utente (destinati al docente o al tecnico che usa il Registro). Alla fine i due canali sono distinti nel codice e nella UI.

Perché serve nel percorso

Quando un’operazione fallisce nel Registro, accadono due cose distinte: lo sviluppatore deve capire cosa è successo (log tecnico), e l’utente deve sapere cosa fare (messaggio UI). Se questi due canali si mescolano, succede uno di questi problemi:

  • l’utente vede Cannot read properties of undefined — incomprensibile;
  • lo sviluppatore vede solo “Si è verificato un errore” — inutile per il debug.

Nel Registro del laboratorio

Il logger tecnico va nella console, contiene dettagli tecnici, non viene mai mostrato all’utente:

// src/infrastructure/logger.js
export const logger = {
  info(evento, contesto = {}) {
    console.info(`[INFO] ${evento}`, contesto);
  },
  warn(evento, contesto = {}) {
    console.warn(`[WARN] ${evento}`, contesto);
  },
  error(evento, errore, contesto = {}) {
    // Logga il messaggio tecnico completo, incluso lo stack
    console.error(`[ERROR] ${evento}`, { messaggio: errore?.message, ...contesto });
  }
};

Il messaggio per l’utente va nella UI, è scritto in italiano comprensibile, non contiene stack trace o codici interni:

// src/presentation/messaggi.js
export function mostraMessaggio(elemento, testo, tipo = "info") {
  elemento.textContent = testo;
  elemento.className = `messaggio messaggio--${tipo}`;
}

export function traduciErrore(codice) {
  const messaggi = {
    STRUMENTO_NON_TROVATO: "Lo strumento cercato non esiste più nel registro.",
    STRUMENTO_GUASTO_NON_PRESTABILE: "Questo strumento è guasto e non può essere prestato.",
    PRESTITO_GIA_CHIUSO: "Questo prestito è già stato chiuso.",
    NOME_STRUMENTO_NON_VALIDO: "Il nome inserito non è valido (minimo 2 caratteri)."
  };
  return messaggi[codice] ?? "Si è verificato un errore. Riprova o contatta il tecnico.";
}

Come si usano insieme:

// src/application/registraPrestito.js
import { logger } from "../infrastructure/logger.js";

export function registraPrestito({ strumentoId, studenteId }, archivioStrumenti, archivioPrestitif) {
  const strumento = archivioStrumenti.trovaPerId(strumentoId);
  if (!strumento) {
    logger.warn("prestito_strumento_non_trovato", { strumentoId }); // canale tecnico
    throw new Error("STRUMENTO_NON_TROVATO");                        // codice per la UI
  }
  // ...
}

// src/presentation/azioniPrestito.js
import { traduciErrore, mostraMessaggio } from "./messaggi.js";

try {
  registraPrestito({ strumentoId, studenteId }, archivioStrumenti, archivioPrestiti);
  mostraMessaggio(elementoUI, "Prestito registrato.", "successo");
} catch (err) {
  logger.error("ui_registra_prestito_fallita", err, { strumentoId });  // canale tecnico
  mostraMessaggio(elementoUI, traduciErrore(err.message), "errore");   // canale utente
}

Procedura guidata

  1. Crea src/infrastructure/logger.js con le funzioni info, warn, error.
  2. Crea src/presentation/messaggi.js con mostraMessaggio e traduciErrore.
  3. Cerca tutti i posti nel codice dove un errore viene gestito.
  4. Per ogni gestione errori: aggiungi il log tecnico nel canale logger e il messaggio italiano nel canale UI.
  5. Verifica che nessun err.message tecnico arrivi direttamente a textContent o innerHTML.

Attività in classe

Questo codice confonde i due canali — trova i problemi:

document.getElementById("btn-chiudi").addEventListener("click", () => {
  try {
    chiudiPrestito({ prestitoId }, archivioPrestiti);
    alert("Successo");
  } catch (err) {
    // Problema 1: mostra err.message in UI
    document.getElementById("errore").textContent = err.message;
    // Problema 2: nessun log tecnico
  }
});

Riscrivi il codice separando i due canali, poi compila la scheda:

## Separazione log/messaggi

File modificati:

Log tecnici aggiunti (evento, contesto):

Messaggi utente aggiunti (testo italiano):

Verifica: err.message appare in UI? sì/no

Spiegazione guidata per studiare

I due canali hanno pubblici diversi e quindi formati diversi:

AspettoLog tecnicoMessaggio utente
DestinatarioSviluppatoreDocente/tecnico
Dove appareConsole del browserUI della pagina
ContenutoCodice errore, ID, stackFrase in italiano
ObiettivoDebugAzione da intraprendere

Il logger non deve essere complesso. Per il Registro scolastico, la versione mostrata sopra è sufficiente. Non serve un sistema di log esterno, non serve scrivere su file, non serve un database di log.

La funzione traduciErrore è un dizionario: prende un codice stringa ("STRUMENTO_NON_TROVATO") e restituisce una frase italiana. Il codice viene dall’errore lanciato dal caso d’uso o dall’infrastruttura, e la traduzione avviene solo nella UI — mai nel dominio.

Pattern completo:

Domain/Application → lancia Error("CODICE_ERRORE")
    ↓
Infrastructure/Logger → logga il dettaglio tecnico
    ↓
Presentation → cattura l'errore, logga nella console, mostra il messaggio tradotto in UI

Passo per passo nel progetto

  1. Crea src/infrastructure/logger.js con info, warn, error.
  2. Crea src/presentation/messaggi.js con mostraMessaggio e traduciErrore.
  3. Aggiungi al dizionario di traduciErrore tutti i codici errore che il Registro può lanciare.
  4. Apri ogni file in src/presentation/ e sostituisci le gestioni errori con il pattern: logger.error + mostraMessaggio(traduciErrore(...)).
  5. Verifica nella console e nella UI che i due canali funzionino separatamente.

Prova di comprensione

  • Perché err.message non dovrebbe apparire mai direttamente nell’UI?
  • Chi dovrebbe chiamare traduciErrore — il caso d’uso o la UI?
  • Cosa succede se aggiungi un nuovo tipo di errore ma dimentichi di aggiungerlo al dizionario?
  • Come si verifica che il log tecnico contiene informazioni sufficienti per il debug?

Errori da evitare

  • mostrare err.message o err.stack nella UI — incomprensibili per l’utente;
  • mettere messaggi in italiano nei casi d’uso — il dominio non deve sapere come parla la UI;
  • dimenticare il log tecnico — senza di esso, quando qualcosa va storto non hai informazioni per il debug;
  • usare alert() per i messaggi — non è separabile, non è accessibile, non è testabile.

Prodotto da consegnare

I file src/infrastructure/logger.js e src/presentation/messaggi.js funzionanti, con tutti i casi d’errore del Registro coperti dal dizionario di traduciErrore. Nessun err.message diretto in UI.

Checklist di chiusura

  • logger.js esiste in src/infrastructure/ con info, warn, error
  • messaggi.js esiste in src/presentation/ con mostraMessaggio e traduciErrore
  • tutti i codici errore del Registro sono nel dizionario
  • nessun handler della UI mostra err.message direttamente
  • ogni gestione errori usa entrambi i canali: logger.error e mostraMessaggio

Risultato atteso

Ho creato logger.js in src/infrastructure/ e messaggi.js in src/presentation/.
Il dizionario di traduciErrore copre tutti i codici errore del Registro.
Ogni handler della UI usa logger.error per il canale tecnico e mostraMessaggio per la UI.
Nella console vedo eventi con ID e dettagli tecnici.
Nella UI vedo frasi in italiano leggibili senza dettagli tecnici.