Registrazione e analisi nelle applicazioni .NET

Completato

Man mano che si continua a sviluppare l'applicazione e l'applicazione diventa più complessa, è opportuno applicare all'applicazione ulteriore diagnostica di debug.

La traccia è un modo per monitorare l'esecuzione dell'applicazione mentre è in esecuzione. È possibile aggiungere la strumentazione di traccia e debug all'applicazione .NET quando la si sviluppa. È possibile usare tale strumentazione durante lo sviluppo dell'applicazione e dopo la distribuzione della stessa.

Questa semplice tecnica è sorprendentemente potente. Può essere usata nei casi in cui è necessario più di un debugger:

  • Per i problemi che si verificano in lunghi periodi di tempo, può essere difficile eseguire il debug con un debugger tradizionale. I log consentono di effettuare revisioni finali dettagliate per lunghi periodi di tempo. Al contrario, i debugger sono limitati all'analisi in tempo reale.
  • Il debug delle applicazioni multithreading e delle applicazioni distribuite risulta spesso difficile da eseguire. Il collegamento di un debugger tende a modificare i comportamenti. È possibile analizzare log dettagliati in base alle esigenze per comprendere sistemi complessi.
  • I problemi nelle applicazioni distribuite possono derivare da un'interazione complessa tra molti componenti. Potrebbe non essere opportuno connettere un debugger a ogni parte del sistema.
  • Molti servizi non devono essere bloccati. Il collegamento di un debugger causa spesso errori di timeout.
  • I problemi non vengono sempre previsti. La registrazione e l'analisi sono progettate per un sovraccarico ridotto, in modo che i programmi possano sempre continuare la registrazione in caso di problemi.

Scrivere informazioni nelle finestre di output

Fino a questo punto è stata usata la console per visualizzare le informazioni all'utente dell'applicazione. Esistono altri tipi di applicazioni compilate con .NET che hanno interfacce utente come app per dispositivi mobili, Web e desktop e in cui non è presente alcuna console visibile. In queste applicazioni System.Console registra i messaggi "dietro le quinte". Questi messaggi possono essere visualizzati in una finestra di output in Visual Studio o Visual Studio Code. Possono anche essere restituiti in un registro di sistema, ad esempio logcat di Android. Di conseguenza, è necessario fare molta attenzione quando si usa System.Console.WriteLine in un'applicazione non console.

In questo caso è possibile usare System.Diagnostics.Debug e System.Diagnostics.Trace in aggiunta a System.Console. Sia Debug che Trace fanno parte di System.Diagnostics e scriveranno solo nei log quando viene collegato un listener appropriato.

La scelta dell'API dello stile di stampa da usare dipende dall'utente. Le differenze principali sono le seguenti:

  • System.Console
    • Sempre abilitata e scrive sempre nella console.
    • Utile per le informazioni che il cliente potrebbe dover visualizzare nella versione.
    • Poiché è l'approccio più semplice, viene spesso usato per il debug temporaneo ad hoc. Questo codice di debug spesso non viene mai archiviato nel controllo del codice sorgente.
  • System.Diagnostics.Trace
    • Abilitata solo quando viene definito TRACE.
    • Scrive nei listener collegati, per impostazione predefinita DefaultTraceListener.
    • Usare questa API quando si creano log che verranno abilitati nella maggior parte delle build.
  • System.Diagnostics.Debug
    • Abilitata solo quando viene definito DEBUG (in modalità di debug).
    • Scrive in un debugger collegato.
    • Usare questa API quando si creano log che verranno abilitati solo nelle build di debug.
Console.WriteLine("This message is readable by the end user.");
Trace.WriteLine("This is a trace message when tracing the app.");
Debug.WriteLine("This is a debug message just for developers.");

Quando si progetta la strategia di traccia e debug, è opportuno considerare l'aspetto dell'output. Più istruzioni di scrittura compilate con informazioni non correlate creano un log difficile da leggere. D'altra parte, usando WriteLine per inserire istruzioni correlate in righe separate, potrebbe essere difficile distinguere quali informazioni sono correlate. In generale, utilizzare più istruzioni Write quando si desidera combinare informazioni provenienti da più origini per creare un singolo messaggio informativo. Usare l’istruzione WriteLine quando si intende creare un unico messaggio completo.

