ASP.NET Core 5 Structured Logging con Azure Application Insights Come registrare i log di una tipica applicazione web in ASP.NET 5 Core in modo strutturato su Azure Application Insights

ASP.NET Core 5 Structured Logging con Azure Application Insights

Questo articolo è il quarto 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 primo strumento, analizzando i passaggi necessari per configurare la nostra app di esempio così da consentire la registrazione dei log in modalità strutturata tramite Azure Application Insights.

Prerequisiti

La prima cosa da fare è scaricare i seguenti pacchetti NuGet, che ci consentiranno di utilizzare l’Application Insights logging provider all’interno della nostra applicazione web:

  • Microsoft.Extensions.Logging.ApplicationInsights
  • Microsoft.ApplicationInsights.AspNetCore

ASP.NET Core 5 Structured Logging con Azure Application Insights

In realtà l’unico pacchetto davvero necessario è il primo: il secondo ci servirà per illustrare una modalità di configurazione alternativa nell’ultima parte di questo articolo.

Configurazione su Azure

Una volta installati i NuGet packages, possiamo collegarci a MS Azure per creare una nuova risorsa su Application Insights: la procedura di creazione è estremamente semplice e richiede pochissimi parametri: Gruppo di risorse, nome, regione e resource mode, che per semplicità andremo a impostare a classic in quanto non ci interessa gestire un’area di lavoro formata da molteplici end-point.

ASP.NET Core 5 Structured Logging con Azure Application InsightsInstrumentation Key

Una volta creata la risorsa su Azure Application Insights sarà possibile recuperare la Instrumentation Key, che ci consentirà di mettere in comunicazione l’Application Insights logging provider che configureremo all’interno della nostra app e la risorsa che abbiamo appena creato su MS Azure.

ASP.NET Core 5 Structured Logging con Azure Application Insights

Per recuperare la Instrumentation Key sarà sufficiente recarsi nella tab Overview della entry creata su Application Insights, come illustrato nella screenshot di cui sopra.

Configurazione su ASP.NET

Adesso possiamo finalmente aggiungere il nuovo logging provider all’elenco dei provider abilitati all’interno della configurazione del Generic Host della nostra applicazione.

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

Per far questo, apriamo il file Program.cs e modifichiamo il metodo  predefinito nel seguente modo:

Come si può vedere, oltre ad aggiungere il logging provider è possibile configurare dei filtri per determinare i LogLevel da registrare: anche in questo caso possiamo specificare una categoria specifica oppure un comportamento generale per tutte le categorie, in questo caso inserendo una stringa vuota nel primo parametro.

Un’alternativa preferibile rispetto a definire i suddetti filtri all’interno del codice sorgente è quella di impostarli all’interno del file appsettings.json, utilizzando la stessa tecnica che abbiamo visto quando abbiamo introdotto i logging provider predefiniti: vediamo come è possibile utilizzare questa tecnica per implementare gli stessi filtri di cui sopra.

Come possiamo vedere, è sufficiente aggiungere un elemento JSON definendo il nome del provider (in questo caso ApplicationInsights) e impostando i LogLevel che ci interessa registrare per le varie tipologie di evento: in questo caso abbiamo deciso di mantenere le impostazioni predefinite per la categoria default ed effettuare l’override solo per le categorie che iniziano con “Microsoft”, escludendo ancora una volta la gli eventi relativi al namespace Microsoft.Hosting.Lifetime per i quali è previsto un LogLevel di tipo Information.

Best-practice di configurazione

Impostare i parametri di configurazione dei logging provider a livello del file appsettings.json e non all’interno del codice sorgente è certamente una buona pratica, in quanto ci consente di impostare diverse tipologie di logging a seconda dell’environment, nonché di modificarle senza bisogno di ricompilare e quindi dover effettuare nuovamente il deploy della nostra app.

Del resto, quella di non inserire parametri di configurazione all’interno del codice sorgente è una best practice anche dal punto di vista della sicurezza, in quanto i consente di evitare che i dati riservati della nostra app – come ad esempio le chiavi di autorizzazione, le password dei database, eccetera – possano essere pubblicati, magari accidentalmente, in luoghi accessibili a terzi, ad esempio su un repository GitHub. Da questo punto di vista possiamo affermare che il codice sorgente che abbiamo pubblicato all’inizio del paragrafo precedente non è eccelso, in quanto la Instrumentation Key è presente in chiaro all’interno del codice sorgente della file Program.cs: questo significa che, qualora il nostro progetto venisse pubblicato su GitHub, chiunque avrebbe la possibilità “avvelenare”, inquinare i nostri log inserendo informazioni arbitrarie, magari relative a un’altra app…. Per non parlare del fatto che potrebbe persino trovare il modo di vedere i nostri.

Insomma, per quanto la nostra implementazione possa funzionare, è decisamente il caso di rivederla in modo da garantire un livello di sicurezza maggiore.

Environment Variable

Una possibilità, ad esempio, è quella di definire la Instrumentation Key a livello di variabile di ambiente, prima nelle proprietà del progetto su Visual Studio 2019:

ASP.NET Core 5 Structured Logging con Azure Application Insights

… e quindi all’interno del codice del file Program.cs, nel seguente modo:

AppSettings.json

