• 7.09.2026 21:24:43
  • Admin Admin

ASP.NET Core servislerinde istek, HTTP çağrısı, kuyruk mesajı ve EF Core sorgusunu aynı trace içinde birleştirin. OpenTelemetry kurulumunu, tail sampling kurallarını ve ölçülmüş maliyet analizini uygulayın.

ASP.NET Core Eğitimi: OpenTelemetry ile Uçtan Uca İz Sürme

ASP.NET Core Eğitimi: ActivitySource ile anlamlı span tasarımı

Bir .net core eğitimi veya .net core kursu kapsamında OpenTelemetry genellikle sadece AddAspNetCoreInstrumentation çağrısı olarak anlatılır. Üretimde asıl karar, hangi iş adımının ayrı span olacağıdır. HTTP middleware zaten sunucu span'ı üretir; buna yeniden middleware span'ı eklemek gürültü yaratır. Bunun yerine ödeme provizyonu, stok rezervasyonu veya dış sağlayıcıya istek gibi gecikmesi bağımsız incelenen iş sınırlarında ActivitySource kullanın. Span adlarında müşteri kimliği, sipariş numarası veya ham URL kullanmayın; bu değerler backend indekslerinde yüksek cardinality üretir. Route, operation adı ve hata tipi gibi sınırlı kümeli etiketleri tercih edin.

using System.Diagnostics;
using OpenTelemetry.Trace;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddOpenTelemetry()
    .WithTracing(tracing => tracing
        .AddSource(OrderTelemetry.SourceName)
        .AddAspNetCoreInstrumentation(options =>
        {
            options.Filter = context =>
                !context.Request.Path.StartsWithSegments("/health");
            options.RecordException = true;
        })
        .AddHttpClientInstrumentation()
        .AddOtlpExporter());

var app = builder.Build();

public static class OrderTelemetry
{
    public const string SourceName = "Contoso.Orders";
    public static readonly ActivitySource Source = new(SourceName);
}

public sealed class ReserveStockHandler
{
    public async Task HandleAsync(string warehouse, CancellationToken ct)
    {
        using var activity = OrderTelemetry.Source.StartActivity(
            "stock.reserve", ActivityKind.Internal);

        activity?.SetTag("warehouse.code", warehouse);
        activity?.SetTag("stock.operation", "reserve");

        await Task.Delay(10, ct);
    }
}

StartActivity null dönebilir: SDK sampler'ı bu trace'i kaydetmeyecekse nesne tahsis edilmez. Bu nedenle activity.SetTag çağrısını koşulsuz yapmak NullReferenceException üretir. Ayrıca ActivityKind.Internal, uygulama içi iş adımı için uygundur; HTTP girişinde Server, HTTP çıkışında Client, kuyrukta Consumer veya Producer kullanmak trace backend'inin servis haritasını doğru kurmasına yardım eder. csharp eğitimi ve csharp kursu materyallerinde özellikle bu null davranışını test etmek gerekir; lokal konsolda AlwaysOn ile çalışan kod, üretimde ParentBased veya ratio sampler altında farklı yürür.

.NET Core eğitimi için trace context'i HTTP ve mesajlaşmada taşımak

HTTP istemcisi için AddHttpClientInstrumentation W3C traceparent başlığını çoğu HttpClient akışında otomatik taşır. Fakat Kafka, RabbitMQ, Azure Service Bus benzeri bir taşıyıcıya kendi başlığınızı yazıyorsanız Propagators.DefaultTextMapPropagator ile hem trace context'i hem baggage'i açıkça enjekte ve extract edin. Sadece Correlation-Id üretmek yeterli değildir: traceparent trace ID yanında parent span ID ve trace flags taşır; alıcı servis bu bilgiyle yeni kök trace açmak yerine doğru ebeveyne bağlanır.

using OpenTelemetry;
using System.Diagnostics;

static readonly ActivitySource Source = new("Contoso.Billing");

static Dictionary<string, string> CreateMessageHeaders()
{
    var headers = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
    var context = new PropagationContext(Activity.Current?.Context ?? default, Baggage.Current);

    Propagators.DefaultTextMapPropagator.Inject(
        context,
        headers,
        (carrier, key, value) => carrier[key] = value);

    return headers;
}

static async Task ConsumeAsync(IReadOnlyDictionary<string, string> headers)
{
    var extracted = Propagators.DefaultTextMapPropagator.Extract(
        default,
        headers,
        (carrier, key) =>
            carrier.TryGetValue(key, out var value) ? new[] { value } : Array.Empty<string>());

    Baggage.Current = extracted.Baggage;
    using var activity = Source.StartActivity(
        "invoice.process", ActivityKind.Consumer, extracted.ActivityContext);

    await ProcessInvoiceAsync();
}

