2024年8月19日
幫 ASP.NET Core 接上 OpenTelemetry:從零到 Grafana/Tempo/Loki
分散式架構下服務一多(Message Queue、各種微服務、Restful/gRPC/GraphQL API),要追蹤 API 異常、改善流程、確認 SLI 指標,都需要一套可觀測性架構打 底。這篇把「如何從零幫一個 ASP.NET Core 專案接上 OpenTelemetry、並串到 Grafana 全家桶」整理成一份可以直接照抄的參考做法,完整可執行的範例放在 Web-Application-Observability-and-Availability。
解決方案架構
以 OpenTelemetry(OTEL) 作為統一的可觀測性框架,搭配 Grafana 生態系:
- OpenTelemetry Collector:收集 Trace 與 Metric 的集線器,可輸出到 Grafana、Jaeger 等不同後端
- Prometheus:儲存時序資料(CPU、Memory 等)的監控方案
- Grafana:將 Prometheus、資料庫等來源的資料視覺化
- Grafana Tempo:開源、可大規模擴展的分散式追蹤後端
- Grafana Loki:可擴展、多租戶的日誌聚合系統

本機執行
# 先調整 configs/ 內各服務的 IP 與 Port
docker compose up -d
幫 ASP.NET Core 8 接上可觀測性
安裝套件:OpenTelemetry.Extensions.Hosting、
OpenTelemetry.Instrumentation.AspNetCore、
OpenTelemetry.Instrumentation.Http、
OpenTelemetry.Exporter.OpenTelemetryProtocol、
OpenTelemetry.Exporter.Prometheus.AspNetCore
Tracing 與 Metrics 的註冊:
var otel = builder.Services.AddOpenTelemetry();
otel.ConfigureResource(resource => resource
.AddService(serviceName: builder.Environment.ApplicationName));
otel.WithMetrics(metrics => metrics
.AddAspNetCoreInstrumentation()
.AddMeter(greeterMeter.Name)
.AddMeter("Microsoft.AspNetCore.Hosting")
.AddPrometheusExporter());
otel.WithTracing(tracing =>
{
tracing.AddAspNetCoreInstrumentation(o =>
{
// 從 X-Forwarded-For 或連線資訊解析真實客戶端 IP,寫入 span tag
o.EnrichWithHttpRequest = (activity, request) => { /* ... */ };
});
tracing.AddHttpClientInstrumentation();
tracing.AddSource(TracingActivitySource.WebApiRegisteredActivity.Name);
tracing.AddSource(TracingActivitySource.ServiceRegisteredActivity.Name);
tracing.AddOtlpExporter(o => o.Endpoint = new Uri("http://otel:4317"));
});
分層命名 ActivitySource,讓一次請求跨 WebApi/Service/Repository 三層的
Trace 都能被串起來:
public static class TracingActivitySource
{
public static readonly ActivitySource WebApiRegisteredActivity = new("Monitor.Sample.Api");
public static readonly ActivitySource ServiceRegisteredActivity = new("Monitor.Sample.Service");
public static readonly ActivitySource RepositoryRegisteredActivity = new("Monitor.Sample.Repository");
}
public Task<int> GetNumberAsync()
{
using (TracingActivitySource.ServiceRegisteredActivity.StartActivity(
$"{nameof(TracingTestService)}.{nameof(GetNumberAsync)}"))
{
return tracingTestRepository.GetNumberAsync();
}
}
踩過的坑
⚠️
OpenTelemetry.Exporter.Prometheus.AspNetCore版本 1.8.0 會導致 Prometheus 收集 log 錯誤,需改回 1.7.0 或更新版本才能解決 (對應 issue)。
延伸應用
這次在範例專案上把 Traces、Logs、Metrics 三條線都串起來了,下一步是套用到正式 環境:統一告警儀表板,並依業務情境挑選合適的 Prometheus 指標類型(Counter、 Gauge、Histogram),讓告警規則與 PromQL 查詢有一致的設計基礎。