此專案用以了解如何在 .Net Core 7 中透過 OpenTelemetry 建立日誌、指標與追蹤,並暴露到 Prometheus 中。
請注意,OpenTelemetry 雖然目前在開源社群非常活躍,但對於 .Net Core 來說,它的相關 SDK 有很多都還是搶鮮版,因此若要應用到生產環境中,請審慎評估。
- NuGet 套件
- OpenTelemetry
- OpenTelemetry.Exporter.Console
將日誌輸出到終端機
- OpenTelemetry.Exporter.Prometheus.AspNetCore (搶鮮版)
將指標輸出到指定的路徑,並使用 Prometheus 格式輸出
- OpenTelemetry.Exporter.OpenTelemetryProtocol
- OpenTelemetry.Extensions.Hosting
- OpenTelemetry.Instruumentation.AspNetCore
- OpenTelemetry.Instrumentation.Http
- OpenTelemetry.Instrumentation.Process
輸出 CPU、記憶體等指標資料
- OpenTelemetry.Instrumentation.Runtime
輸出 GC 等資料
並在 Extensions 資料夾中新增 OpenTelemetryExtension.cs
檔案,寫入如下的內容
public static class OpenTelemetryExtension
{
public static Meter openTelemetryMeter = new Meter("dotnet7.core.telemetry", "1.0.0");
public static IServiceCollection ConfigureOpenTelemetry(
ILoggingBuilder loggingBuilder,
IServiceCollection serviceCollection,
IConfiguration config)
{
string? applicationName = config.GetValue<string>("Application:Name");
if (applicationName == null)
{
applicationName = "OpenTelemetry.Lab";
}
string? otlpTargetUri = config.GetValue(
"OpenTelemetry:OtlpUri",
"http://localhost:4318");
if (otlpTargetUri == null)
{
otlpTargetUri = "http://localhost:4318";
}
var resource = ResourceBuilder.CreateDefault().AddService(applicationName);
// 設定僅輸出警告日誌
// loggingBuilder.AddFilter<OpenTelemetryLoggerProvider>("*", LogLevel.Warning);
loggingBuilder.AddOpenTelemetry(options =>
{
options.SetResourceBuilder(resource);
options.AddConsoleExporter();
});
serviceCollection.AddOpenTelemetry()
.ConfigureResource(resource => resource
.AddService(serviceName: applicationName))
.WithMetrics(metrics => metrics
.AddMeter(openTelemetryMeter.Name)
.AddAspNetCoreInstrumentation()
.AddRuntimeInstrumentation()
.AddProcessInstrumentation()
.AddPrometheusExporter()
.AddConsoleExporter()
.AddPrometheusExporter())
.WithTracing(tracing => tracing
.AddAspNetCoreInstrumentation()
.AddConsoleExporter())
.UseOtlpExporter(
OtlpExportProtocol.HttpProtobuf,
new Uri(otlpTargetUri));
return serviceCollection;
}
}
最後到 Program.cs
中調整程式碼:
...
var builder = WebApplication.CreateBuilder(args);
...
SwaggerDefinitionExtension.ConfigureSwagger(builder.Services);
ServiceMapperExtension.GetServiceProvider(builder.Services);
DatabaseExtension.AddDatabaseContext(builder.Services, builder.Configuration);
AuthorizationExtension.ConfigureAuthorization(builder.Services, builder.Configuration);
HttpClientExtension.ConfigureHttpClients(builder.Services);
// 增加 OpenTelemetryExtension
OpenTelemetryExtension.ConfigureOpenTelemetry(builder.Logging, builder.Services, builder.Configuration);
...
app.UseHttpsRedirection();
app.UseAuthorization();
app.UseRouting();
// 設定 OpenTelemetry 輸出 Prometheus 指標的路徑
app.UseOpenTelemetryPrometheusScrapingEndpoint(context =>
context.Request.Path == "/metrics");
請注意,所有的日誌輸出都應使用結構化日誌進行輸出,這類格式的日誌對於後續系統自動化分析才有意義。
-
在 Extensions 資料夾新增
ILoggerExtension.cs
,並加入以下程式碼:這個主要在做結構化日誌的設定,不一定要透過這種方式輸出日誌
internal static partial class LoggerExtensions { /// <summary> /// 啟動應用程式的日誌 /// </summary> /// <param name="logger"></param> [LoggerMessage(LogLevel.Information, "Starting the app...")] public static partial void StartingApp(this ILogger logger); /// <summary> /// 取得天氣預報的日誌,其中包含請求的使用者名稱 /// </summary> /// <param name="logger"></param> /// <param name="userName">使用者名稱</param> [LoggerMessage(LogLevel.Information, "Requesting all weather forcast results, user name is `{userName}`.")] public static partial void RetrievingWeatherForecast(this ILogger logger, string userName); }
-
在專案任意地方注入 ILogger 介面後,透過 ILogger 去呼叫第一步中定義的方法輸入日誌。
-
若需要調整日誌輸出的嚴重性設定,可以到
OpenTelemetryExtension
中設定只輸出哪些嚴重性的日誌。 -
若需要針對日誌做後處理或前處理,可以自行新增一
LogProcessor
,如下範例是一個日誌後處理,可以在結束時輸出Hello - OnEnd
日誌:-
新增 LogProcessor
public class LogProcessor : BaseProcessor<LogRecord> { public override void OnEnd(LogRecord data) { Console.WriteLine("Hello - OnEnd"); base.OnEnd(data); } }
-
調整
OpenTelemetryExtension.cs
中日誌的設定,加入以下邏輯:loggingBuilder.AddOpenTelemetry(options => { options.SetResourceBuilder(resource); options.AddConsoleExporter(); options.AddProcessor(new LogProcessor()); options.IncludeFormattedMessage = true; options.ParseStateValues = true; });
-
重新啟動應用程式,並試著打看看 API,就會看到終端機輸出
Hello - OnEnd
字樣的日誌了。
-
-
預期指標都會透過 Middleware 進行記錄,因此需建立
OpenTelemetryMiddleware
,並在檔案中鍵入以下程式碼:Meter 不建議建立太多,這會影響應用程式的效能,因此最好的方式就是在 OpenTelemetryExtension 中建立後,這邊直接引用該 Meter 即可
using System.Diagnostics.Metrics; namespace DotNet7.Template.Api.Middlewares { public class OpenTelemetryMiddleware { private readonly RequestDelegate _next; private Counter<int> _greetingCounter; public OpenTelemetryMiddleware(RequestDelegate next) { _next = next; // 引用 OpenTelemetryExtension 中的 Meter 建立自訂指標 _greetingCounter = OpenTelemetryExtension.openTelemetryMeter.CreateCounter<int>( "greetings.count", description: "Counts the number of greetings."); } public async Task Invoke(HttpContext context) { await _next(context); _greetingCounter.Add(1); } } }
-
在
MiddlewareExtension.cs
中調整為程式碼:public static class MiddlewareExtension { public static IApplicationBuilder ConfigureMiddlewares(this IApplicationBuilder builder) { // 套用 OpenTelemetryMiddleware builder.UseMiddleware<OpenTelemetryMiddleware>(); return builder; } }
-
在到
Program.cs
中確認下面的程式碼有沒有被啟用... var app = builder.Build(); // 套用 Middlewares app.ConfigureMiddlewares(); ...
-
啟動應用程式,存取
/metrics
就可以看到 Prometheus 格式的指標被輸出在頁面上若沒看到自訂的指標,請先試著打幾次 API,當流量經過 Middleware 後,這些指標就會被暴露出來了
OpenTelemetry 在 .Net Core 中的追蹤實作是依賴於 .Net Core 內建的 System.Diagnostics
API,因此若希望使用 OpenTelemetry API 實作追蹤,請參閱 OpenTelemetry API Shim 文件。
- Documentation - OpenTelemetry
- open-telemetry / opentelemetry-dotnet
- 初探 OpenTelemetry 工具組:蒐集遙測數據的新標準
- [鐵人賽 Day03] ASP.NET Core 2 系列 - Middleware
- OpenTelemetry 的 .NET 可檢視性
- OpenTelemetry .NET Logs
- Getting Started with OpenTelemetry .NET Logs in 5 Minutes - Console Application
- Prometheus Exporter AspNetCore for OpenTelemetry .NET
- Customizing OpenTelemetry .NET SDK for Metrics
- opentelemetry-dotnet - Program.cs
- How to Setup OpenTelemetry Logging in .NET
- Collector
- OTLP Exporter Configuration
- OTLP Exporter for OpenTelemetry .NET
- Getting started with OpenTelemetry Metrics in .NET 8. Part 2: Instrumenting the BookStore API
- 在 ASP .NET Core 中使用 OpenTelemetry,為應用程式埋下觀測點
- ASP.NET Core Instrumentation for OpenTelemetry .NET
- 建立計量
- System.Diagnostics.Metrics 命名空間
- podman build with relative path to dockerfile in subdirectory fails with "no such file or directory" on Mac #12841