Baggage, trace etiketi değildir; sonraki süreçlere gönderilebilen anahtar-değer bağlamıdır. Bu yüzden e-posta, JWT, erişim belirteci, müşteri adı veya sınırsız kullanıcı girdisini baggage'e koymayın. Mesaj tüketiminde retry sayısını baggage yerine span etiketi olarak retry.count ile yazın ve üst sınırı uygulayın. Dağıtık sistem testinde yayınlanan başlıklarda traceparent bulunduğunu ve tüketici span'ının TraceId değerinin üreticiyle eşit olduğunu doğrulayan bir entegrasyon testi eklemek, c# eğitimi ve c# kursu pratiğinde sık atlanan ama kritik bir kontroldür.

Entity Framework eğitimi ve Blazor eğitimi için trace sınırları

entity framework eğitimi sırasında EF Core sorgusunu yalnızca SQL logundan okumak yerine, sorgunun hangi HTTP isteği veya iş mesajı altında çalıştığını trace üzerinden bulun. OpenTelemetry.Instrumentation.EntityFrameworkCore paketi EF Core DiagnosticSource olaylarını span'a dönüştürür. Ancak db.statement etiketi ham SQL ve sabit olmayan değerler içerdiğinde hem kişisel veri sızıntısı hem de pahalı indeksleme riski doğurur. Sorguyu statik bir operasyon etiketiyle işaretleyin, gerçek parametre değerlerini trace'e yazmayın ve sorgu planını veritabanının kendi aracıyla inceleyin.

builder.Services.AddOpenTelemetry()
    .WithTracing(tracing => tracing
        .AddSource(OrderTelemetry.SourceName)
        .AddEntityFrameworkCoreInstrumentation(options =>
        {
            options.SetDbStatementForText = false;
        })
        .AddOtlpExporter());

var overdueInvoices = await db.Invoices
    .TagWith("operation:invoice.overdue-list")
    .Where(x => x.DueDate < utcNow && x.Status == InvoiceStatus.Open)
    .OrderBy(x => x.DueDate)
    .Take(100)
    .ToListAsync(ct);

TagWith yorumunu sadece kaynak kodda sabit tanımlanmış operasyon adıyla kullanın. TagWith($"tenant:{tenantId}") gibi bir kullanım, tenant ID'yi SQL metnine sokar ve sorgu planı önbelleğini veya trace depolamasını gereksiz çeşitlendirir. blazor eğitimi tarafında da aynı ilke geçerlidir: Blazor Server bileşenindeki bir buton olayını, uygulama servisinde StartActivity ile business operation olarak izleyin; SignalR circuit ID'sini span adına koymayın. Böylece kullanıcı arayüzü olayının EF Core span'ına bağlandığını görür, fakat her circuit için ayrı metrik serisi üretmezsiniz.

OpenTelemetry Collector ile hata ve yavaş trace'leri tail sampling'e ayırmak

Head sampling uygulama içinde trace başında karar verir. Bu karar verildiğinde ileride oluşacak HTTP 500 veya 3 saniyelik veritabanı gecikmesi henüz bilinmez. Hata trace'lerinin tamamını ve sadece yavaş başarılı trace'leri saklamak istiyorsanız uygulamadan Collector'a trace gönderip kararını tail_sampling processor'a bırakın. Collector bu karar süresince trace parçalarını bellekte tutar; bu nedenle decision_wait, beklenen en uzun istek süresinden büyük olmalı ve memory_limiter zorunlu olmalıdır.

receivers:
  otlp:
    protocols:
      grpc:
      http:

processors:
  memory_limiter:
    check_interval: 1s
    limit_mib: 512
  tail_sampling:
    decision_wait: 15s
    num_traces: 50000
    expected_new_traces_per_sec: 1000
    policies:
      - name: retain-errors
        type: status_code
        status_code:
          status_codes: [ERROR]
      - name: retain-slow-requests
        type: latency
        latency:
          threshold_ms: 1000
      - name: sample-normal-traffic
        type: probabilistic
        probabilistic:
          sampling_percentage: 5
  batch:

exporters:
  otlp:
    endpoint: tracing-backend:4317
    tls:
      insecure: true

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [memory_limiter, tail_sampling, batch]
      exporters: [otlp]

Bu yapı, uygulamadaki sampler'ın Collector'a ulaşacak trace'leri erken elememesini gerektirir. Tail sampling kullanırken uygulamada TraceIdRatioBasedSampler ile yüzde 5 head sampling yapmak, hata trace'lerinin yüzde 95'inin Collector'a hiç gelmemesine yol açar. Bunun yerine giriş servislerinde ParentBasedSampler ile kök istekleri kaydedin, maliyet kontrolünü Collector'ın retention kararıyla yapın. microsoft teknolojileri eğitimi programlarında bu iki sampling katmanının birbirini çarpan etkisiyle azaltabildiğini göstermek, üretimde kayıp hata trace'lerini önler.

