Vận hành Trung bình 5 phút đọc

OpenTelemetry trong .NET: trace, metric, log

Cách cấu hình OpenTelemetry vào ASP.NET Core cho distributed trace, metric, và structured log. Pipeline instrumentation duy nhất chạy mọi alert trong service.

Mục lục
  1. Khi nào observability không thể thương lượng nữa?
  2. Ngân sách số nào cho tier observability?
  3. Pipeline observability tối thiểu trông thế nào?
  4. Cấu hình .NET 10 cho OpenTelemetry?
  5. Trace và metric tuỳ chỉnh nào nên thêm?
  6. Biến observability thành alert hữu dụng ra sao?
  7. Bản thân observability tạo failure mode nào?
  8. Khi nào đầu tư observability là sớm?
  9. Đi tiếp đâu từ đây?

Lần đầu phải debug sự cố production mà không có observability, bạn hiểu vì sao mọi chương trong series đều trích metric. Không trace, metric, log, bạn đoán mò. Có chúng, post-mortem tự viết. Chương này cấu hình OpenTelemetry vào ASP.NET Core theo hình đã thành chuẩn 2026.

Khi nào observability không thể thương lượng nữa?

Ba tín hiệu.

Service ở production. Dù chỉ một khách hàng, outage tốn danh tiếng. Không metric thì không alert; không trace thì không sửa; không log thì không giải thích.

Service có hơn một instance. Log file local trên mỗi máy hết chạy ngay khi request trải qua replica. Cần pipeline tập trung.

Service phụ thuộc service khác. Checkout chậm hoá ra do provider thanh toán third-party chậm mất nhiều giờ chẩn đoán nếu không có distributed trace, vài phút khi có.

Nếu code là script một lần chạy và bạn đọc console, không cần chương này. Còn lại, có.

Ngân sách số nào cho tier observability?

Tín hiệu    Khối lượng/req      Yếu tố chi phí           Storage
Metric      ~hằng số             số label unique          rẻ
Trace       1 trace              tỉ lệ sampling           đắt
Log         ~5-20 dòng           kích thước + số dòng     vừa

Cho service 10K QPS:

Đừng log mức INFO trên path nóng. WARN trở lên cho steady state; DEBUG chỉ khi điều tra.

Pipeline observability tối thiểu trông thế nào?

flowchart LR
    App[ASP.NET Core] -->|OTel SDK| Collector[OTel Collector]
    Collector -->|metric| Prom[(Prometheus)]
    Collector -->|trace| Jaeger[(Jaeger / Tempo)]
    Collector -->|log| Loki[(Loki / OpenSearch)]
    Prom --> Grafana
    Jaeger --> Grafana
    Loki --> Grafana
    Grafana --> Alert[Alert -> PagerDuty]

App emit qua OTel; Collector demultiplex theo loại tín hiệu vào backend chuyên dụng; Grafana là dashboard thống nhất. Cùng pipeline hỗ trợ Datadog, Honeycomb, Azure Monitor - đổi backend không đụng code app.

Cấu hình .NET 10 cho OpenTelemetry?

Một block trong Program.cs:

builder.Services.AddOpenTelemetry()
    .ConfigureResource(r => r.AddService(
        serviceName: builder.Environment.ApplicationName,
        serviceVersion: Assembly.GetExecutingAssembly().GetName().Version?.ToString()))
    .WithTracing(t => t
        .AddAspNetCoreInstrumentation()
        .AddHttpClientInstrumentation()
        .AddEntityFrameworkCoreInstrumentation()
        .AddSource("MyService.*")
        .SetSampler(new TraceIdRatioBasedSampler(0.01))   // 1% head-based
        .AddOtlpExporter())
    .WithMetrics(m => m
        .AddAspNetCoreInstrumentation()
        .AddHttpClientInstrumentation()
        .AddRuntimeInstrumentation()                      // GC, thread pool
        .AddProcessInstrumentation()
        .AddMeter("MyService.*")
        .AddOtlpExporter());

// Log qua Serilog với OTel sink
builder.Host.UseSerilog((ctx, lc) => lc
    .ReadFrom.Configuration(ctx.Configuration)
    .Enrich.FromLogContext()
    .WriteTo.Console()
    .WriteTo.OpenTelemetry(o => o.Endpoint = "http://collector:4317"));

Ba chi tiết. Instrumentation ASP.NET Core mặc định cho bạn cả phương pháp RED không cần code. Instrumentation EF Core gồm SQL - lọc PII trước khi export. Sampling 1% head-based là default tiết kiệm nhất; kết hợp với tail sampling trên error cho trace error giàu hơn.

