ASP.NET Core 5 Structured Logging con Serilog Come registrare i log di una tipica applicazione web in ASP.NET 5 Core in modo strutturato su SQL Server, MariaDB o altri DBMS con Serilog

ASP.NET Core 5 Structured Logging con Azure Application Insights

Questo articolo è il quinto e ultimo di una serie di approfondimenti che illustrano come implementare un meccanismo di structured logging (registrazione strutturata degli eventi) all’interno di una tipica applicazione web realizzata con ASP.NET Core 5 e C# facendo uso di due strumenti che sfruttano le API fornite dall’interfaccia ILogger presente nel namespace Microsoft.Extensions.Logging:

  • Azure Application Insights, il servizio di monitoraggio fornito da MS Azure.
  • Serilog, una libreria open-source di registrazione diagnostica per applicazioni .NET disponibile su NuGet che consente di memorizzare i suddetti log sui database management system più diffusi, tra cui SQL Server e MariaDB.

In questo approfondimento ci occuperemo del secondo strumento, analizzando i passaggi necessari per configurare la nostra app di esempio così da consentire la registrazione dei log in modalità strutturata su un database SQL Server o MariaDB tramite Serilog.

Serilog: Panoramica

La libreria Serilog, disponibile su NuGet, è un progetto open-source noto con l’obiettivo di fornire un logging provider centralizzato che consenta di gestire una pluralità di formati attraverso una interfaccia comune: ciascun formato di logging è gestito da un handler specifico, che nella libreria è chiamato Sink, installabile e configurabile all’interno della nostra applicazione come un modulo aggiuntivo.

Ad oggi, Serilog può vantare l’esistenza di oltre 50 Sink che consentono di gestire altrettanti formati (strutturati e non), tra cui i principali DBMS disponibili sul mercato: SQL Server, MariaDB, PostreSQL e così via. Questa abbondanza di moduli consente di superare una delle poche limitazioni connesse all’utilizzo dell’interfaccia ILogger fornita da Microsoft, ovvero l’assenza di logging provider nativi che consentano di gestire database relazionali… a patto, ovviamente, di essere disposti a utilizzare un componente esterno, ovvero un logging provider di terze parti.

Vantaggi e svantaggi dei Sink

Come detto, il principal e vantaggio di Serilog è dato dal fatto che, con un’unica interfaccia, avremo modo di gestire una pluralità di formati grazie alla presenza di un gran numero di Sink: alcuni dei Sink disponibili sono stati realizzati direttamente dal team di programmatori che ha creato la libreria, mentre altri sono opera della community di developer che contribuisce allo sviluppo del progetto.

Questo approccio variegato alla realizzazione dei Sink ha consentito in breve tempo di rendere disponibile l’utilizzo di un gran numero di formati, tra cui la maggior parte dei DBMS e dei servizi di monitoring in circolazione; ha però anche comportato alcune controindicazioni, tra cui il fatto che ciascun Sink espone metodi, funzioni e opzioni di configurazione leggermente diverse: non di rado, queste differenze non sono dovute unicamente alle caratteristiche intrinseche del formato supportato, ma anche alla capacità dell’autore di attenersi in modo più o meno rigoroso alle interfacce di configurazione, naming conventions, best practices e standard della libreria.

Nonostante la concreta possibilità di doversi cimentare con queste innegabili situazioni, che potranno rendere più o meno macchinosa la configurazione di molteplici Sink specialmente per lo sviluppatore alle prime armi, sia l’approccio che la metodologia alla base di Serilog restano comunque estremamente vantaggiose, in quanto consentono di centralizzare alcuni importanti aspetti di configurazione e, di conseguenza, gestire tutti gli aspetti dell’application logging in modo relativamente ordinato.

Installazione

Vediamo ora come è possibile installare e configurare Serilog su una tipica applicazione web ASP.NET Cor 5. Ancora una volta utilizzeremo un progetto standard, creata sulla base del solito template di Visual Studio che abbiamo utilizzato fino ad ora: ASP.NET 5 Web Application con front-end Angular.

