LOGGING IN ASP.NET CORE MVC
USARE IL SERVIZIO DI LOGGING
Il servizio di logging che come al solito riceviamo nel costruttore di un nostro componente, ci consente di scrivere una riga di testo con dei parametri quando nella nostra applicazione si verificano degli eventi che reputiamo interessanti. Siamo noi a decidere cosa dove e quando scrivere in questo log, informazioni utili quando l’applicazione verrà messa nel server di produzione. Il servizio di Logging si affida a dei provider per emettere le righe scritte nel log, se il provider supporta il Logging strutturato possiamo filtrare le righe in base ai segnaposti.
Vediamo un’altra classe la ILoggerFactory. Il metodo CreateLogger crea sempre un ILogger<T>.
Questi sono i vari livelli di Logging.
Se impostiamo nel file appsettings.json il livello su Warning verranno loggati solo quei tipi di messaggio.
Se adesso imposto su Production l’ambiente i due messaggi di Log di tipo informativo messi nel codice non dovrei vederli, mentre impostando l’ambiente su Development li vedo perché ho questa configurazione nel file appsettings.Development.json.
CATTURARE ECCEZIONI NON GESTITE E VISUALIZZARE MESSAGGI DI ERRORE PERTINENTI
Prima o poi la nostra applicazione andrà in errore, vuoi perché il database non è raggiungibile, oppure siamo stati noi a introdurre un bug che non abbiamo scoperto o ancora l’utente ha fornito un input errato. In tutte queste situazioni potrebbe verificarsi una cosiddetta eccezione non gestita, cioè un errore che non viene catturato dal nostro codice in un blocco try catch e l’utente si vedrebbe arrivare una pagina di errore piena di tecnicismi non sapendo se ha sbagliato lui o la nostra applicazione. Possiamo evitare ciò configurando un Middleware nella classe Startup.cs.
Se siamo in Production usiamo un nuovo Middleware.
Supponiamo che l’utente abbia sbagliato l’URL e arrivi una richiesta /Courses/Detail/5000. La richiesta percorre tutti i Middleware fino a che non viene sollevata l’eccezione. A questo punto ASP.NET Core anziché far arrivare una pagina vuota con un generico errore http 500 fa una seconda richiesta, questo volta indicando nell’URL /Error come da noi configurato e aggiungendo informazioni dettagliate sull’errore. Richiesta che questa volta arriva all’utente. Con /Error indichiamo un Controller con la sua Action Index.
RIEPILOGO DEL SERVIZIO DI LOGGING
Anche se la nostra applicazione è stata adeguatamente testata, quando l’andiamo a mettere in produzione potranno comunque verificarsi delle situazioni impreviste. Ad esempio, un bug non ancora scoperto o dei problemi transitori di connessione al database impediranno all’applicazione di funzionare normalmente.
È importante tenere una traccia di questi accadimenti e di ciò che si è verificato nei momenti immediatamente precedenti, così che possiamo diagnosticare il problema più facilmente e trovare delle contromisure per evitare che si verifichi di nuovo in futuro.
Dunque, anche dopo che lo sviluppo di un’applicazione è stato “completato”, abbiamo comunque il dovere di studiare il suo comportamento, identificare le criticità e ridurre al minimo i disservizi causati ai nostri utenti.
Il servizio di logging ILogger ci serve appunto per lasciare una traccia di eventi che reputiamo importanti nella nostra applicazione. Come al solito, riceviamo il servizio nel costruttore di un nostro componente, come il servizio applicativo AdoNetCourseService.
- public class AdoNetCourseService : ICourseService
- {
- private readonly ILogger<AdoNetCourseService> logger;
- //Ricevo il servizio dal costruttore
- public AdoNetCourseService(ILogger<AdoNetCourseService> logger)
- {
- //Conservo il riferimento al servizio su un campo privato…
- this.logger = logger;
- }
- public async Task GetCoursesAsync()
- {
- //…poi scrivo un messaggio nel log per tracciare il fatto
- //che un utente ha richiesto l’elenco dei corsi.
- //Il messaggio verrà visualizzato in console.
- logger.LogInformation(“Course list requested”);
- //TODO: ottengo l’elenco dei corsi
- }
- }
Nella seguente immagine possiamo vedere che il nome AdoNetCourseService che abbiamo indicato come parametro di tipo T del servizio ILogger sarà usato come categoria, ovvero come origine del messaggio di log. In questo modo riusciamo a determinare meglio la provenienza di ciascun messaggio.
In alternativa, se vogliamo essere noi stessi a decidere arbitrariamente il nome della categoria, possiamo usare il servizio ILoggerFactory.
- public class AdoNetCourseService : ICourseService
- {
- private readonly ILogger logger;
- //Ricevo il servizio dal costruttore
- public AdoNetCourseService(ILoggerFactory loggerFactory)
- {
- //Creo un ILogger con una categoria dal nome arbitrario…
- this.logger = loggerFactory.CreateLogger(“Corsi”);
- }
- public async Task GetCoursesAsync()
- {
- //…poi, come al solito, scrivo un messaggio nel log
- logger.LogInformation(“Course list requested”);
- //TODO: ottengo l’elenco dei corsi
- }
- }
LIVELLI DI CRITICITA’ DEI LOG
Il servizio di logging ci mette a disposizione vari metodi, ciascuno usato per scrivere messaggi a vari livelli di criticità. Eccoli ordinati dal più importante al meno importante:
- LogCritical: usato per tenere traccia di eventi che compromettono il corretto funzionamento dell’intera applicazione per tutti gli utenti, come ad esempio l’impossibilità di scrivere su disco;
- LogError: per errori che riguardano una situazione ben precisa, come ad esempio l’impossibilità di aggiornare uno specifico corso;
- LogWarning: per situazioni di errore che non necessariamente dipendono dalla nostra applicazione, come per esempio l’apertura di un corso che non è mai esistito nel database;
- LogInformation: per messaggi che confermano la corretta esecuzione di un’operazione, come ad esempio l’acquisto di un corso da parte di uno studente;
- LogDebug: per messaggi che ci aiutano a diagnosticare un problema in produzione quando non riusciamo a identificarlo con altri mezzi. Usiamo questo metodo per tenere traccia di query SQL, numero di righe estratte dal database, e così via;
- LogTrace: per tenere traccia di ogni singolo passo operativo compiuto dalla nostra applicazione. Può essere utile per avere una trascrizione del comportamento interno di un metodo a scopo di verifica della sua conformità alla specifica.
Usare bene i livelli è importante perché i messaggi di log vengono scritti in console, che è una risorsa contesa dai vari thread della nostra applicazione. Di conseguenza, può anche rappresentare un collo di bottiglia e deteriorare le prestazioni, soprattutto quando siamo in produzione e abbiamo tanti utenti contemporanei.
Perciò è importante che in produzione vengano scritti nel log solo i messaggi con un alto livello di criticità, come ad esempio da Warning in su. Perciò, apriamo il file appsettings.json e impostiamo questa configurazione.
- {
- “Logging”: {
- “LogLevel”: {
- “Default”: “Warning”
- }
- }
- }
Mentre invece, finché siamo in sviluppo, abbiamo tutta la libertà di scrivere nel log anche i messaggi meno importanti. Quindi apriamo il file appsettings.Development.json e aggiungiamo questa configurazione per ridefinire il livello per l’ambiente Development.
- {
- “Logging”: {
- “LogLevel”: {
- “Default”: “Information”
- }
- }
- }
LOGGING STRUTTURATO
Grazie al servizio di logging possiamo scrivere messaggi strutturati, che cioè tengono separato il testo dai propri valori. Nel seguente esempio, il messaggio definisce due segnaposto chiamati {query} e {error} che verranno valorizzati con i successivi due parametri forniti al metodo LogError.
- try
- {
- DataSet dataSet = await db.QueryAsync(query);
- //…
- }
- catch (Exception exc)
- {
- logger.LogError(“Error while executing query {query}: {error}”, query, exc.Message);
- }
Questa funzionalità è utile soprattutto se si usano provider di logging di terze parti come Serilog che ci permettono di tenere ben separati testi e valori, in modo che poi i nostri log possano addirittura essere esaminati con strumenti di analisi e reportistica.
LINK AL CODICE SU GITHUB
Scaricare il codice della sezione12 o clonare il repository GITHUB per avere a disposizione tutte le sezioni nel tuo editor preferito.
Scrivi un commento