Dağıtılmış izleme izleme izlemesi ekleme
Bu makale şunlar için geçerlidir: ✔️ .NET Core 2.1 ve sonraki sürümleri✔️ .NET Framework 4.5 ve sonraki sürümleri
.NET uygulamaları, dağıtılmış izleme telemetrisi System.Diagnostics.Activity oluşturmak için API kullanılarak izlenebilir. Bazı izlemeler standart .NET kitaplıklarında yerleşiktir, ancak kodunuzu daha kolay tanınabilir hale getirmek için daha fazlasını eklemek isteyebilirsiniz. Bu öğreticide, yeni özel dağıtılmış izleme izleme araçları ekleyebilirsiniz. Bu izleme tarafından oluşturulan telemetriyi kaydetme hakkında daha fazla bilgi edinmek için koleksiyon öğreticisine bakın.
Önkoşullar
- .NET Core 2.1 SDK veya sonraki bir sürüm
İlk uygulamayı oluşturma
İlk olarak, OpenTelemetry kullanarak telemetri toplayan ancak henüz herhangi bir izlemesi olmayan örnek bir uygulama oluşturacaksınız.
dotnet new console
.NET 5 ve üzerini hedefleyen uygulamalar zaten gerekli dağıtılmış izleme API'lerine sahiptir. Eski .NET sürümlerini hedefleyen uygulamalar için System.Diagnostics.DiagnosticSource NuGet paketi sürüm 5 veya üzerini ekleyin.
dotnet add package System.Diagnostics.DiagnosticSource
Telemetriyi toplamak için kullanılacak OpenTelemetry ve OpenTelemetry.Exporter.Console NuGet paketlerini ekleyin.
dotnet add package OpenTelemetry
dotnet add package OpenTelemetry.Exporter.Console
Oluşturulan Program.cs dosyasının içeriğini şu örnek kaynakla değiştirin:
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);
}
}
}
Uygulama henüz izleme özelliğine sahip olmadığından görüntülenecek izleme bilgisi yoktur:
> dotnet run
Example work done
En iyi yöntemler
Bu örnekteki OpenTelemetry gibi dağıtılmış izleme telemetrisini toplamak için yalnızca uygulama geliştiricilerin isteğe bağlı bir üçüncü taraf kitaplığına başvurması gerekir. .NET kitaplık yazarları, .NET çalışma zamanının bir parçası olan System.Diagnostics.DiagnosticSource'taki API'leri özel olarak kullanabilir. Bu, uygulama geliştiricisinin telemetri toplamak için hangi kitaplığı veya satıcıyı kullanacağı konusunda tercihlerinden bağımsız olarak kitaplıkların çok çeşitli .NET uygulamalarında çalışmasını sağlar.
Temel izleme ekleme
Uygulamalar ve kitaplıklar ve System.Diagnostics.Activity sınıflarını kullanarak System.Diagnostics.ActivitySource dağıtılmış izleme izleme araçları ekler.
ActivitySource
İlk olarak activitysource örneğini oluşturun. ActivitySource, Activity nesnelerini oluşturmak ve başlatmak için API'ler sağlar. Main() öğesinin üzerine ve using System.Diagnostics;
using deyimlerine static ActivitySource değişkenini ekleyin.
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)
{
...
En iyi yöntemler
ActivitySource'u bir kez oluşturun, statik değişkende depolayın ve gerektiğinde bu örneği kullanın. Her kitaplık veya kitaplık alt bileşeni kendi kaynağını oluşturabilir (ve genellikle oluşturmalıdır). Uygulama geliştiricilerinin kaynaklarda Etkinlik telemetrisini bağımsız olarak etkinleştirip devre dışı bırakabilmeyi takdir edeceğini düşünüyorsanız, mevcut bir kaynağı yeniden kullanmak yerine yeni bir kaynak oluşturmayı göz önünde bulundurun.
Oluşturucuya geçirilen kaynak adı, diğer kaynaklarla çakışmaları önlemek için benzersiz olmalıdır. Aynı derleme içinde birden çok kaynak varsa, derleme adını ve isteğe bağlı olarak bir bileşen adını içeren hiyerarşik bir ad kullanın. Örneğin,
Microsoft.AspNetCore.Hosting
. Bir derleme ikinci ve bağımsız bir derlemede kod için izleme ekliyorsa, adın kodu izlenen derlemeyi değil ActivitySource'u tanımlayan derlemeyi temel alması gerekir.Sürüm parametresi isteğe bağlıdır. Kitaplığın birden çok sürümünü serbest bırakmanız ve izlemeli telemetride değişiklik yapmanız durumunda sürümü sağlamanızı öneririz.
Dekont
OpenTelemetry, alternatif 'tracer' ve 'Span' terimlerini kullanır. .NET'te 'ActivitySource', İzleme'nin uygulanmasıdır ve Etkinlik ise 'Span' uygulamasıdır. . NET'in Etkinlik türü, OpenTelemetry belirtimi ve özgün .NET adlandırmasının uzun ön tarihlerini .NET ekosistemi ve .NET uygulama uyumluluğu içinde tutarlılık sağlamak için korunmaktadır.
Etkinlik
Etkinlik nesnelerini anlamlı iş birimleri etrafında başlatmak ve durdurmak için ActivitySource nesnesini kullanın. DoSomeWork() öğesini burada gösterilen kodla güncelleştirin:
static async Task DoSomeWork(string foo, int bar)
{
using (Activity activity = source.StartActivity("SomeWork"))
{
await StepOne();
await StepTwo();
}
}
Uygulamayı çalıştırdığınızda artık günlüğe kaydedilen yeni Etkinlik gösterilir:
> 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
Notlar
ActivitySource.StartActivity etkinliği aynı anda oluşturur ve başlatır. Listelenen kod düzeni, bloğu yürütürken oluşturulan Activity nesnesini otomatik olarak atan bloğunu kullanıyor
using
. Activity nesnesinin atılması, kodun açıkça çağırması Activity.Stop()gerekmemesi için nesnesini durdurur. Bu, kodlama düzenini basitleştirir.ActivitySource.StartActivity etkinliği kaydeden dinleyici olup olmadığını dahili olarak belirler. Kayıtlı dinleyici yoksa veya ilgilenmeyen dinleyiciler varsa,
StartActivity()
geri dönernull
ve Activity nesnesini oluşturmaktan kaçının. Bu, kod deseninin sık çağrılan işlevlerde kullanılmaya devam edebilmesi için bir performans iyileştirmesidir.
İsteğe bağlı: Etiketleri doldurma
Etkinlikler, işin tanılama için yararlı olabilecek parametrelerini depolamak için yaygın olarak kullanılan Etiketler adlı anahtar-değer verilerini destekler. DoSomeWork() öğesini bunları içerecek şekilde güncelleştirin:
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
En iyi yöntemler
- Yukarıda belirtildiği gibi,
activity
tarafından ActivitySource.StartActivity döndürülen null olabilir. C# içindeki null birleşim işleci?.
, yalnızca null değilseactivity
çağırmak Activity.SetTag için kullanışlı bir kısa eldir. Davranış, yazma işlemiyle aynıdır:
if(activity != null)
{
activity.SetTag("foo", foo);
}
OpenTelemetry, yaygın uygulama çalışması türlerini temsil eden Etkinliklerde Etiketler'i ayarlamak için bir dizi önerilen kural sağlar.
İşlevleri yüksek performanslı gereksinimlerle izliyorsanız, Activity.IsAllDataRequested Etkinlikler'i dinleyen kodlardan herhangi birinin Etiketler gibi yardımcı bilgileri okumayı amaçlayıp amaçlamadığını gösteren bir ipucudur. Hiçbir dinleyici okumazsa, araçlı kodun CPU döngülerini doldurarak harcamasına gerek yoktur. Kolaylık olması için bu örnek bu iyileştirmeyi uygulamaz.
İsteğe bağlı: Olay ekleme
Olaylar, Etkinliklere rastgele ek tanılama verileri akışı ekleyebilen zaman damgasına sahip iletilerdir. Etkinliğe bazı olaylar ekleyin:
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
En iyi yöntemler
- Olaylar, iletilene kadar bellek içi bir listede depolanır ve bu da bu mekanizmayı yalnızca mütevazı sayıda olayın kaydedilmesi için uygun hale getirir. Büyük veya ilişkisiz bir olay hacmi için, ILogger gibi bu göreve odaklanan bir günlük API'sini kullanmak daha iyidir. ILogger ayrıca, uygulama geliştiricisinin dağıtılmış izlemeyi kullanmayı tercih edip etmediğine bakılmaksızın günlük bilgilerinin kullanılabilir olmasını sağlar. ILogger, etkin Etkinlik Kimliklerinin otomatik olarak yakalanmasını destekler, böylece bu API aracılığıyla günlüğe kaydedilen iletiler dağıtılmış izlemeyle yine de ilişkilendirilebilir.
İsteğe bağlı: Durum ekle
OpenTelemetry, her Etkinliğin çalışmanın başarılı/başarısız sonucunu temsil eden bir Durum bildirmesine olanak tanır. .NET şu anda bu amaçla kesin olarak belirlenmiş bir API'ye sahip değildir, ancak Etiketler kullanılarak oluşturulmuş bir kural vardır:
otel.status_code
, depolamakStatusCode
için kullanılan Etiket adıdır. StatusCode etiketinin değerleri sırasıylaUnset
Ok
, veError
StatusCode'dan gelen "UNSET", "Ok" veya "ERROR" dizelerinden biri olmalıdır.otel.status_description
isteğe bağlı olarak depolamak için kullanılan Etiket adıdırDescription
Durumu ayarlamak için DoSomeWork() öğesini güncelleştirin:
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");
}
}
İsteğe bağlı: Ek Etkinlikler ekleme
Etkinlikler, daha büyük bir iş biriminin bölümlerini açıklamak için iç içe yerleştirilebilir. Bu, kodun hızlı yürütülmeyebilecek bölümleri veya belirli dış bağımlılıklardan gelen hataları daha iyi yerelleştirmek için değerli olabilir. Bu örnek her yöntemde bir Etkinlik kullansa da, bunun nedeni yalnızca ek kodun simge durumuna küçültülmüş olmasıdır. Daha büyük ve daha gerçekçi bir projede, her yöntemde bir Etkinlik kullanmak son derece ayrıntılı izlemeler üreteceğinden önerilmez.
Bu ayrı adımlar etrafında daha fazla izleme eklemek için StepOne ve StepTwo'yi güncelleştirin:
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
Hem StepOne hem de StepTwo'da SomeWork'e başvuran bir ParentId olduğuna dikkat edin. Konsol, iç içe geçmiş iş ağaçlarının harika bir görselleştirmesi değildir, ancak Zipkin gibi birçok GUI görüntüleyici bunu Gantt grafiği olarak gösterebilir:
İsteğe bağlı: ActivityKind
Etkinlikler, Etkinlik, üst öğesi ve alt öğeleri arasındaki ilişkiyi açıklayan bir Activity.Kind özelliğe sahiptir. Varsayılan olarak, tüm yeni Etkinlikler olarak ayarlanır Internalve bu, uzak üst veya alt öğe içermeyen bir uygulama içindeki iç işlem olan Etkinlikler için uygundur. Üzerinde kind parametresi ActivitySource.StartActivitykullanılarak başka türlerde ayarlanabilir. Diğer seçenekler için bkz System.Diagnostics.ActivityKind. .
İsteğe bağlı: Bağlantılar
Toplu işleme sistemlerinde iş gerçekleştiğinde, tek bir Etkinlik aynı anda birçok farklı istek adına çalışmayı temsil edebilir ve bunların her birinin kendi trace-id'si vardır. Etkinlik tek bir üst öğeye sahip olacak şekilde kısıtlanmış olsa da, kullanarak System.Diagnostics.ActivityLinkek trace-id'lere bağlanabilir. Her ActivityLink, bağlanılmakta olan Etkinlikle ilgili kimlik bilgilerini depolayan bir ActivityContext ile doldurulur. ActivityContext, kullanarak Activity.Context işlem içi Etkinlik nesnelerinden alınabilir veya kullanılarak ActivityContext.Parse(String, String)serileştirilmiş kimlik bilgilerinden ayrıştırılabilir.
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
}
}
İsteğe bağlı olarak eklenebilen olayların ve Etiketlerin aksine, StartActivity() sırasında bağlantılar eklenmelidir ve daha sonra sabittir.