Il codice che andremo a commentare in questo paragrafo e nei successivi può essere scaricato da GitHub a questo indirizzo.

Questa volta andremo a installare i seguenti pacchetti NuGet

  • Serilog
  • Serilog.AspNetCore
  • Serilog.Settings.Configuration
  • Serilog.Sinks.MSSqlServer
  • Serilog.Sinks.MariaDB

Il package Serilog è relativo al logging provider vero e proprio; il package Serilog.AspNetCore contiene le funzionalità che servono per instradare i messaggi di log ASP.NET Core tramite Serilog, sostituendo quindi l’implementazione predefinita con quella di Serilog. Questo aspetto è molto importante, perché consente a Serilog di gestire i logging provider forniti nativamente dal framework in tre possibili modi:

  • prendere il loro posto, occupandosi di generare i log nei formati non strutturati gestiti dai suddetti provider (Console, Debug, EventSource, EventLog, etc.) tramite i propri Sink corrispondenti;
  • disabilitarli, evitando quindi la generazione dei log non strutturati di cui sopra;
  • mantenerli attivi, alimentandoli internamente (agendo come un proxy) insieme ai propri Sink.

Il package Serilog.Settings.Configuration è il modulo che consente a Serilog di leggere i file di configurazione, ovvero i file appsettings.json e secrets.json; come abbiamo visto nel precedente approfondimento dedicato ad Application Insights si tratta di una funzionalità molto importante in termini di sicurezza, in quanto consente allo sviluppatore di evitare di inserire i parametri di configurazione (tra cui, ad esempio, le credenziali di accesso al Database) all’interno del codice sorgente.

Gli ultimi due package sono i due Sink che andremo a utilizzare, ovvero SQL Server e MariaDB. Si tratta ovviamente di due Sink esemplificativi, scelti tra gli oltre 50 disponibili su NuGet che coprono la quasi totalità dei database relazionali e non relazionali oggi più diffusi.

Configurazione

Una volta installati i pacchetti NuGet possiamo cominciare con la configurazione dei Sink: cominciamo con quello relativo a SQL Server.

Program.cs

Apriamo il file Program.cs e modifichiamo il metodo CreateHostBuilder aggiungendo una chiamata a UseSerilog(), attraverso la quale andremo a configurare il nuovo logging provider.

Come si può vedere, in questa fase ci limitiamo a dire che la configurazione va letta direttamente dal file appsettings.json, ancora una volta utilizzando un overload che ci mette a disposizione il parametro hostingContext e quindi l’istanza di IConfiguration che contiene le relative impostazioni.

Startup.cs

Prima di procedere alla configurazione all’interno dei file appsettings.json dobbiamo aggiungere un’altra cosa: le funzionalità di HTTP request logging, che vengono gestite tramite un middleware apposito che fa parte del pacchetto Serilog.AspNetCore. E’ opportuno abilitarle, altrimenti Serilog non registrerà gli eventi relativi alle connessioni HTTP in ingresso e quindi le URL visitate dagli utenti, il metodo, lo status code di ritorno, il tempo di risposta, e così via.

Per inserire il middleware nella pipeline dell’applicazione dobbiamo aprire il file Startup.cs e inserire la seguente riga di codice all’interno del metodo Configure:

AppSettings.json

Adesso possiamo recarci all’interno del file appsettings.json, dove possiamo vedere i parametri di configurazione dei due Sink che abbiamo deciso di utilizzare.

Come si può vedere c’è qualche piccola differenza nel naming delle varie opzioni di configurazione tra SQL Server e MariaDB; tali differenze, come abbiamo spiegato in precedenza, sono dovute sia al fatto che i relativi Sink sono stati sviluppati in momenti diversi e da team di sviluppo diversi, sia al fatto che ogni DBMS ha le sue caratteristiche peculiari.

