Konzepte verteilter .NET-Ablaufverfolgung
Die verteilte Ablaufverfolgung ist eine Diagnosetechnik, mit der Techniker Fehler und Leistungsprobleme in Anwendungen lokalisieren können, insbesondere solche, die auf mehrere Computer oder Prozesse verteilt werden können. Allgemeine Informationen dazu, wo verteilte Ablaufverfolgung sinnvoll ist, finden Sie in der Übersicht über verteilte Ablaufverfolgung.
Spuren und Aktivitäten
Jedes Mal, wenn eine neue Anforderung von einer Anwendung empfangen wird, kann sie mit einer Ablaufverfolgung verknüpft werden. In Anwendungskomponenten, die in .NET geschrieben sind, werden Arbeitseinheiten in einer Ablaufverfolgung durch Instanzen von System.Diagnostics.Activity dargestellt und die Ablaufverfolgung als Ganzes bildet eine Baumstruktur dieser Aktivitäten, die sich möglicherweise über viele verschiedene Prozesse erstreckt. Die erste Aktivität, die für eine neue Anforderung erstellt wird, bildet den Stamm der Ablaufverfolgungsstruktur und verfolgt die Gesamtdauer und den Erfolg/das Scheitern der Verarbeitung der Anforderung. Kinderaktivitäten können optional erstellt werden, um die Arbeit in verschiedene Schritte zu unterteilen, die einzeln nachverfolgt werden können. Anhand einer Aktivität, die eine bestimmte eingehende HTTP-Anforderung in einem Webserver nachverfolgt, können beispielsweise untergeordnete Aktivitäten erstellt werden, um jede der Datenbankabfragen nachzuverfolgen, die zum Abschließen der Anforderung erforderlich waren. Dadurch kann die Dauer und der Erfolg jeder Abfrage unabhängig aufgezeichnet werden. Aktivitäten können andere Informationen für jede Arbeitseinheit aufzeichnen, z. B. OperationName, Name-Wert-Paare, die als Tags bezeichnet werden, und Events. Der Name identifiziert den Typ der ausgeführten Arbeit, Tags können beschreibende Parameter der Arbeit aufzeichnen, und Ereignisse sind ein einfacher Protokollierungsmechanismus zum Aufzeichnen von Zeitstempel-Diagnosemeldungen.
Anmerkung
Eine andere branchenübliche Bezeichnung für Arbeitseinheiten in einer verteilten Ablaufverfolgung sind „Spans“. .NET hat den Begriff „Aktivität“ schon vor vielen Jahren verwendet, bevor der Name „Span“ für dieses Konzept etabliert war.
Aktivitäts-IDs
Beziehungen zwischen übergeordneten und untergeordneten Elementen zwischen Aktivitäten in der verteilten Ablaufverfolgungsstruktur werden über eindeutige IDs hergestellt. Die .NET-Implementierung der verteilten Ablaufverfolgung unterstützt zwei ID-Schemas, den W3C-Standard TraceContext, der ab .NET 5 Standard ist, sowie eine ältere .NET-Konvention namens „Hierarchical“, die aus Gründen der Abwärtskompatibilität verfügbar ist. Activity.DefaultIdFormat steuert, welches ID-Schema verwendet wird. Im W3C TraceContext-Standard wird jedem Trace eine global eindeutige 16-Byte-Trace-ID (Activity.TraceId) zugewiesen, und jeder Aktivität innerhalb des Trace wird eine eindeutige 8-Byte-Span-ID (Activity.SpanId) zugewiesen. Jede Aktivität zeichnet die trace-id, die eigene span-id und die Span-id des übergeordneten Elements (Activity.ParentSpanId) auf. Da verteilte Ablaufverfolgungen die Arbeit über Prozessgrenzen hinweg verfolgen können, befinden sich übergeordnete und untergeordnete Aktivitäten möglicherweise nicht im selben Prozess. Die Kombination aus einer trace-id und einer übergeordneten span-id kann die übergeordnete Aktivität global eindeutig identifizieren, unabhängig davon, in welchem Prozess sie sich befindet.
Activity.DefaultIdFormat steuert, welches ID-Format für den Start neuer Traces verwendet wird, aber standardmäßig wird beim Hinzufügen einer neuen Aktivität zu einem bestehenden Trace das Format verwendet, das die übergeordnete Aktivität verwendet. Wenn Sie Activity.ForceDefaultIdFormat auf TRUE festlegen, wird dieses Verhalten außer Kraft gesetzt, und alle neuen Aktivitäten werden mit dem DefaultIdFormat erstellt, auch wenn das übergeordnete Element ein anderes ID-Format verwendet.
Starten und Beenden von Aktivitäten
Jeder Thread in einem Prozess kann über ein entsprechendes Activity-Objekt verfügen, das die auf diesem Thread ausgeführte Arbeit verfolgt und über Activity.Currentzugänglich ist. Die aktuelle Aktivität durchläuft automatisch alle synchronen Aufrufe für einen Thread und folgt asynchronen Aufrufen, die in verschiedenen Threads verarbeitet werden. Wenn Aktivität A die aktuelle Aktivität in einem Thread ist und der Code eine neue Aktivität B startet, wird B zur neuen aktuellen Aktivität in diesem Thread. Standardmäßig behandelt Aktivität B auch Aktivität A als übergeordnetes Element. Wenn Aktivität B später beendet wird, wird Aktivität A als aktuelle Aktivität im Thread wiederhergestellt. Wenn eine Aktivität gestartet wird, wird die aktuelle Uhrzeit als Activity.StartTimeUtc erfasst. Wenn sie beendet wird, wird Activity.Duration als Differenz zwischen der aktuellen Uhrzeit und der Startzeit berechnet.
Koordinieren über Prozessgrenzen hinweg
Übergeordnete Aktivitäts-IDs müssen über das Netzwerk übertragen werden, damit der empfangende Prozess Aktivitäten erstellen kann, die sich auf sie beziehen. So können Aufgaben über Prozessgrenzen hinweg verfolgt werden. Bei Verwendung des W3C-TraceContext-ID-Formats verwendet .NET auch die vom Standard empfohlenen HTTP-Header, um diese Informationen zu übertragen. Bei Verwendung des Hierarchical-ID-Formats verwendet .NET einen benutzerdefinierten Anforderungs-ID-HTTP-Header, um die ID zu übertragen. Im Gegensatz zu vielen anderen Language Runtimes verstehen integrierte .NET-Bibliotheken wie der ASP.NET-Webserver und System.Net.Http nativ, wie Aktivitäts-IDs in HTTP-Nachrichten decodiert und codiert werden. Die Runtime versteht auch, wie die ID durch synchrone und asynchrone Aufrufe geleitet wird. Das bedeutet, dass .NET-Anwendungen, die HTTP-Nachrichten empfangen und ausgeben, automatisch am Fluss der verteilten Ablaufverfolgungs-IDs teilnehmen, ohne spezielle Codierung durch den App-Entwickler oder Abhängigkeiten von Bibliotheken von Drittanbietern. Drittanbieterbibliotheken können Unterstützung für die Übertragung von IDs über Nicht-HTTP-Nachrichtenprotokolle oder die Unterstützung von benutzerdefinierten Codierungskonventionen für HTTP hinzufügen.
Nachverfolgungserfassung
Instrumentierter Code kann Activity-Objekte als Teil einer verteilten Ablaufverfolgung erstellen, aber die Informationen in diesen Objekten müssen übertragen und in einem zentralen persistenten Speicher serialisiert werden, damit die gesamte Ablaufverfolgung später sinnvoll überprüft werden kann. Es gibt mehrere Telemetriesammlungsbibliotheken, die diese Aufgabe ausführen können, z. B. Application Insights, OpenTelemetry-oder eine Bibliothek, die von einem Drittanbieter für Telemetrie oder APM bereitgestellt wird. Alternativ können Entwickler mithilfe von System.Diagnostics.ActivityListener oder System.Diagnostics.DiagnosticListener eine eigene benutzerdefinierte Aktivitätstelemetrieerfassung erstellen. ActivityListener unterstützt die Beobachtung von Aktivitäten, unabhängig davon, ob der Entwickler über vorherige Kenntnisse verfügt. Dadurch wird ActivityListener zu einer einfachen und flexiblen Allzwecklösung. Im Gegensatz dazu ist die Verwendung von DiagnosticListener ein komplexeres Szenario, bei dem der instrumentierte Code sich durch den Aufruf von DiagnosticSource.StartActivity anmelden und die Erfassungsbibliothek die genauen Namensinformationen kennen muss, die der instrumentierte Code beim Start verwendet hat. Die Verwendung von DiagnosticSource und DiagnosticListener ermöglicht es dem Ersteller und Listener, beliebige .NET-Objekte auszutauschen und angepasste Konventionen zum Übergeben von Informationen einzurichten.
Stichproben
Um die Leistung in Anwendungen mit hohem Durchsatz zu verbessern, unterstützt die verteilte Ablaufverfolgung unter .NET das Abfragen nur einer Teilmenge von Ablaufverfolgungen, anstatt alle Ablaufverfolgungen aufzuzeichnen. Für Aktivitäten, die mit der empfohlenen ActivitySource.StartActivity-API erstellt wurden, können Telemetrieerfassungsbibliotheken die Erfassung mit dem ActivityListener.Sample-Rückruf steuern. Die Protokollierungsbibliothek kann die Aktivität wahlweise überhaupt nicht erstellen, sie mit minimalen Informationen erstellen, die für die Weitergabe von verteilten Ablaufverfolgungs-IDs erforderlich sind, oder sie mit vollständigen Diagnoseinformationen auffüllen. Diese Entscheidungen bedeuten einen Kompromiss zwischen erhöhtem Leistungsaufwand und gesteigerter diagnostischer Nützlichkeit. Aktivitäten, die mit dem älteren Muster des Aufrufs von Activity.Activity und DiagnosticSource.StartActivity gestartet werden, können auch die DiagnosticListener-Erfassung unterstützen, indem sie zuerst DiagnosticSource.IsEnabled aufrufen. Selbst beim Erfassen vollständiger Diagnoseinformationen ist die .NET-Implementierung schnell konzipiert – gekoppelt mit einem effizienten Sammler, kann eine Aktivität erstellt, aufgefüllt und in etwa einem Mikrosekunden auf moderner Hardware übertragen werden. Sampling kann die Instrumentierungskosten für jede nicht aufgezeichnete Aktivität auf weniger als 100 Nanosekunden reduzieren.
Nächste Schritte
Beispielcode für den Einstieg in verteilte Ablaufverfolgung in .NET-Anwendungen finden Sie unter Übersicht über verteilte Ablaufverfolgung.
Eine Liste der Aktivitäten, die nativ von .NET ausgegeben werden, finden Sie unter Integrierte Aktivitäten in .NET.