Rejestrowanie i śledzenie w aplikacjach .NET

Ukończone

W miarę dalszego opracowywania aplikacji i coraz bardziej złożonej aplikacji należy zastosować dodatkową diagnostykę debugowania do aplikacji.

Śledzenie to sposób monitorowania wykonywania aplikacji podczas jej działania. Instrumentację śledzenia i debugowania można dodać do aplikacji platformy .NET podczas jej opracowywania. Możesz użyć tej instrumentacji podczas tworzenia aplikacji i po jej wdrożeniu.

Ta prosta technika jest zaskakująco potężna. Można go używać w sytuacjach, w których potrzebujesz więcej niż debuger:

  • Problemy występujące w długich okresach czasu mogą być trudne do debugowania przy użyciu tradycyjnego debugera. Dzienniki umożliwiają szczegółowy przegląd pośmiertny, który obejmuje długie okresy czasu. Natomiast debugery są ograniczone do analizy w czasie rzeczywistym.
  • Aplikacje wielowątkowa i aplikacje rozproszone są często trudne do debugowania. Dołączanie debugera ma tendencję do modyfikowania zachowań. Szczegółowe dzienniki można analizować zgodnie z potrzebami, aby zrozumieć złożone systemy.
  • Problemy w aplikacjach rozproszonych mogą wynikać ze złożonej interakcji między wieloma składnikami. Połączenie debugera z każdą częścią systemu może nie być uzasadnione.
  • Wiele usług nie powinno być wstrzymanych. Dołączanie debugera często powoduje błędy przekroczenia limitu czasu.
  • Problemy nie zawsze są przewidywane. Rejestrowanie i śledzenie są przeznaczone dla małych obciążeń, dzięki czemu programy zawsze mogą być rejestrowane w przypadku wystąpienia problemu.

Zapisywanie informacji w oknach wyjściowych

Do tego momentu używaliśmy konsoli do wyświetlania informacji użytkownikowi aplikacji. Istnieją inne typy aplikacji utworzonych za pomocą platformy .NET, które mają interfejsy użytkownika, takie jak aplikacje mobilne, internetowe i klasyczne, i nie ma widocznej konsoli. W tych aplikacjach System.Console rejestruje komunikaty "za kulisami". Te komunikaty mogą być wyświetlane w oknie danych wyjściowych w programie Visual Studio lub Visual Studio Code. Mogą one również być danymi wyjściowymi dziennika systemu, takiego jak system Android logcat. W związku z tym należy wziąć pod uwagę bardzo dużą uwagę podczas korzystania z System.Console.WriteLine aplikacji innej niż konsola.

Jest to miejsce, w którym można używać funkcji System.Diagnostics.Debug i System.Diagnostics.Trace oprócz System.Consoleprogramu . ZarównoDebug, jak i są częścią System.Diagnostics i Trace będą zapisywane tylko w dziennikach, gdy jest dołączony odpowiedni odbiornik.

Wybór, którego interfejsu API stylu drukowania ma być używany, jest dla Ciebie. Najważniejsze różnice to:

  • System.console
    • Zawsze włączone i zawsze zapisuje dane w konsoli programu .
    • Przydatne w przypadku informacji, które klient może potrzebować w wydaniu.
    • Ponieważ jest to najprostsze podejście, często jest używane do tymczasowego debugowania ad hoc. Ten kod debugowania często nigdy nie jest zaewidencjonowany do kontroli źródła.
  • System.Diagnostics.Trace
    • Włączono tylko wtedy, gdy TRACE jest zdefiniowana.
    • Zapisuje do dołączonych odbiorników, domyślnie, DefaultTraceListener.
    • Użyj tego interfejsu API podczas tworzenia dzienników, które będą włączone w większości kompilacji.
  • System.Diagnostics.Debug
    • Włączone tylko wtedy, gdy DEBUG jest zdefiniowane (w trybie debugowania).
    • Zapisuje w dołączonym debugerze.
    • Użyj tego interfejsu API podczas tworzenia dzienników, które będą włączone tylko w kompilacjach debugowania.
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.");

