1. Introduzione
Se il logging testuale normale è il tuo diario con note "Ho mangiato oggi", il logging strutturato trasforma ogni voce in una scheda con campi: {Data: ..., Evento: "Ho mangiato", Calorie: 500, Piatto: "Carne fritta"}. Questo significa che dopo puoi non solo leggere il diario, ma anche costruire un grafico delle calorie per il mese, filtrare eventi per piatto e scoprire quando hai mangiato troppo tardi.
Perché il semplice testo non basta?
Con il testo semplice tutto è facile... finché dura. Prova a raccogliere statistiche sugli errori nei log, sui volumi di vendita in un negozio online, la sequenza di azioni di un singolo utente per il suo ID (per esempio, 42), se tutti i dati sono montagne di testo. Il logging strutturato permette di collegare analisi e persino AI ai log. Con esso puoi cercare anomalie, costruire dashboard e reagire automaticamente ai problemi.
Vantaggi
- Permette di loggare non solo messaggi, ma anche dati correlati (campi/proprietà).
- I log possono essere analizzati automaticamente: conteggi, filtri, report.
- Si usano formati standardizzati, come JSON, facili da parsare dalle macchine.
Serilog: cos'è e perché usarlo?
Serilog (sito ufficiale: serilog.net, documentazione: github.com/serilog/serilog/wiki) è una libreria popolare per il logging strutturato in .NET. Si integra molto bene con Microsoft.Extensions.Logging, supporta output verso decine di sistemi (file, console, Seq, ElasticSearch, Grafana, Azure ecc.), impatta minimamente sulle performance ed è semplice da configurare.
In cosa Serilog è diverso dal "semplice logging"?
- Struttura: i log sono oggetti con campi, filtrabili (per esempio, tutti gli errori dell'utente con ID 42).
- Formati: sa scrivere non solo testo, ma anche JSON, XML, comodo per l'elaborazione successiva.
- Flessibilità: molti pacchetti-sink pronti per inviare i log ovunque.
Struttura di una voce di log con Serilog
Diamo un'occhiata a un log strutturato prima di scrivere codice.
{
"Timestamp": "2024-06-22T10:23:45.123Z",
"Level": "Information",
"MessageTemplate": "Utente {UserId} ha effettuato il login",
"Properties": {
"UserId": 42,
"IpAddress": "127.0.0.1"
}
}
Qui anche l'analisi meno sofisticata capisce: si parla dell'utente n.42 e del suo indirizzo IP.
2. Installazione e configurazione base di Serilog in un progetto C#
Passo 1. Installare i pacchetti NuGet
In Rider/Visual Studio tramite il NuGet Package Manager installa:
- Serilog
- Serilog.Sinks.Console (output su console)
- Serilog.Extensions.Logging (per integrare con Microsoft.Extensions.Logging)
Da riga di comando:
dotnet add package Serilog
dotnet add package Serilog.Sinks.Console
Passo 2. Configurazione minima
Aggiungiamo la configurazione in Program.cs e scriviamo il primo log.
using System;
using Serilog;
namespace MySuperApp
{
class Program
{
static void Main(string[] args)
{
// 1. Configurazione di base di Serilog: output su console
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.Console()
.CreateLogger();
// 2. Esempio di logging strutturato
int userId = 42;
string ip = "127.0.0.1";
Log.Information("Utente {UserId} ha effettuato il login dall'IP {IpAddress}", userId, ip);
Log.CloseAndFlush();
}
}
}
In console vedremo più o meno così:
[10:30:16 INF] Utente 42 ha effettuato il login dall'IP 127.0.0.1
Dopo è facile mandare l'output su un file JSON, su Seq o altro sistema.
3. Formattazione dei log: Message Template
In Serilog invece di concatenare stringhe si usa la sintassi dei template:
Log.Information("Operazione {Operation} sul file {FileName}", "cancellazione", "test.txt");
Non è solo una bella scrittura — è logging strutturato: nel log finiranno i campi Operation e FileName, disponibili per filtrare e aggregare.
In cosa differisce da string.Format?
string.Format("Operazione {0} sul file {1}", operation, fileName) concatena semplicemente la stringa con i placeholder {0}, {1}. Serilog invece crea campi separati con cui poi si può lavorare analiticamente.
Configurazione flessibile: livelli, filtri, diversi "sink"
Serilog può scrivere i log contemporaneamente in più destinazioni.
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.Console()
.WriteTo.File("log.txt", rollingInterval: RollingInterval.Day)
.CreateLogger();
Ora i log vengono scritti sia in console che in file con rotazione giornaliera.
4. Esempio
Immaginiamo di creare una console app "notes" che permette di creare note utente. Aggiungiamo il logging strutturato delle azioni.
using System;
using Serilog;
namespace NotesApp
{
class Program
{
static void Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information()
.WriteTo.Console()
.WriteTo.File("notes-log.json", rollingInterval: RollingInterval.Day,
formatter: new Serilog.Formatting.Json.JsonFormatter())
.CreateLogger();
Console.WriteLine("Inserisci il nome utente:");
string userName = Console.ReadLine();
Log.Information("Utente {UserName} ha avviato l'app NotesApp", userName);
while (true)
{
Console.WriteLine("Inserisci il testo della nota (o scrivi 'uscita'):");
string note = Console.ReadLine();
if (note == "uscita")
{
Log.Information("Utente {UserName} ha terminato l'esecuzione", userName);
break;
}
Log.Information("Utente {UserName} ha creato una nota: {NoteText}", userName, note);
}
Log.CloseAndFlush();
}
}
}
Commento:
Il log cattura chi avvia il programma, cosa inserisce e quando termina. Nel file notes-log.json ogni voce è un oggetto JSON, facile da analizzare.
5. Note utili
Migliori pratiche per il logging strutturato
- Non abusare dei livelli Debug/Trace in produzione — usa Information e Warning con giudizio.
- Usa parametri nominati nei template invece della concatenazione di stringhe.
- Non loggare mai dati sensibili (password, token, chiavi).
- Logga eventi di business importanti, non solo errori ed eccezioni.
- Configura rotazione e pulizia dei log per non esaurire lo spazio su disco.
Visualizzazione e analisi: Seq, Kibana, Application Insights
Serilog supporta molti sink — endpoint finali dove inviare i log.
| Sink | Breve descrizione | Dove si usa |
|---|---|---|
| Console | Direttamente in console | Sviluppo, test |
| File | Su file locale o di rete | Progetti piccoli, dev |
| Seq | Interfaccia web con filtri e dashboard | Enterprise, analisi |
| ElasticSearch | Potente sistema di storage e analisi | Grandi aziende |
| Azure Application Insights | Monitoraggio cloud e telemetry | Servizi con carico su Azure |
Seq (datalust.co/seq) è una soluzione molto popolare per sviluppo e uso interno: filtraggio comodo, ricerca per campi e deployment rapido.
Tabelle e visualizzazione
Qui sotto una tabella sintetica di cosa si può loggare in modo strutturato:
| Cosa logghiamo | Come appare in Serilog | Esempio di valore |
|---|---|---|
| ID utente | |
123 |
| Azione | |
"Cancellazione" |
| Errore | |
"Modulo di registrazione" |
| Tempo operazione | {Elapsed:0.000} sec | 1.234 |
| Nome file | |
"report.pdf" |
Trucchi interessanti e funzionalità aggiuntive di Serilog
- Enrichers: aggiungono a ogni log proprietà (per esempio, .Enrich.WithMachineName()).
- Log correlati: aggiungi RequestId per correlare la catena di eventi.
- Configurazione via appsettings.json: comoda per l'ambiente di produzione.
{
"Serilog": {
"MinimumLevel": "Debug",
"WriteTo": [
{ "Name": "Console" },
{ "Name": "File", "Args": { "path": "log.txt" } }
]
}
}
Advanced sinks: è possibile inviare i log su Slack, Telegram, via email (ma attenzione a non ricevere migliaia di mail per ogni errore).
6. Parte pratica: integrazione con Microsoft.Extensions.Logging
In .NET si usa spesso l'interfaccia standard ILogger per non legarsi a una libreria specifica. Serilog può essere collegato come provider.
Passo 1. Installare il pacchetto
dotnet add package Serilog.Extensions.Logging
Passo 2. Configurare
using Microsoft.Extensions.Logging;
using Serilog;
// ...
// Configuriamo Serilog, come al solito:
Log.Logger = new LoggerConfiguration()
.WriteTo.Console()
.CreateLogger();
// Ora usiamo Microsoft.Extensions.Logging
var loggerFactory = LoggerFactory.Create(builder =>
{
builder.AddSerilog();
});
ILogger<Program> logger = loggerFactory.CreateLogger<Program>();
logger.LogInformation("Messaggio di test: {TestValue}", 123);
// Non dimenticare di chiudere:
Log.CloseAndFlush();
Commento:
Ora tutto il tuo codice che usa ILogger<T> è indipendente dal provider — se vuoi puoi passare a NLog o Log4Net.
7. Errori tipici quando si lavora con Serilog
Overlogging: se scrivi tutto, sarà difficile trovare le informazioni utili.
Loggare eccezioni come testo: usa l'overload con l'eccezione — così l'errore entra nel log in forma strutturata.
try
{
// some code
}
catch (Exception ex)
{
Log.Error(ex, "Si è verificato un errore durante l'esecuzione della richiesta");
}
Abuso della configurazione: non trasformare la configurazione in un ammasso — aggiungi solo i sink e i livelli necessari.
GO TO FULL VERSION