Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Den här artikeln gäller för: ✔️ .NET Core 2.1 och senare versioner ✔️ .NET Framework 4.5 och senare versioner
.NET-program kan instrumenteras med hjälp av API:et System.Diagnostics.Activity för att producera distribuerad spårningstelemetri. En del instrumentation är inbyggd i .NET-standardbibliotek, men du kanske vill lägga till mer för att göra koden enklare att diagnostisera. I den här handledningen lägger du till nya anpassade instrument för distribuerad spårning. Se samlingsguiden för att lära dig mer om att spela in telemetrin som produceras av den här instrumentationen.
Förutsättningar
- .NET Core 2.1 SDK eller en senare version
Skapa en första app
Först skapar du en exempelapp som samlar in telemetri med OpenTelemetry, men som ännu inte har någon instrumentation.
dotnet new console
Program som riktar in sig på .NET 5 och senare har redan de nödvändiga distribuerade spårnings-API:erna inkluderade. För appar som riktar sig till äldre .NET-versioner lägger du till NuGet-paketet System.Diagnostics.DiagnosticSource version 5 eller senare. För bibliotek som är inriktade på netstandard rekommenderar vi att du refererar till den äldsta versionen av paketet som fortfarande stöds och innehåller de API:er som biblioteket behöver.
dotnet add package System.Diagnostics.DiagnosticSource
Lägg till NuGet-paketen OpenTelemetry och OpenTelemetry.Exporter.Console , som används för att samla in telemetrin.
dotnet add package OpenTelemetry
dotnet add package OpenTelemetry.Exporter.Console
Ersätt innehållet i den genererade Program.cs med den här exempelkällan:
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);
        }
    }
}
Appen har ingen instrumentation ännu, så det finns ingen spårningsinformation att visa:
> dotnet run
Example work done
Metodtips
Endast apputvecklare behöver referera till ett valfritt bibliotek från tredje part för att samla in den distribuerade spårningstelemetrin, till exempel OpenTelemetry i det här exemplet. .NET-biblioteksförfattare kan uteslutande förlita sig på API:er i System.Diagnostics.DiagnosticSource, som är en del av .NET-körningen. Detta säkerställer att bibliotek körs i en mängd olika .NET-appar, oavsett apputvecklarens inställningar för vilket bibliotek eller vilken leverantör som ska användas för insamling av telemetri.
Lägg till grundläggande instrumentation
Program och bibliotek lägger till distribuerade spårningsinstrumentation med hjälp av klasserna System.Diagnostics.ActivitySource och System.Diagnostics.Activity .
ActivitySource
Skapa först en instans av ActivitySource. ActivitySource tillhandahåller API:er för att skapa och starta aktivitetsobjekt. Lägg till variabeln static ActivitySource ovan Main() och using System.Diagnostics; till direktiven using .
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)
        {
            // ...
Metodtips
- Skapa ActivitySource en gång, lagra den i en statisk variabel och använd den instansen så länge som det behövs. Varje bibliotek eller biblioteksunderkomponent kan (och bör ofta) skapa en egen källa. Överväg att skapa en ny källa i stället för att återanvända en befintlig om du förväntar dig att apputvecklare skulle uppskatta att kunna aktivera och inaktivera telemetrin Aktivitet i källorna oberoende av varandra. 
- Källnamnet som skickas till konstruktorn måste vara unikt för att undvika konflikter med andra källor. Om det finns flera källor inom samma sammansättning använder du ett hierarkiskt namn som innehåller sammansättningsnamnet och eventuellt ett komponentnamn, - Microsoft.AspNetCore.Hostingtill exempel . Om en sammansättning lägger till instrumentation för kod i en andra oberoende sammansättning bör namnet baseras på den sammansättning som definierar ActivitySource, inte den sammansättning vars kod instrumenteras.
- Versionsparametern är valfri. Vi rekommenderar att du anger versionen om du släpper flera versioner av biblioteket och gör ändringar i den instrumenterade telemetrin. 
Anmärkning
OpenTelemetry använder alternativa termer "Tracer" och "Span". I .NET "ActivitySource" är implementeringen av Tracer och Activity är implementeringen av "Span". .NET:s aktivitetstyp har funnits mycket längre än OpenTelemetry-specifikationen och den ursprungliga .NET-namngivningen har bevarats för att upprätthålla konsekvens inom ekosystemet och säkerställa kompatibilitet med .NET-applikationer.
Aktivitet
Använd ActivitySource-objektet för att starta och stoppa aktivitetsobjekt runt meningsfulla arbetsenheter. Uppdatera DoSomeWork() med koden som visas här:
static async Task DoSomeWork(string foo, int bar)
{
    using (Activity activity = source.StartActivity("SomeWork"))
    {
        await StepOne();
        await StepTwo();
    }
}
När appen körs visas nu den nya aktiviteten som loggas:
> 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
Noteringar
- ActivitySource.StartActivity skapar och startar aktiviteten samtidigt. Det angivna kodmönstret använder - usingblocket, som automatiskt bortskaffar det skapade aktivitetsobjektet när blocket har körts. Om du tar bort aktivitetsobjektet stoppas det så att koden inte uttryckligen behöver anropa Activity.Stop(). Det förenklar kodningsmönstret.
- ActivitySource.StartActivity bestämmer internt om det finns några lyssnare som spelar in Aktiviteten. Om det inte finns några registrerade lyssnare eller om det finns lyssnare som inte är intresserade, - StartActivity()returneras- nulloch undviker att skapa aktivitetsobjektet. Det här är en prestandaoptimering så att kodmönstret fortfarande kan användas i funktioner som anropas ofta.
Valfritt: Fyll i taggar
Aktiviteter stöder nyckelvärdesdata som kallas Taggar, som ofta används för att lagra alla parametrar i arbetet som kan vara användbara för diagnostik. Uppdatera DoSomeWork() för att inkludera dem:
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
Metodtips
- Som nämnts ovan kan activitysom returneras av ActivitySource.StartActivity vara nullvärde. Operatorn?.null-coalescing i C# är en praktisk kort hand för att endast anropa Activity.SetTag omactivityinte är null. Beteendet är identiskt med att skriva:
if(activity != null)
{
    activity.SetTag("foo", foo);
}
- OpenTelemetry innehåller en uppsättning rekommenderade konventioner för att ange taggar för aktiviteter som representerar vanliga typer av programarbete. 
- Om du instrumenterar funktioner med höga prestandakrav är Activity.IsAllDataRequested ett tips som anger om någon av koden som lyssnar på Aktiviteter avser att läsa extra information, till exempel Taggar. Om ingen lyssnare läser den behöver den instrumenterade koden inte spendera CPU-cykler på att fylla den. För enkelhetens skull tillämpar det här exemplet inte den optimeringen. 
Valfritt: Lägg till händelser
Händelser är tidsstämplade meddelanden som kan koppla en godtycklig ström med ytterligare diagnostikdata till Aktiviteter. Lägg till några händelser i aktiviteten:
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
Metodtips
- Händelser lagras i en minnesintern lista tills de kan överföras, vilket gör den här mekanismen endast lämplig för att registrera ett blygsamt antal händelser. För stora eller obundna händelser är det bättre att använda ett loggnings-API som fokuserar på den här uppgiften, till exempel ILogger. ILogger ser också till att loggningsinformationen blir tillgänglig oavsett om apputvecklaren väljer att använda distribuerad spårning. ILogger stöder automatisk insamling av aktiva aktivitets-ID:n så att meddelanden som loggas via api:et fortfarande kan korreleras med den distribuerade spårningen.
Valfritt: Lägg till status
Med OpenTelemetry kan varje aktivitet rapportera en status som representerar resultatet av arbetets pass/fail. .NET har ett starkt skrivet API för detta ändamål:
Värdena ActivityStatusCode representeras som antingen , UnsetOk, och Error.
Uppdatera DoSomeWork() för att ange status:
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");
    }
}
Valfritt: Lägg till ytterligare aktiviteter
Aktiviteter kan kapslas för att beskriva delar av en större arbetsenhet. Detta kan vara värdefullt kring delar av kod som kanske inte körs snabbt eller för att bättre lokalisera fel som kommer från specifika externa beroenden. Även om det här exemplet använder en aktivitet i varje metod beror det bara på att extra kod har minimerats. I ett större och mer realistiskt projekt skulle användning av en aktivitet i varje metod ge extremt utförliga spårningar, så det rekommenderas inte.
Uppdatera StepOne och StepTwo för att lägga till mer spårning kring dessa separata steg:
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
Observera att både StepOne och StepTwo innehåller ett ParentId som refererar till SomeWork. Konsolen är inte en bra visualisering av kapslade arbetsträd, men många GUI-användare som Zipkin kan visa detta som ett Gantt-schema:
Valfritt: ActivityKind
Aktiviteter har en Activity.Kind egenskap, som beskriver relationen mellan aktiviteten, dess förälder och dess barn. Som standard är alla nya aktiviteter inställda på Internal, vilket är lämpligt för Aktiviteter som är en intern åtgärd i ett program utan fjärrförälder eller underordnade. Andra typer kan anges med hjälp av typparametern på ActivitySource.StartActivity. Andra alternativ finns i System.Diagnostics.ActivityKind.
Valfritt: Länkar
När arbete utförs i batchbearbetningssystem kan en enskild aktivitet representera arbete för många olika begäranden samtidigt, som var och en har ett eget spårnings-ID. Även om Aktiviteten är begränsad till att ha en enda överordnad kan den länka till ytterligare spårnings-ID:er med hjälp av System.Diagnostics.ActivityLink. Varje ActivityLink fylls i med en ActivityContext som lagrar ID-information om aktiviteten som länkas till. ActivityContext kan hämtas från pågående aktivitetsobjekt med hjälp av Activity.Context eller så kan det parsas från serialiserad ID-information med hjälp av 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
    }
}
Till skillnad från händelser och taggar som kan läggas till på begäran måste länkar läggas till under StartActivity() och är oföränderliga efteråt.
Viktigt!
Enligt OpenTelemetry-specifikationen är den föreslagna gränsen för antalet länkar 128. Det är dock viktigt att observera att den här gränsen inte tillämpas.