Podczas projektowania strategii śledzenia i debugowania zastanów się, jak mają wyglądać dane wyjściowe. Wiele instrukcji Write wypełnionych niepowiązanymi informacjami tworzy dziennik, który jest trudny do odczytania. Z drugiej strony użycie funkcji WriteLine do umieszczania powiązanych instrukcji w osobnych wierszach może utrudnić odróżnienie informacji, do których informacji należy. Ogólnie rzecz biorąc, użyj wielu instrukcji Write, jeśli chcesz połączyć informacje z wielu źródeł, aby utworzyć pojedynczy komunikat informacyjny. Użyj instrukcji WriteLine, jeśli chcesz utworzyć pojedynczy pełny komunikat.

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

Te dane wyjściowe pochodzą z poprzedniego rejestrowania za pomocą Debugpolecenia :

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

Definiowanie stałych TRACE i DEBUG

Domyślnie, gdy aplikacja jest uruchomiona w ramach debugowania, stała jest zdefiniowana DEBUG . Możesz to kontrolować, dodając DefineConstants wpis w pliku projektu w grupie właściwości. Oto przykład włączania TRACE obu Debug konfiguracji i Release oprócz DEBUG Debug konfiguracji.

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

Jeśli nie Trace dołączono do debugera, należy skonfigurować odbiornik śledzenia, taki jak dotnet-trace.

Śledzenie warunkowe

Oprócz prostych Write i WriteLine metod istnieje również możliwość dodawania warunków za pomocą WriteIf metod i WriteLineIf. Na przykład poniższa logika sprawdza, czy liczba jest równa zero, a następnie zapisuje komunikat debugowania:

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

Można to napisać ponownie w jednym wierszu kodu:

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

Możesz również użyć tych warunków z flagami Trace zdefiniowanymi w aplikacji i z flagami:

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");

Sprawdź, czy istnieją pewne warunki

Asercji lub Assert instrukcji testuje warunek określony jako argument instrukcji Assert . Jeśli warunek zwróci wartość true, nie wystąpi żadna akcja. Jeśli warunek zwróci wartość false, asercji zakończy się niepowodzeniem. Jeśli używasz kompilacji debugowania, program przechodzi w tryb przerwania.

Możesz użyć Assert metody z Debug adresu lub Trace, które znajdują się w System.Diagnostics przestrzeni nazw. Debug Metody klas nie są uwzględniane w wersji wydania programu, więc nie zwiększają rozmiaru ani nie zmniejszają szybkości kodu wydania.

System.Diagnostics.Debug.Assert Użyj metody bezpłatnie, aby przetestować warunki, które powinny zawierać wartość true, jeśli kod jest poprawny. Załóżmy na przykład, że utworzono funkcję dzielenia liczb całkowitych. Zgodnie z zasadami matematyki podział nigdy nie może być zerowy. Ten warunek można przetestować przy użyciu asercji:

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

    return dividend / divisor;
}

Po uruchomieniu tego kodu w debugerze zostanie obliczona instrukcja asercji. Jednak porównanie nie jest wykonywane w wersji wydania, więc nie ma dodatkowych obciążeń.

Uwaga

Jeśli używasz metody System.Diagnostics.Debug.Assert, upewnij się, że żaden kod wewnątrz Assert programu nie zmienia wyników programu, jeśli asercja zostanie usunięta. W przeciwnym razie możesz przypadkowo wprowadzić usterkę, która jest wyświetlana tylko w wersji programu. Należy zachować szczególną ostrożność w przypadku asertów zawierających wywołania funkcji lub procedury.

Użycie Debug elementów i Trace z System.Diagnostics przestrzeni nazw to doskonały sposób zapewnienia dodatkowego kontekstu podczas uruchamiania i debugowania aplikacji.