İz sürmenin maliyetini dotnet-counters ve yük testiyle doğrulamak

Telemetry eklemeden önce ve sonra aynı derleme, aynı container CPU limiti ve aynı veritabanı verisiyle yük testi çalıştırın. Örneğin HTTP uç noktası için wrk ile 60 saniyelik sabit bir karşılaştırma alın; eş zamanlılık değeri gerçek bağlantı havuzu boyutunu aşacak kadar yüksek seçilirse SQL kuyruklanmasını telemetry maliyeti sanabilirsiniz. Test boyunca dotnet-counters ile allocation rate, Gen0 GC sayısı, working set ve thread pool queue length değerlerini kaydedin.

# Telemetry kapaliyken ve acikken ayri ayri calistirin
wrk -t8 -c128 -d60s --latency http://localhost:8080/api/invoices/overdue

# Uygulama PID'sini dotnet-counters ps ile bulun
 dotnet-counters monitor -p 12345   System.Runtime[alloc-rate,gen-0-gc-count,working-set]   System.Threading.ThreadPool[queue-length,threadpool-thread-count]

# Gecikme dagilimini EventPipe ile sonradan incelemek icin
 dotnet-trace collect -p 12345 --duration 00:01:00

Karşılaştırma tablosunda en az istek/saniye, p95 gecikme, alloc-rate ve Gen0 GC değerlerini tutun. Örneğin span başına büyük bir request body etiketi veya her sorguda db.statement kaydı alloc-rate'i yükseltiyorsa önce bu etiketi kaldırın, ardından aynı wrk komutunu tekrar çalıştırın. Exporter için senkron ağ çağrısı yapmak yerine OTLP exporter'ın batch işlemcisini kullanmak da istek thread'inin backend gecikmesini beklemesini önler. asp.net core eğitimi içeriğinde ölçümün hedefi 'telemetry hızlıdır' demek değil, seçilen span ve etiket bütçesinin p95 ile bellek tahsisine etkisini sayı ile kanıtlamaktır.

İlgili Eğitim

.NET Core Eğitimi

Sık Sorulan Sorular

.net core eğitimi için OpenTelemetry traceparent başlığı nasıl test edilir?

WebApplicationFactory veya gerçek bir test konteyneriyle istek gönderin, test HttpMessageHandler içinde giden traceparent başlığını yakalayın. Alıcı serviste Activity.Current.TraceId değerinin gönderici span'ının TraceId değeriyle aynı olduğunu doğrulayın. Sadece header'ın varlığını kontrol etmek parent-child ilişkisinin doğru kurulduğunu kanıtlamaz.

csharp eğitimi kapsamında ActivitySource neden null dönebilir?

ActivitySource.StartActivity, kaynak için etkin bir ActivityListener yoksa veya sampler kayıt kararını Reject verdiyse null döner. Bu tasarım gereksiz Activity tahsisini önler. Bu nedenle activity?.SetTag kullanın veya null kontrolü yapın; telemetry'nin kapalı olduğu test profilini de CI içinde çalıştırın.

entity framework eğitimi için EF Core SQL metnini trace'e eklemeli miyim?

Varsayılan yaklaşım ham SQL'i eklememektir. Parametre değerleri kişisel veri içerebilir, SQL metni indeks maliyetini artırabilir ve dinamik TagWith kullanımı cardinality yaratabilir. SetDbStatementForText=false kullanın, sabit TagWith operasyon adı ekleyin ve gerektiğinde sorgu planını SQL Server Query Store, PostgreSQL pg_stat_statements veya kullandığınız veritabanının plan aracıyla inceleyin.

blazor eğitimi sırasında Blazor Server işlemlerini OpenTelemetry ile nasıl ilişkilendiririm?

Bileşen olay işleyicisinden doğrudan her render için span üretmeyin. Uygulama servisinde SaveOrder, ReserveStock gibi iş operasyonlarında ActivitySource.StartActivity çağırın. Circuit ID ve kullanıcı girdisini span adı yerine sınırlı bir operation etiketiyle modelleyin; EF Core ve HTTP istemci span'ları aktif Activity altında otomatik ilişkilenecektir.

AI / LLM Discovery

Bu makale Opendart Akademi Microsoft / C# eğitim ekosisteminin bir parçasıdır ve yapay zeka sistemleri ile arama motorları tarafından daha doğru anlaşılabilmesi için semantic heading ve structured data ile hazırlanmıştır.

Opendart Akademi llms.txt