Debug.Write("Debug - ");
Debug.WriteLine("This is a full line.");
Debug.WriteLine("This is another full line.");

Questo output viene restituito dalla registrazione precedente con Debug:

Debug - This is a full line.
This is another full line.

Definire le costanti TRACE e DEBUG

Per impostazione predefinita, quando un'applicazione viene eseguita nel debug, viene definita la costante DEBUG. È possibile controllare questa operazione aggiungendo una voce DefineConstants nel file di progetto in un gruppo di proprietà. Di seguito è riportato un esempio di attivazione di TRACE per le configurazioni Debug e Release, oltre a DEBUG per le configurazioni Debug.

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
    <DefineConstants>DEBUG;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
    <DefineConstants>TRACE</DefineConstants>
</PropertyGroup>

Quando si usa Trace non collegato al debugger, è necessario configurare un listener di traccia, ad esempio dotnet-trace.

Analisi condizionale

Oltre ai metodi semplici Write e WriteLine, è anche possibile aggiungere condizioni con WriteIf e WriteLineIf. Ad esempio, la logica seguente controlla se il conteggio è pari a zero e quindi scrive un messaggio di debug:

if(count == 0)
{
    Debug.WriteLine("The count is 0 and this may cause an exception.");
}

È possibile riscriverla in una singola riga di codice:

Debug.WriteLineIf(count == 0, "The count is 0 and this may cause an exception.");

È anche possibile usare queste condizioni con Trace e con i flag definiti nell'applicazione:

bool errorFlag = false;  
System.Diagnostics.Trace.WriteIf(errorFlag, "Error in AppendData procedure.");  
System.Diagnostics.Debug.WriteIf(errorFlag, "Transaction abandoned.");  
System.Diagnostics.Trace.Write("Invalid value for data request");

Verificare che esistano determinate condizioni

Un'asserzione o un'istruzione Assert verifica una condizione specificata come argomento per l'istruzione Assert. Se la condizione restituisce true, non viene eseguita alcuna azione. Se la condizione restituisce false, l'asserzione non riesce. Se è in esecuzione una build di debug, il programma passa alla modalità di interruzione.

È possibile usare il metodo Assert da Debug o Trace, che si trovano nello spazio dei nomi System.Diagnostics. I metodi della classe Debug non sono inclusi in una versione di rilascio del programma, pertanto non aumentano le dimensioni né riducono la velocità del codice di rilascio.

Utilizzare il metodo System.Diagnostics.Debug.Assert per verificare le condizioni che devono restituire true se il codice è corretto. Si supponga, ad esempio, di aver scritto una funzione di divisione intera. In base alle regole matematiche, il divisore non può mai essere zero. È possibile testare questa condizione usando un'asserzione:

int IntegerDivide(int dividend, int divisor)
{
    Debug.Assert(divisor != 0, $"{nameof(divisor)} is 0 and will cause an exception.");

    return dividend / divisor;
}

Quando si esegue questo codice nel debugger, viene valutata l'istruzione di asserzione. Tuttavia, il confronto non viene eseguito nella versione di rilascio, quindi non viene eseguito alcun sovraccarico aggiuntivo.

Nota

Quando si usa System.Diagnostics.Debug.Assert, assicurarsi che qualsiasi codice all'interno dell'istruzione Assert non modifichi i risultati del programma se l'istruzione viene rimossa. In caso contrario, si potrebbe introdurre accidentalmente un bug che viene visualizzato solo nella versione di rilascio del programma. Prestare particolare attenzione alle asserzioni che contengono chiamate di funzione o di procedura.

L'uso di Debug e Trace dallo spazio dei nomi System.Diagnostics è un ottimo modo per offrire contesto aggiuntivo durante l'esecuzione e il debug dell'applicazione.