Trace và metric tuỳ chỉnh nào nên thêm?

Ba loại đáng công:

// Span tuỳ chỉnh quanh thao tác nghiệp vụ
using var activity = ActivitySource.StartActivity("checkout.process");
activity?.SetTag("order.id", orderId);
activity?.SetTag("order.amount", amount);
// ... code nghiệp vụ

// Metric tuỳ chỉnh cho KPI nghiệp vụ
private static readonly Meter Meter = new("MyService.Business");
private static readonly Counter<long> OrdersPlaced =
    Meter.CreateCounter<long>("orders_placed_total");

OrdersPlaced.Add(1, new KeyValuePair<string, object?>("payment_method", "stripe"));

// Log structured với correlation
log.LogInformation(
    "Order {OrderId} placed by user {UserId} for {Amount:0.00}",
    orderId, userId, amount);

ActivitySource gắn span tuỳ chỉnh vào cây trace tự động; Meter lộ counter để Prometheus scrape. Log structured giữ correlation ID ASP.NET Core inject - Grafana có thể nhảy từ trace sang log của nó.

Biến observability thành alert hữu dụng ra sao?

Ba quy tắc.

Một: alert theo triệu chứng user thấy, không phải nguyên nhân nội bộ. "5xx rate trên 0.5% trong 5 phút" tốt. "Thời gian GC trên 1%" không - có thể ổn, tuỳ workload. "Symptom-based alerting" của SRE là khung đúng. Tín hiệu từ tầng cache - hit rate, miss-per-second - là loại đúng: user thấy như latency.

Hai: alert theo burn rate SLO, không ngưỡng thô. Error rate 1% ổn nếu SLO 99% trong 30 ngày; là cháy nếu SLO 99.99%. Tính burn rate so SLO và alert khi tiêu error budget quá nhanh.

Ba: mọi alert phải có runbook. Nếu engineer on-call không trả lời được "phải làm gì?", alert là tiếng ồn. Ghép mọi alert với trang Confluence hay wiki giải thích chẩn đoán và sửa.

Bản thân observability tạo failure mode nào?

Khi nào đầu tư observability là sớm?

Khi service một instance, traffic thấp, chưa lên production. Máy dev local đọc console log là ổn. Thêm OpenTelemetry vào ngày deploy staging - không trước, không sau. Dây vào service đã phục vụ user khó hơn cài đặt từ ngày đầu. Ước lượng QPS chương 2 cho biết khi nào "production" đủ thật để cần điều này.

Đi tiếp đâu từ đây?

Chương kế tiếp: rate limit trong .NET - khối ops thứ hai. Cùng observability, rate limit là cái giữ service sống khi upstream hỏng. Sau đó, các chương case study lắp mọi block từ foundations đến ops thành thiết kế thật, đầy đủ.

Câu hỏi thường gặp

Ba tín hiệu - cái nào quan trọng nhất?
Metric cho alert (rate, latency p99, error count - rẻ aggregate, query nhanh). Trace cho debug (request chậm ở đâu). Log cho ngữ cảnh (exception cụ thể, input vỡ). Phần lớn team đầu tư quá nhiều vào log và quá ít vào metric; tỉ lệ đúng là 'metric trước, trace sau, log làm bằng chứng phụ'.
OpenTelemetry vs Serilog vs Application Insights?
Không phải vs - phân tầng. OpenTelemetry là protocolinstrumentation. Serilog là thư viện xử lýg emit qua OTel. Application Insights là backend nhận data OTel. Lựa chọn chủ yếu là 'backend nào' (App Insights, Datadog, Honeycomb, Grafana Cloud, self-host Jaeger+Prometheus). Code không đổi; OTel tách instrumentation khỏi vendor.
Mặc định nên emit metric nào?
RED cho mọi endpoint: Rate (req/s), Errors (req/s với status >=500), Duration (histogram latency). Cộng tín hiệu quá tải: CPU, memory, GC pause, độ dài queue thread pool. Package instrumentation .NET emit tất cả miễn phí. Metric tuỳ chỉnh là cho KPI nghiệp vụ (đơn/phút, signup/giờ) - hiếm khi cần cho alert kỹ thuật.
Observability tốn bao nhiêu trong production?
Phần lớn là storage. Trace đắt ở scale - 1 trace mỗi request ở 10K QPS là 25M trace/giờ. Sampling cắt 100x với mất ít; 1% head-based sampling cộng error sampling 100% là chuẩn. Metric rẻ (đã aggregate khi vào storage). Log ở giữa - structured, sample tại nguồn cho path lắm lời.