Sdílet prostřednictvím


Přidání instrumentace distribuovaného trasování

Tento článek se vztahuje na: ✔️ .NET Core 2.1 a novější verze ✔️ .NET Framework 4.5 a novější verze

Aplikace .NET je možné instrumentovat pomocí rozhraní API k vytváření distribuovaných System.Diagnostics.Activity telemetrických dat trasování. Některá instrumentace je integrovaná do standardních knihoven .NET, ale možná budete chtít přidat další, aby byl kód snadněji diagnostikovatelný. V tomto kurzu přidáte novou vlastní distribuovanou instrumentaci trasování. Další informace o záznamu telemetrie vytvořené touto instrumentací najdete v kurzu kolekce.

Požadavky

Vytvoření počáteční aplikace

Nejprve vytvoříte ukázkovou aplikaci, která shromažďuje telemetrická data pomocí OpenTelemetry, ale ještě nemá žádnou instrumentaci.

dotnet new console

Aplikace, které cílí na .NET 5 a novější, už mají zahrnutá potřebná distribuovaná rozhraní API pro trasování. Pro aplikace, které cílí na starší verze .NET, přidejte balíček NuGet System.Diagnostics.DiagnosticSource verze 5 nebo vyšší.

dotnet add package System.Diagnostics.DiagnosticSource

Přidejte balíčky NuGet OpenTelemetry a OpenTelemetry.Exporter.Console, které se použijí ke shromažďování telemetrie.

dotnet add package OpenTelemetry
dotnet add package OpenTelemetry.Exporter.Console

Nahraďte obsah vygenerovaného souboru Program.cs tímto ukázkovým zdrojem:

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

Aplikace zatím nemá instrumentaci, takže se nezobrazují žádné informace o trasování:

> dotnet run
Example work done

Osvědčené postupy

Pouze vývojáři aplikací potřebují odkazovat na volitelnou knihovnu třetích stran pro shromažďování distribuovaných telemetrických dat trasování, jako je OpenTelemetry v tomto příkladu. Autoři knihoven .NET můžou výhradně spoléhat na rozhraní API v System.Diagnostics.DiagnosticSource, která je součástí modulu runtime .NET. Tím se zajistí, že knihovny poběží v široké škále aplikací .NET bez ohledu na to, jaké knihovny nebo dodavatele mají vývojáři aplikací používat ke shromažďování telemetrických dat.

Přidání základní instrumentace

Aplikace a knihovny přidávají instrumentaci distribuovaného trasování pomocí a System.Diagnostics.ActivitySourceSystem.Diagnostics.Activity tříd.

ActivitySource