Un’altra possibilità che abbiamo, e che ci sentiamo di raccomandare per una serie di ragioni di sicurezza e praticità, è quella di definire l’Instrumentation Key all’interno del file appsettings.json, ovvero utilizzando la medesima tecnica già utilizzata per gestire i LogLevel. In questo caso possiamo utilizzare un comodissimo overload del metodo ConfigureLogging che ci consente di avere a disposizione l’oggetto hostingContext, fondamentale per accedere all’istanza dell’interfaccia IConfiguration creata dal metodo CreateDefaultBuilder e che contiene i valori contenuti nei file appsettings da considerare per l’environment in esecuzione.

Seguendo questo approccio, il nostro file appsettings.json si presenterà grossomodo così:

Il valore della Instrumentation Key così definito potrà essere recuperato all’interno del file Program.cs nel seguente modo:

Ovviamente, se si opta per il metodo AppSettings, dovremo assicurarci di definire la chiave soltanto sul file che andremo a pubblicare sul server di produzione, altrimenti rischieremo anche in questo caso di farla finire su GitHub. Per quanto riguarda l’ambiente di sviluppo è possibile utilizzare la funzionalità User Secrets prevista da Visual Studio, che molti di voi probabilmente conosceranno: per chi non la conoscesse, ecco un breve riassunto.

Visual Studio User Secrets

A partire dalla versione 2017 Visual Studio mette a disposizione un file, denominato secrets.json, che può essere utilizzato per definire degli override puntuali sul file appsettings.json quando eseguiamo l’applicazione all’interno dell’ambiente di sviluppo.

Questo significa che possiamo inserire le nostre chiavi di autenticazione lì, lasciando il file appsettings.json vuoto, o magari con dei placeholder che possono aiutare a ricordare a noi o ad eventuali altri sviluppatori che per poter eseguire la app devono recuperare quelle credenziali e definirle a loro volta all’interno del file secrets.json. Ovviamente il file secrets.json non fa parte del progetto e viene memorizzato in una posizione sicura all’interno della nostra cartella utente sul PC di sviluppo locale, e non si correrà quindi il rischio di pubblicarlo all’interno di qualche repository. Il percorso del file secrets.json viene memorizzato all’interno del file di progetto nel momento in cui il file viene richiesto la prima volta: la cartella predefinita è rappresentata da un GUID, cosa che garantisce la generazione di un file univoco per ogni progetto.

Ovviamente, è anche possibile impostare un percorso personalizzato, cosa che consente di utilizzare lo stesso file secrets.json per una pluralità di progetti: può essere comodo in alcuni casi molto limitati quando abbiamo una pluralità di siti, o di librerie, che utilizzano le stesse identiche connection string, ma in generale non si tratta di una tecnica che mi sento di consigliare a meno che l’intero team di sviluppo non abbia un livello di consapevolezza molto elevato su questi aspetti specifici, in quanto il rischio di errore umano inevitabilmente aumenta visto che è generalmente considerato un file specifico per progetto.

Una volta che ci si prende la mano, il meccanismo che prevede la combinazione virtuosa dei file appsettings e secrets.json è molto versatile e garantisce un buon livello di sicurezza, quindi personalmente è un approccio che mi sento di raccomandare. E’ anche molto utile per fare tutorial come questo, in quanto mi consente di mostrare dei placeholder nei file appsettings, che verranno poi sovrascritti dalle chiavi vere e proprie senza doverle mostrare a schermo.

Per ulteriori informazioni sulla funzionalità User Secrets di Visual Studio consigliamo la lettura di questo articolo.

Telemetry packages

Come abbiamo visto nei paragrafi precedenti, sia la tecnica che prevede l’inserimento della InstrumentationKey all’interno di una Environment Variable che quella che prevede l’utilizzo dei file appsettings.json e/o secrets.json necessitano di una indicazione specifica, all’interno del codice del file Program.cs, del nome della variabile (o dell’elemento JSON) da recuperare.

In alternativa a questo approccio “dichiarativo” è possibile recuperare il valore della Instrumentation Key utilizzando il pacchetto NuGet Microsoft.ApplicationInsights.AspNetCore, che contiene un set di servizi appositi che è possibile aggiungere all’interno della pipeline della nostra applicazione.

Una volta installato il pacchetto, è possibile aggiungere questi servizi utilizzando il metodo AddApplicationInsightsTelemetry() all’interno del metodo ConfigureServices della classe Startup della nostra applicazione (file Startup.cs) nel seguente modo:

Il vantaggio di questo approccio è che i servizi così configurati sono in grado di leggere automaticamente sia le variabili di ambiente che i valori all’interno dei file appsetting.json e secrets.json, sollevandoci quindi dall’onere di specificarli in modo esplicito come parametri, come abbiamo dovuto fare fino ad ora… a patto ovviamente di rispettare le convenzioni di naming previste, ovvero quelle che abbiamo utilizzato nei paragrafi precedenti e che per comodità ricordiamo adesso:

  • Il nome da utilizzare per la Variabile di Ambiente (Environment Variable) è APPINSIGHTS_INSTRUMENTATIONKEY
  • Il nome del percorso JSON all’interno dei file appsettings.json e/o secrets.json è ApplicationInsights:InstrumentationKey

Conclusioni

Per il momento è tutto: nel prossimo articolo vedremo come è possibile effettuare la registrazione degli application log in modalità strutturata su un DBMS, ovvero su un database SQL Server o MariaDB su un’istanza locale o remota a noi accessibile, utilizzando la libreria open-source Serilog.

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.