Rejestrowanie i śledzenie w aplikacjach platformy .NET
W miarę jak opracowywanie aplikacji postępuje i staje się ona bardziej złożona, może być konieczne zastosowanie bardziej zaawansowanych metod diagnostycznych debugowania.
Śledzenie służy do monitorowania wykonywania aplikacji w trakcie jej działania. Można dodać instrumentację śledzenia i debugowania do aplikacji .NET podczas jej opracowywania. Możesz użyć tej instrumentacji podczas tworzenia aplikacji i po jej wdrożeniu.
Ta prosta technika jest zaskakująco zaawansowana. Można jej używać w sytuacjach, gdy potrzebujesz rozwiązania bardziej złożonego niż debuger:
- Problemy występujące w długich okresach 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. Z kolei debugery są ograniczone do analizy w czasie rzeczywistym.
- Aplikacje wielowątkowe i aplikacje rozproszone często są trudne do debugowania. Dołączenie debugera powoduje modyfikację zachowań. Szczegółowe dzienniki można analizować zgodnie z potrzebami, aby zrozumieć złożone systemy.
- Problemy w aplikacjach rozproszonych mogą powstać w wyniku złożonej interakcji między wieloma składnikami. Dołączanie debugera do każdej części systemu może nie być uzasadnione.
- Wielu usług nie należy zawieszać. Dołączanie debugera często powoduje błędy polegające na przekroczeniu limitu czasu.
- Problemy nie zawsze można przewidzieć. Rejestrowanie i śledzenie zostało zaprojektowane z myślą o niskim obciążeniu, dzięki czemu program może zawsze rejestrować w przypadku wystąpienia problemu.
Zapisywanie informacji w oknach danych wyjściowych
Do tego momentu używaliśmy konsoli w celu wyświetlenia informacji dla użytkownika aplikacji. Istnieją inne typy aplikacji utworzonych za pomocą platformy .NET, które mają interfejsy użytkownika i nie są widoczne konsole, takie jak aplikacje mobilne, internetowe i klasyczne. W tych aplikacjach System.Console służy do rejestrowania komunikatów "za kulisami". Te komunikaty mogą być wyświetlane w oknie danych wyjściowych w programie Visual Studio lub Visual Studio Code. Mogą być również wyprowadzane jako dane wyjściowe do dziennika systemowego, takiego jak logcat w systemie Android. W związku z tym podczas korzystania z System.Console.WriteLine w aplikacji niekonsolowej należy dokładnie rozważyć.
W takiej sytuacji można użyć System.Diagnostics.Debug i System.Diagnostics.Trace oprócz System.Console. Zarówno Debug, jak i Trace są częścią System.Diagnostics i zapisują tylko do dziennika, gdy jest dołączony odpowiedni odbiornik.
Wybór interfejsu API w stylu wydruku do użycia należy do Ciebie. Kluczowe różnice są następujące:
-
System.Console
- Zawsze włączony i zawsze zapisuje do konsoli.
- Przydatny w przypadku informacji, które mogą być wymagane przez klienta w danym wydaniu.
- Ponieważ jest to najprostsze podejście,
System.Consolejest często używane do tymczasowego debugowania ad hoc. Często zdarza się, że ten kod debugowania nie jest nigdy zaewidencjonowany w ramach kontroli źródła.
-
System.Diagnostics.Trace
- Włączony tylko w przypadku zdefiniowana elementu
TRACE. - Zapisuje do dołączonych odbiorników. Wartość domyślna: DefaultTraceListener.
- Użyj tego interfejsu API podczas tworzenia dzienników, które planujesz włączyć w większości kompilacji.
- Włączony tylko w przypadku zdefiniowana elementu
-
System.Diagnostics.Debug
- Włączony tylko w przypadku zdefiniowania elementu
DEBUG(podczas pracy w trybie debugowania). - Zapisuje do dołączonego debugera.
- Użyj tego interfejsu API podczas tworzenia dzienników, które planujesz włączyć tylko w kompilacjach debugowania.
- Włączony tylko w przypadku zdefiniowania elementu
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żywanie funkcji WriteLine do umieszczania powiązanych instrukcji w osobnych wierszach może utrudnić odróżnienie, które informacje występują wspólnie. Mówiąc ogólnie, użyj wielu instrukcji Write, jeśli chcesz połączyć informacje z wielu źródeł w celu utworzenia pojedynczego komunikatu informacyjnego. Jeśli chcesz utworzyć pojedynczy, kompletny komunikat, użyj instrukcji WriteLine.
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 przy użyciu elementu Debug:
Debug - This is a full line.
This is another full line.
Definiowanie stałych TRACE i DEBUG
Domyślnie, gdy aplikacja działa w trybie debugowania, definiowana jest stała DEBUG. Tę definicję DefineConstants można kontrolować, dodając wpis w pliku projektu w grupie właściwości. Poniżej znajduje się przykład włączenia stałej TRACE dla konfiguracji Debug i Release, oprócz stałej DEBUG dla konfiguracji Debug.
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DefineConstants>DEBUG;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DefineConstants>TRACE</DefineConstants>
</PropertyGroup>
Jeśli używasz Trace kiedy nie jest się podłączonym do debugera, należy skonfigurować nasłuchiwacz śladów, taki jak dotnet-trace.
Śledzenie warunkowe
Oprócz prostych metod Write i WriteLine istnieje również możliwość dodawania warunków przy użyciu elementów WriteIf 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żesz zapisać go 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");
Sprawdzanie, czy określone warunki istnieją
Potwierdzenie (lub instrukcja Assert) testuje warunek, który określasz jako argument instrukcji Assert. Jeśli warunek ma wartość „true” (prawda), nie są wykonywane żadne akcje. Jeśli warunek ma wartość „false” (fałsz), asercja kończy się niepowodzeniem. W przypadku uruchamiania programu z kompilacją debugowania program przechodzi do trybu przerwania.
Można użyć metody Assert z instrukcji Debug lub Trace, które znajdują się w przestrzeni nazw System.Diagnostics. Metody klasy Debug nie są uwzględnione w wydanej wersji 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 napisałeś funkcję dzielącą liczby całkowite. Zgodnie z regułami matematycznymi dzielnik nigdy nie może być równy zero. Można przetestować ten warunek 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 instrukcja asercji jest oceniana. Ale w wersji finalnej porównanie nie jest wykonane, więc nie ma dodatkowego obciążenia.
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żna przypadkowo wprowadzić usterkę, która będzie wyświetlana tylko w wydanej wersji programu. Szczególnie ostrożnie obsługuj asercje, które zawierają wywołania funkcji lub procedur.
Jak widać, użycie elementów Debug i Trace z System.Diagnostics przestrzeni nazw jest doskonałym sposobem zapewnienia ważnego kontekstu podczas uruchamiania i debugowania aplikacji.