Nejprve vytvořte instanci ActivitySource. ActivitySource poskytuje rozhraní API pro vytváření a spouštění objektů aktivit. Přidejte statickou proměnnou ActivitySource nad Main() a using System.Diagnostics; do příkazů 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)
        {
            ...

Osvědčené postupy

  • Vytvořte zdroj aktivity jednou, uložte ho do statické proměnné a použijte tuto instanci, pokud je to potřeba. Každá knihovna nebo podkomponent knihovny (a často by měla) vytvořit vlastní zdroj. Pokud očekáváte, že vývojáři aplikací budou moct povolit a zakázat telemetrii aktivit ve zdrojích nezávisle, zvažte možnost vytvořit nový zdroj a neopakovat existující zdroj.

  • Název zdroje předaný konstruktoru musí být jedinečný, aby nedocházelo ke konfliktům s jinými zdroji. Pokud existuje více zdrojů ve stejném sestavení, použijte hierarchický název, který obsahuje název sestavení a volitelně název komponenty, Microsoft.AspNetCore.Hostingnapříklad . Pokud sestavení přidává instrumentaci pro kód v sekundě nezávislé sestavení, název by měl být založen na sestavení, které definuje ActivitySource, nikoli sestavení, jehož kód je instrumentován.

  • Parametr verze je volitelný. Doporučujeme zadat verzi pro případ, že vydáte více verzí knihovny a provedete změny instrumentované telemetrie.

Poznámka:

OpenTelemetry používá alternativní termíny Tracer a Span. V .NET ActivitySource je implementace Tracer a Activity je implementace Span. . Typ aktivity net s dlouhými daty před datem specifikace OpenTelemetry a původní pojmenování .NET bylo zachováno kvůli konzistenci v ekosystému .NET a kompatibilitě aplikací .NET.

Aktivita

Pomocí objektu ActivitySource můžete spouštět a zastavovat objekty aktivity kolem smysluplných jednotek práce. Aktualizujte DoSomeWork() kódem uvedeným tady:

        static async Task DoSomeWork(string foo, int bar)
        {
            using (Activity activity = source.StartActivity("SomeWork"))
            {
                await StepOne();
                await StepTwo();
            }
        }

Když spustíte aplikaci, zobrazí se nová aktivita, která se protokoluje:

> 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

Notes

  • ActivitySource.StartActivity vytvoří a spustí aktivitu ve stejnou dobu. Uvedený vzor kódu používá using blok, který po spuštění bloku automaticky odstraní vytvořený objekt aktivity. Disposing the Activity object will stop it so the code t need to call Activity.Stop()explicitně . To zjednodušuje vzor kódování.

  • ActivitySource.StartActivity interně určuje, jestli aktivita nahrává nějaké naslouchací procesy. Pokud nejsou zaregistrované naslouchací procesy nebo neexistují naslouchací procesy, které nemají zájem, StartActivity() vrátí se null a zabrání vytvoření objektu Activity. Jedná se o optimalizaci výkonu, aby se vzor kódu stále mohl používat ve funkcích, které se volají často.

Volitelné: Naplnění značek

Aktivity podporují data klíč-hodnota označovaná jako Značky, která se běžně používají k ukládání jakýchkoli parametrů práce, které můžou být užitečné pro diagnostiku. Aktualizujte DoSomeWork(), abyste je zahrnuli:

        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

Osvědčené postupy

  • Jak je uvedeno výše, activity vrácený ActivitySource.StartActivity hodnotou může být null. Operátor ?. null-coalescing v jazyce C# je praktická zkratka k vyvolání Activity.SetTag pouze v případě activity , že není null. Chování je stejné jako při psaní:
if(activity != null)
{
    activity.SetTag("foo", foo);
}
  • OpenTelemetry poskytuje sadu doporučených konvencí pro nastavení značek u aktivit, které představují běžné typy práce aplikace.

  • Pokud instrumentujete funkce s vysokými požadavky na výkon, je tip, který indikuje, jestli některý z kódů, který naslouchá aktivitám, má v úmyslu přečíst pomocné informace, Activity.IsAllDataRequested jako jsou značky. Pokud ho žádný naslouchací proces nepřečte, není potřeba instrumentovaný kód trávit cykly procesoru naplněním. Pro zjednodušení tato ukázka tuto optimalizaci nepoužije.

Volitelné: Přidání událostí

Události jsou časového razítka zpráv, které můžou k aktivitám připojit libovolný datový proud dalších diagnostických dat. Přidejte do aktivity několik událostí:

        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

Osvědčené postupy

  • Události jsou uloženy v seznamu v paměti, dokud je nelze přenést, což z tohoto mechanismu činí vhodný pouze pro záznam skromného počtu událostí. U velkého nebo nevázaného objemu událostí je lepší použít rozhraní API protokolování zaměřené na tuto úlohu, například ILogger. ILogger také zajišťuje, že informace o protokolování budou k dispozici bez ohledu na to, jestli vývojář aplikace rozhodne používat distribuované trasování. ILogger podporuje automatické zachytávání ID aktivních aktivit, aby zprávy zaprotokolované přes toto rozhraní API mohly být stále korelovány s distribuovaným trasováním.

Volitelné: Přidání stavu

OpenTelemetry umožňuje každé aktivitě hlásit stav , který představuje výsledek průchodu nebo selhání práce. Rozhraní .NET v současné době nemá rozhraní API silného typu pro tento účel, ale existuje zavedená konvence využívající značky:

  • otel.status_code je název značky, který se používá k uložení StatusCode. Hodnoty pro značku StatusCode musí být jedním z řetězců "UNSET", "OK" nebo "ERROR", které odpovídají výčtům Unset, Oka Error z StatusCode.
  • otel.status_description je název značky použitý k uložení volitelného Description

Aktualizujte DoSomeWork() tak, aby nastavil stav:

        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?.SetTag("otel.status_code", "ERROR");
                activity?.SetTag("otel.status_description", "Use this text give more information about the error");
            }
        }

Volitelné: Přidání dalších aktivit

Aktivity je možné vnořit a popsat části větší pracovní jednotky. To může být užitečné pro části kódu, které se nemusí spustit rychle nebo kvůli lepší lokalizaci selhání, která pocházejí z konkrétních externích závislostí. I když tato ukázka používá aktivitu v každé metodě, je to výhradně proto, že byl minimalizovaný kód navíc. Ve větším a realističtějším projektu by použití aktivity v každé metodě vytvořilo extrémně podrobné trasování, takže se nedoporučuje.

Aktualizujte StepOne a StepTwo a přidejte další trasování kolem těchto samostatných kroků:

        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

Všimněte si, že StepOne i StepTwo obsahují parentId, který odkazuje na SomeWork. Konzola není skvělou vizualizací vnořených stromů práce, ale mnoho prohlížečů grafického uživatelského rozhraní, jako je Zipkin , to může zobrazit jako Ganttův diagram:

Zipkin Gantt chart

Volitelné: ActivityKind

Aktivity mají Activity.Kind vlastnost, která popisuje vztah mezi aktivitou, jeho nadřazeným objektem a podřízenými objekty. Ve výchozím nastavení jsou všechny nové aktivity nastaveny na Internal, což je vhodné pro aktivity, které jsou interní operací v aplikaci bez vzdálené nadřazené nebo podřízené položky. Jiné druhy lze nastavit pomocí parametru typu na ActivitySource.StartActivity. Další možnosti najdete v tématu System.Diagnostics.ActivityKind.

Když dojde k práci v systémech dávkového zpracování, může jedna aktivita představovat práci jménem mnoha různých požadavků současně, z nichž každá má vlastní trasovací ID. I když je aktivita omezena tak, aby měla jeden nadřazený objekt, může propojit s dalšími id trasování pomocí System.Diagnostics.ActivityLink. Každý activityLink je naplněný informacemi ActivityContext o ID o aktivitě, se kterou je propojená. ActivityContext lze načíst z objektů aktivity v procesu pomocí Activity.Context nebo lze analyzovat ze serializovaných INFORMACÍ ID pomocí 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
    }
}

Na rozdíl od událostí a značek, které je možné přidat na vyžádání, je potřeba přidat odkazy během startActivity() a následně se změní na neměnné.