In questo esempio, per ragioni di tempo, non ci siamo voluti dilungare sui dettagli di configurazione relativi a questi due Sink, limitandoci a specificare i parametri fondamentali: la connection string, il nome della tabella DB dove registrare gli application log, e l’autorizzazione a creare automaticamente tale tabella qualora non esista già all’interno del Database. Per tutto il resto abbiamo lasciato valide le impostazioni predefinite, che ovviamente possono essere modificate per ciascun Sink in vario modo, con diverse decine di opzioni che consentono di stabilire:

  • il numero di log che vengono scritti a ogni batch;
  • l’intervallo di scrittura;
  • la retention dei log, ovvero il numero di giorni/mesi/anni da mantenere prima di effettuare un pruning dei log più vecchi con delle istruzioni DELETE;
  • il nome dei campi della tabella da utilizzare;
  • e così via.

Aggiunta di Log personalizzati

Prima di concludere questo articolo è bene spendere due parole su una funzionalità molto importante dell’interfaccia ILogger, con tutta probabilità già nota alla maggior parte degli sviluppatori senior ma molto importante per chi si avvicina per la prima volta a questi argomenti: la possibilità di inserire log personalizzati all’interno del codice, che saranno poi registrati da tutti i logging provider che andremo a configurare secondo le modalità che abbiamo visto finora.

Se apriamo il file WeatherForecastController.cs dell’applicazione di esempio disponibile su GitHub possiamo vedere un paio di log informativi che abbiamo aggiunto a titolo di esempio, e che saranno registrati nel momento in cui andremo ad eseguire la pagina che interroga questa API tramite HTTP request.

Come si può vedere, per poter utilizzare l’interfaccia ILogger è sufficiente definire una variabile apposita ( _ilogger ) e istanziarla tramite Dependency Injection all’interno del costruttore del controller stesso, seguendo il tipico development pattern dei controller ASP.NET Core. Una volta definita in questo modo, tale variabile potrà essere utilizzata all’interno di ciascun Action Method del controller per effettuare le registrazioni di nostro interesse tramite i metodi relativi al LogLevel di volta in volta desiderato: LogInformation, LogWarning, LogError, e così via.

Se apriamo il file TestController.cs possiamo vedere un ulteriore esempio:

La tecnica utilizzata è la medesima del precedente controller, ma stavolta abbiamo a che fare con un’eccezione preceduta da un log informativo personalizzato: l’Action Method in questione, denominato Get, genererà dunque due log entry distinte e consecutive, aventi rispettivamente LogLevel.Information e LogLevel.Error.

Conclusioni

Siamo giunti al termine della nostra lunga panoramica sulla strutturata degli eventi su ASP.NET Core: ci auguriamo che gli approfondimenti siano stati piacevoli e che possano aiutare altri sviluppatori a implementare modalità efficaci di logging per i propri applicativi. Per ulteriori approfondimenti vi invitiamo a visitare il relativo repository su GitHub.

Alla prossima!

Articoli correlati

  1. Indice degli argomenti
  2. ASP.NET Core 5 Application Logging: concetti di base
  3. Application Logging in .NET 5: Logging Provider predefiniti
  4. Differenze tra Log strutturati e non strutturati
  5. Structured Logging con Azure Application Insights
  6. Structured Logging su DBMS con Serilog
Vuoi saperne di più sullo sviluppo di applicationi web in tecnologia ASP.NET Core 5? Scrivici per comunicarci le tue esigenze o richiedi un preventivo gratuito e senza impegno per realizzare il tuo progetto!
Fork me on GitHub

 

About Ryan

IT Project Manager, Web Interface Architect e Lead Developer di numerosi siti e servizi web ad alto traffico in Italia e in Europa. Dal 2010 si occupa anche della progettazione di App e giochi per dispositivi Android, iOS e Mobile Phone per conto di numerose società italiane. Microsoft MVP for Development Technologies dal 2018.

View all posts by Ryan

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *

Questo sito usa Akismet per ridurre lo spam. Scopri come i tuoi dati vengono elaborati.