Uwaga
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Ten artykuł dotyczy: ✔️ .NET Core 2.1 i nowsze wersje ✔️ programu .NET Framework 4.5 i nowszych wersji
Aplikacje platformy .NET można instrumentować przy użyciu interfejsu System.Diagnostics.Activity API do tworzenia telemetrii śledzenia rozproszonego. Niektóre instrumentacje są wbudowane w standardowe biblioteki .NET, ale warto dodać więcej, aby ułatwić rozpoznanie kodu. W tym samouczku dodasz niestandardową, rozproszoną instrumentację śledzenia. Zobacz samouczek dotyczący zbierania danych, aby dowiedzieć się więcej na temat rejestrowania danych telemetrycznych generowanych przez tę instrumentację.
Wymagania wstępne
- .NET Core 2.1 SDK lub nowsza wersja
Tworzenie początkowej aplikacji
Najpierw utworzysz przykładową aplikację, która zbiera dane telemetryczne przy użyciu biblioteki OpenTelemetry, ale nie ma jeszcze żadnej instrumentacji.
dotnet new console
Aplikacje, które korzystają z platformy .NET 5 i nowszych, zawierają już niezbędne API śledzenia rozproszonego. W przypadku aplikacji przeznaczonych dla starszych wersji platformy .NET dodaj pakiet NuGet System.Diagnostics.DiagnosticSource w wersji 5 lub nowszej. W przypadku bibliotek docelowych netstandard zalecamy odwoływanie się do najstarszej wersji pakietu, która jest nadal obsługiwana i zawiera interfejsy API wymagane przez bibliotekę.
dotnet add package System.Diagnostics.DiagnosticSource
Dodaj pakiety NuGet OpenTelemetry i OpenTelemetry.Exporter.Console, które będą używane do zbierania danych telemetrycznych.
dotnet add package OpenTelemetry
dotnet add package OpenTelemetry.Exporter.Console
Zastąp zawartość wygenerowanego Program.cs tym przykładowym źródłem:
using OpenTelemetry;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;
using System;
using System.Threading.Tasks;
namespace Sample.DistributedTracing
{
class Program
{
static async Task Main(string[] args)
{
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
.SetResourceBuilder(ResourceBuilder.CreateDefault().AddService("MySample"))
.AddSource("Sample.DistributedTracing")
.AddConsoleExporter()
.Build();
await DoSomeWork("banana", 8);
Console.WriteLine("Example work done");
}
// All the functions below simulate doing some arbitrary work
static async Task DoSomeWork(string foo, int bar)
{
await StepOne();
await StepTwo();
}
static async Task StepOne()
{
await Task.Delay(500);
}
static async Task StepTwo()
{
await Task.Delay(1000);
}
}
}
Aplikacja nie ma jeszcze instrumentacji, więc nie ma żadnych danych śledzenia do wyświetlenia.
> dotnet run
Example work done
Najlepsze rozwiązania
Tylko deweloperzy aplikacji muszą odwoływać się do opcjonalnej zewnętrznej biblioteki w celu zbierania rozproszonej telemetrii śledzenia, takiej jak OpenTelemetry w tym przykładzie. Autorzy bibliotek platformy .NET mogą wyłącznie polegać na interfejsach API w pliku System.Diagnostics.DiagnosticSource, który jest częścią środowiska uruchomieniowego platformy .NET. Dzięki temu biblioteki będą działać w szerokim zakresie aplikacji platformy .NET, niezależnie od preferencji dewelopera aplikacji dotyczących biblioteki lub dostawcy do zbierania danych telemetrycznych.
Dodawanie instrumentacji podstawowej
Aplikacje i biblioteki dodają instrumentację śledzenia rozproszonego przy użyciu klas System.Diagnostics.ActivitySource i System.Diagnostics.Activity.
Źródło Aktywności
Najpierw utwórz wystąpienie elementu ActivitySource. Usługa ActivitySource udostępnia interfejsy API do tworzenia i uruchamiania obiektów działania. Dodaj statyczną zmienną ActivitySource powyżej Main()
i using System.Diagnostics;
do using
dyrektyw.
using OpenTelemetry;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;
using System;
using System.Diagnostics;
using System.Threading.Tasks;
namespace Sample.DistributedTracing
{
class Program
{
private static ActivitySource source = new ActivitySource("Sample.DistributedTracing", "1.0.0");
static async Task Main(string[] args)
{
// ...
Najlepsze rozwiązania
Utwórz ActivitySource raz, zapisz go w zmiennej statycznej i używaj tego wystąpienia tak długo, jak będzie potrzebne. Każda biblioteka lub podkomponent biblioteki może (i często powinna) utworzyć własne źródło. Rozważ utworzenie nowego źródła zamiast ponownego korzystania z istniejącego źródła, jeśli przewidujesz, że deweloperzy aplikacji docenią możliwość włączenia i wyłączenia telemetrii działania w źródłach niezależnie.
Nazwa źródła przekazana do konstruktora musi być unikatowa, aby uniknąć konfliktów z innymi źródłami. Jeśli istnieje wiele źródeł w tym samym zestawie, użyj nazwy hierarchicznej zawierającej nazwę zestawu i opcjonalnie nazwę składnika, na przykład
Microsoft.AspNetCore.Hosting
. Jeśli zestaw dodaje instrumentację dla kodu w drugim, niezależnym zestawie, nazwa powinna być oparta na zestawie, który definiuje element ActivitySource, a nie zestaw, którego kod jest instrumentowany.Parametr wersji jest opcjonalny. Zalecamy podanie wersji w przypadku wydania wielu wersji biblioteki i wprowadzenia zmian w instrumentowanej telemetrii.
Uwaga
OpenTelemetry stosuje alternatywne terminy "Tracer" i "Span". Na platformie .NET "ActivitySource" jest implementacją Tracera, a "Activity" jest implementacją "Span". Typ Activity w .NET znacznie poprzedza specyfikację OpenTelemetry, a zachowane oryginalne nazewnictwo ma na celu spójność w ekosystemie .NET i zgodność aplikacji .NET.
Aktywność
Użyj obiektu ActivitySource, aby uruchomić i zatrzymać obiekty działania wokół znaczących jednostek pracy. Zaktualizuj aplikację DoSomeWork() przy użyciu kodu pokazanego tutaj:
static async Task DoSomeWork(string foo, int bar)
{
using (Activity activity = source.StartActivity("SomeWork"))
{
await StepOne();
await StepTwo();
}
}
Uruchomienie aplikacji spowoduje teraz wyświetlenie nowego zarejestrowanego działania:
> dotnet run
Activity.Id: 00-f443e487a4998c41a6fd6fe88bae644e-5b7253de08ed474f-01
Activity.DisplayName: SomeWork
Activity.Kind: Internal
Activity.StartTime: 2021-03-18T10:36:51.4720202Z
Activity.Duration: 00:00:01.5025842
Resource associated with Activity:
service.name: MySample
service.instance.id: 067f4bb5-a5a8-4898-a288-dec569d6dbef
Notatki
ActivitySource.StartActivity tworzy i uruchamia działanie w tym samym czasie. Wymieniony wzorzec kodu używa bloku
using
, który automatycznie usuwa utworzony obiekt Activity po wykonaniu bloku. Usuwanie obiektu Activity spowoduje zatrzymanie go, aby kod nie musiał jawnie wywoływać elementu Activity.Stop(). Upraszcza to wzorzec kodowania.ActivitySource.StartActivity wewnętrznie określa, czy istnieją odbiorniki rejestrujące działanie. Jeśli nie ma zarejestrowanych odbiorników lub istnieją odbiorniki, które nie są zainteresowane,
StartActivity()
zwrócinull
i uniknie tworzenia obiektu Activity. Jest to optymalizacja wydajności, dzięki czemu wzorzec kodu może być nadal używany w funkcjach, które są często wywoływane.
Opcjonalne: Wypełnianie tagów
Działania obsługują dane klucz-wartość o nazwie Tagi, często używane do przechowywania dowolnych parametrów pracy, które mogą być przydatne do diagnostyki. Zaktualizuj DoSomeWork()
, aby uwzględnić je:
static async Task DoSomeWork(string foo, int bar)
{
using (Activity activity = source.StartActivity("SomeWork"))
{
activity?.SetTag("foo", foo);
activity?.SetTag("bar", bar);
await StepOne();
await StepTwo();
}
}
> dotnet run
Activity.Id: 00-2b56072db8cb5a4496a4bfb69f46aa06-7bc4acda3b9cce4d-01
Activity.DisplayName: SomeWork
Activity.Kind: Internal
Activity.StartTime: 2021-03-18T10:37:31.4949570Z
Activity.Duration: 00:00:01.5417719
Activity.TagObjects:
foo: banana
bar: 8
Resource associated with Activity:
service.name: MySample
service.instance.id: 25bbc1c3-2de5-48d9-9333-062377fea49c
Example work done
Najlepsze rozwiązania
- Jak wspomniano powyżej,
activity
zwracane przez ActivitySource.StartActivity może mieć wartość null. Operator?.
łączenia wartości null w języku C# jest wygodnym skrótem do wywoływania Activity.SetTag tylko wtedy, gdyactivity
nie ma wartości null. Zachowanie jest identyczne z pisaniem:
if(activity != null)
{
activity.SetTag("foo", foo);
}
Funkcja OpenTelemetry udostępnia zestaw zalecanych konwencji ustawiania tagów dla działań reprezentujących typowe typy pracy aplikacji.
Jeśli instrumentujesz funkcje z wymaganiami o wysokiej wydajności, Activity.IsAllDataRequested jest wskazówką, która wskazuje, czy którykolwiek z kodów nasłuchujących Aktywności zamierza odczytywać informacje pomocnicze, takie jak tagi. Jeśli odbiornik go nie odczytuje, nie ma potrzeby, aby instrumentowany kod zużywał cykle procesora. Dla uproszczenia ten przykład nie stosuje tej optymalizacji.
Opcjonalne: Dodawanie zdarzeń
Zdarzenia to komunikaty ze znacznikami czasu, które mogą dołączać dowolny strumień dodatkowych danych diagnostycznych do działań. Dodaj kilka zdarzeń do aktywności.
static async Task DoSomeWork(string foo, int bar)
{
using (Activity activity = source.StartActivity("SomeWork"))
{
activity?.SetTag("foo", foo);
activity?.SetTag("bar", bar);
await StepOne();
activity?.AddEvent(new ActivityEvent("Part way there"));
await StepTwo();
activity?.AddEvent(new ActivityEvent("Done now"));
}
}
> dotnet run
Activity.Id: 00-82cf6ea92661b84d9fd881731741d04e-33fff2835a03c041-01
Activity.DisplayName: SomeWork
Activity.Kind: Internal
Activity.StartTime: 2021-03-18T10:39:10.6902609Z
Activity.Duration: 00:00:01.5147582
Activity.TagObjects:
foo: banana
bar: 8
Activity.Events:
Part way there [3/18/2021 10:39:11 AM +00:00]
Done now [3/18/2021 10:39:12 AM +00:00]
Resource associated with Activity:
service.name: MySample
service.instance.id: ea7f0fcb-3673-48e0-b6ce-e4af5a86ce4f
Example work done
Najlepsze rozwiązania
- Zdarzenia są przechowywane na liście w pamięci do momentu ich przesyłania, co sprawia, że ten mechanizm nadaje się tylko do rejestrowania niewielkiej liczby zdarzeń. W przypadku dużej lub niezwiązanej liczby zdarzeń lepiej użyć interfejsu API rejestrowania skoncentrowanego na tym zadaniu, takim jak ILogger. Usługa ILogger zapewnia również, że informacje rejestrowania będą dostępne niezależnie od tego, czy deweloper aplikacji zdecyduje się na korzystanie z śledzenia rozproszonego. Aplikacja ILogger obsługuje automatyczne przechwytywanie aktywnych identyfikatorów działań, dzięki czemu komunikaty rejestrowane za pośrednictwem tego interfejsu API mogą być nadal skorelowane ze śladem rozproszonym.
Opcjonalnie: Dodawanie stanu
Funkcja OpenTelemetry umożliwia każdemu z działań zgłaszanie stanu reprezentującego wynik powodzenia/niepowodzenia pracy. W tym celu platforma .NET ma silnie typizowany interfejs API:
Wartości ActivityStatusCode są reprezentowane jako , Unset
, Ok
i Error
.
Zaktualizuj program DoSomeWork(), aby ustawić stan:
static async Task DoSomeWork(string foo, int bar)
{
using (Activity activity = source.StartActivity("SomeWork"))
{
activity?.SetTag("foo", foo);
activity?.SetTag("bar", bar);
await StepOne();
activity?.AddEvent(new ActivityEvent("Part way there"));
await StepTwo();
activity?.AddEvent(new ActivityEvent("Done now"));
// Pretend something went wrong
activity?.SetStatus(ActivityStatusCode.Error, "Use this text give more information about the error");
}
}
Opcjonalnie: Dodawanie dodatkowych działań
Działania można zagnieżdżać, aby opisać fragmenty większej całości pracy. Może to być przydatne w przypadku fragmentów kodu, które mogą nie być wykonywane szybko lub lepiej lokalizować błędy pochodzące z określonych zależności zewnętrznych. Mimo że w tym przykładzie użyto działania w każdej metodzie, jest to wyłącznie dlatego, że dodatkowy kod został zminimalizowany. W większym i bardziej realistycznym projekcie, użycie Activity w każdej metodzie spowodowałoby nadmiernie szczegółowe ślady, dlatego nie jest to zalecane.
Zaktualizuj StepOne i StepTwo, aby dodać więcej śledzenia wokół tych oddzielnych kroków.
static async Task StepOne()
{
using (Activity activity = source.StartActivity("StepOne"))
{
await Task.Delay(500);
}
}
static async Task StepTwo()
{
using (Activity activity = source.StartActivity("StepTwo"))
{
await Task.Delay(1000);
}
}
> dotnet run
Activity.Id: 00-9d5aa439e0df7e49b4abff8d2d5329a9-39cac574e8fda44b-01
Activity.ParentId: 00-9d5aa439e0df7e49b4abff8d2d5329a9-f16529d0b7c49e44-01
Activity.DisplayName: StepOne
Activity.Kind: Internal
Activity.StartTime: 2021-03-18T10:40:51.4278822Z
Activity.Duration: 00:00:00.5051364
Resource associated with Activity:
service.name: MySample
service.instance.id: e0a8c12c-249d-4bdd-8180-8931b9b6e8d0
Activity.Id: 00-9d5aa439e0df7e49b4abff8d2d5329a9-4ccccb6efdc59546-01
Activity.ParentId: 00-9d5aa439e0df7e49b4abff8d2d5329a9-f16529d0b7c49e44-01
Activity.DisplayName: StepTwo
Activity.Kind: Internal
Activity.StartTime: 2021-03-18T10:40:51.9441095Z
Activity.Duration: 00:00:01.0052729
Resource associated with Activity:
service.name: MySample
service.instance.id: e0a8c12c-249d-4bdd-8180-8931b9b6e8d0
Activity.Id: 00-9d5aa439e0df7e49b4abff8d2d5329a9-f16529d0b7c49e44-01
Activity.DisplayName: SomeWork
Activity.Kind: Internal
Activity.StartTime: 2021-03-18T10:40:51.4256627Z
Activity.Duration: 00:00:01.5286408
Activity.TagObjects:
foo: banana
bar: 8
otel.status_code: ERROR
otel.status_description: Use this text give more information about the error
Activity.Events:
Part way there [3/18/2021 10:40:51 AM +00:00]
Done now [3/18/2021 10:40:52 AM +00:00]
Resource associated with Activity:
service.name: MySample
service.instance.id: e0a8c12c-249d-4bdd-8180-8931b9b6e8d0
Example work done
Zwróć uwagę, że zarówno StepOne, jak i StepTwo zawierają identyfikator ParentId, który odnosi się do SomeWork. Konsola nie jest najlepszą wizualizacją zagnieżdżonych struktur pracy, ale wiele narzędzi z graficznym interfejsem użytkownika, takich jak Zipkin, może to pokazać jako wykres Gantta.
Opcjonalnie: ActivityKind
Działania mają właściwość Activity.Kind, która opisuje relację między działaniem, jego elementem nadrzędnym i elementami podrzędnymi. Domyślnie wszystkie nowe Aktywności są ustawione na wartość Internal, co jest odpowiednie dla Aktywności będących operacją wewnętrzną w aplikacji, bez zdalnego rodzica lub dzieci. Inne rodzaje można ustawić przy użyciu parametru kind w pliku ActivitySource.StartActivity. Aby uzyskać inne opcje, zobacz System.Diagnostics.ActivityKind.
Opcjonalne: łącza
W przypadku pracy w systemach przetwarzania wsadowego pojedyncza aktywność może reprezentować pracę na rzecz wielu różnych żądań jednocześnie, z których każde ma własny identyfikator śledzenia. Mimo że aktywność jest ograniczona do pojedynczego elementu nadrzędnego, może łączyć się z dodatkowymi identyfikatorami śledzenia przy użyciu System.Diagnostics.ActivityLink. Każdy element ActivityLink jest wypełniany za pomocą elementu ActivityContext, który przechowuje informacje o identyfikatorze aktywności, do której jest połączony. Element ActivityContext można pobrać z obiektów działań w procesie przy użyciu Activity.Context lub można go przeanalizować z zserializowanych danych identyfikacyjnych przy użyciu ActivityContext.Parse(String, String).
void DoBatchWork(ActivityContext[] requestContexts)
{
// Assume each context in requestContexts encodes the trace-id that was sent with a request
using(Activity activity = s_source.StartActivity(name: "BigBatchOfWork",
kind: ActivityKind.Internal,
parentContext: default,
links: requestContexts.Select(ctx => new ActivityLink(ctx))
{
// do the batch of work here
}
}
W przeciwieństwie do zdarzeń i tagów, które można dodać na żądanie, linki należy dodawać podczas StartActivity()
i są następnie niezmienne.
Ważne
Zgodnie ze specyfikacją OpenTelemetry sugerowany limit liczby łączy wynosi 128. Należy jednak pamiętać, że ten limit nie jest wymuszany.