共用方式為


範例:將 OpenTelemetry 與 Prometheus、Grafana 和 Jaeger 結合使用

此範例會將 Prometheus 用於計量收集、將 Grafana 用於建立儀表板,以及將 Jaeger 用來顯示分散式追蹤。

1. 建立專案

使用 Visual Studio 中的 ASP.NET Core Empty 範本或使用下列 .NET CLI 命令,建立簡單的 Web API 專案:

dotnet new web

2.新增計量和活動定義

下列程式碼會針對呼叫 API 的次數定義新的計量 (greetings.count),以及定義新的活動來源 (OtPrGrYa.Example)。

// Custom metrics for the application
var greeterMeter = new Meter("OtPrGrYa.Example", "1.0.0");
var countGreetings = greeterMeter.CreateCounter<int>("greetings.count", description: "Counts the number of greetings");

// Custom ActivitySource for the application
var greeterActivitySource = new ActivitySource("OtPrGrJa.Example");

3.建立 API 端點

app.MapGet("/", SendGreeting);
async Task<String> SendGreeting(ILogger<Program> logger)
{
    // Create a new Activity scoped to the method
    using var activity = greeterActivitySource.StartActivity("GreeterActivity");

    // Log a message
    logger.LogInformation("Sending greeting");

    // Increment the custom counter
    countGreetings.Add(1);

    // Add a tag to the Activity
    activity?.SetTag("greeting", "Hello World!");

    return "Hello World!";
}

注意

API 定義不會使用 OpenTelemetry 特有的任何項目。 其會針對可檢視性使用 .NET API。

4.參考 OpenTelemetry 套件

使用 NuGet 套件管理員或命令列來新增下列 NuGet 套件:

<ItemGroup>
    <PackageReference Include="OpenTelemetry.Exporter.Console" Version="1.9.0" />
    <PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.9.0" />
    <PackageReference Include="OpenTelemetry.Exporter.Prometheus.AspNetCore" Version="1.9.0-beta.2" />
    <PackageReference Include="OpenTelemetry.Exporter.Zipkin" Version="1.9.0" />
    <PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.9.0" />
    <PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.9.0" />
    <PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.9.0" />
</ItemGroup>

注意

使用最新版本,因為 OTel API 會不斷地發展。

5.使用正確的提供者設定 OpenTelemetry

var tracingOtlpEndpoint = builder.Configuration["OTLP_ENDPOINT_URL"];
var otel = builder.Services.AddOpenTelemetry();

// Configure OpenTelemetry Resources with the application name
otel.ConfigureResource(resource => resource
    .AddService(serviceName: builder.Environment.ApplicationName));

// Add Metrics for ASP.NET Core and our custom metrics and export to Prometheus
otel.WithMetrics(metrics => metrics
    // Metrics provider from OpenTelemetry
    .AddAspNetCoreInstrumentation()
    .AddMeter(greeterMeter.Name)
    // Metrics provides by ASP.NET Core in .NET 8
    .AddMeter("Microsoft.AspNetCore.Hosting")
    .AddMeter("Microsoft.AspNetCore.Server.Kestrel")
    .AddPrometheusExporter());

// Add Tracing for ASP.NET Core and our custom ActivitySource and export to Jaeger
otel.WithTracing(tracing =>
{
    tracing.AddAspNetCoreInstrumentation();
    tracing.AddHttpClientInstrumentation();
    tracing.AddSource(greeterActivitySource.Name);
    if (tracingOtlpEndpoint != null)
    {
        tracing.AddOtlpExporter(otlpOptions =>
         {
             otlpOptions.Endpoint = new Uri(tracingOtlpEndpoint);
         });
    }
    else
    {
        tracing.AddConsoleExporter();
    }
});

此程式碼會使用 ASP.NET Core 檢測,從 ASP.NET Core 取得計量和活動。 其也會針對計量和追蹤分別註冊 MetricsActivitySource 提供者。

程式碼會針對計量使用 Prometheus 匯出工具,其會使用 ASP.NET Core 來裝載端點,因此您也需要新增:

// Configure the Prometheus scraping endpoint
app.MapPrometheusScrapingEndpoint();

6.執行專案

執行專案,然後使用瀏覽器或 curl 存取 API。

curl -k http://localhost:7275

每次您要求頁面時,都會針對已發出的問候語數目增加計數。 您可以使用相同的基礎 URL,搭配路徑 /metrics 存取計量端點。

6.1 記錄輸出

來自程式碼的記錄陳述式是使用 ILogger 的輸出。 根據預設,會啟用主控台提供者,以便將輸出導向至主控台。

對於如何從 .NET 輸出記錄,有幾個選項:

  • stdoutstderr 輸出是由 Kubernetes 等容器系統重新導向至記錄檔。
  • 使用將與 ILogger 整合的記錄程式庫,其中包括 SerilogNLog
  • 使用 OTel 的記錄提供者,例如 OTLP 或 Azure 監視器匯出工具,如下所示。

6.2 存取計量

您可以使用 /metrics 端點來存取計量。

curl -k https://localhost:7275/
Hello World!

curl -k https://localhost:7275/metrics
# TYPE greetings_count counter
# HELP greetings_count Counts the number of greetings
greetings_count 1 1686894204856

# TYPE current_connections gauge
# HELP current_connections Number of connections that are currently active on the server.
current_connections{endpoint="127.0.0.1:7275"} 1 1686894204856
current_connections{endpoint="[::1]:7275"} 0 1686894204856
current_connections{endpoint="[::1]:5212"} 1 1686894204856
...

計量輸出是要求端點時計量的快照集。 結果是以 Prometheus 說明格式提供,這是人類可讀取,但 Prometheus 更加了解的格式。 下一個階段將討論該主題。

6.3 存取追蹤

如果查看伺服器的主控台,您會看到主控台追蹤匯出工具的輸出,此匯出工具會以人類可讀取的格式輸出資訊。 這應該會顯示兩個活動,一個來自您的自訂 ActivitySource,另一個則來自 ASP.NET Core:

Activity.TraceId:            2e00dd5e258d33fe691b965607b91d18
Activity.SpanId:             3b7a891f55b97f1a
Activity.TraceFlags:         Recorded
Activity.ParentSpanId:       645071fd0011faac
Activity.ActivitySourceName: OtPrGrYa.Example
Activity.DisplayName:        GreeterActivity
Activity.Kind:               Internal
Activity.StartTime:          2023-06-16T04:50:26.7675469Z
Activity.Duration:           00:00:00.0023974
Activity.Tags:
    greeting: Hello World!
Resource associated with Activity:
    service.name: OTel-Prometheus-Grafana-Jaeger
    service.instance.id: e1afb619-bc32-48d8-b71f-ee196dc2a76a
    telemetry.sdk.name: opentelemetry
    telemetry.sdk.language: dotnet
    telemetry.sdk.version: 1.5.0

Activity.TraceId:            2e00dd5e258d33fe691b965607b91d18
Activity.SpanId:             645071fd0011faac
Activity.TraceFlags:         Recorded
Activity.ActivitySourceName: Microsoft.AspNetCore
Activity.DisplayName:        /
Activity.Kind:               Server
Activity.StartTime:          2023-06-16T04:50:26.7672615Z
Activity.Duration:           00:00:00.0121259
Activity.Tags:
    net.host.name: localhost
    net.host.port: 7275
    http.method: GET
    http.scheme: https
    http.target: /
    http.url: https://localhost:7275/
    http.flavor: 1.1
    http.user_agent: curl/8.0.1
    http.status_code: 200
Resource associated with Activity:
    service.name: OTel-Prometheus-Grafana-Jaeger
    service.instance.id: e1afb619-bc32-48d8-b71f-ee196dc2a76a
    telemetry.sdk.name: opentelemetry
    telemetry.sdk.language: dotnet
    telemetry.sdk.version: 1.5.0

第一個是您建立的內部自訂活動。 第二個是由 ASP.NET 針對要求所建立的,其中包含 HTTP 要求屬性的標籤。 您會看到這兩者都有相同的 TraceId,其可識別單一交易,而且在分散式系統中,可以用來將交易所涉及的每個服務追蹤相互關聯。 ID 是以 HTTP 標頭的形式傳輸。 ASP.NET Core 會在收到要求時指派 TraceId (如果沒有的話)。 根據預設,HttpClient 會在輸出要求上包含標頭。 每個活動都有 SpanId,這是 TraceIdSpanId 的組合,專門識別每個活動。 Greeter 活動會透過其 ParentSpanId 成為 HTTP 活動的父代,這會對應至 HTTP 活動的 SpanId

在稍後的階段中,您會將此資料饋送至Jaeger,以視覺化分散式追蹤。

7.使用 Prometheus 收集計量

Prometheus 是計量集合、彙總和時間序列資料庫系統。 您可以使用每個服務的計量端點對其進行設定,且其會定期抓取值,並將這些值儲存在時間序列資料庫中。 然後,您可以視需要分析和處理它們。

以 Prometheus 格式公開的計量資料是流程計量的時間點快照集。 每次對計量端點提出要求時,都會報告目前的值。 雖然目前的值很有趣,但相較於歷史值,這些值會變得更有價值,可查看趨勢並偵測值是否異常。 通常,服務會根據一天中的時間或世界事件 (例如假日購物潮) 出現使用高峰。 藉由比較這些值與歷史趨勢,您可以偵測它們是否異常,或計量是否在一段時間後緩慢地變得更糟。

流程不會儲存這些計量快照集的任何歷程記錄。 將該功能新增至流程可能會耗用大量資源。 此外,在分散式系統中,您通常具有每個節點的多個執行個體,因此您希望能夠從所有節點中收集計量,然後將其彙總並與其歷程記錄值進行比較。

7.1 安裝和設定 Prometheus

https://prometheus.io/download/ 下載適用於您平台的 Prometheus,並解壓縮下載內容。

查看執行中伺服器的輸出頂端,以取得 http 端點的連接埠號碼。 例如:

info: Microsoft.Hosting.Lifetime[14]
      Now listening on: https://localhost:7275
info: Microsoft.Hosting.Lifetime[14]
      Now listening on: http://localhost:5212

修改 Prometheus YAML 組態檔,以指定 HTTP 抓取端點的連接埠,並設定較低的抓取間隔。 例如:

  scrape_configs:
  # The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
  - job_name: "prometheus"

    # metrics_path defaults to '/metrics'
    # scheme defaults to 'http'.

    scrape_interval: 1s # poll very quickly for a more responsive demo
    static_configs:
      - targets: ["localhost:5212"]

啟動 Prometheus,並在輸出中尋找其執行所在的連接埠,通常是 9090:

>prometheus.exe
...
ts=2023-06-16T05:29:02.789Z caller=web.go:562 level=info component=web msg="Start listening for connections" address=0.0.0.0:9090

在瀏覽器中開啟此 URL。 在 Prometheus UI 中,您現在應該能夠查詢計量。 使用下圖中的醒目提示按鈕來開啟計量總管,其中顯示所有可用的計量。

Prometheus 計量瀏覽器

選取 greetings_count 計量來查看值的圖表。

greetings_count 圖表

8.使用 Grafana 建立計量儀表板

Grafana 是一項儀表板產品,可根據 Prometheus 或其他資料來源建立儀表板和警示。

遵循您平台的指示,從 https://grafana.com/oss/grafana/ 下載並安裝 Grafana 的 OSS 版本。 一旦安裝,Grafana 通常就會在連接埠 3000 上執行,因此請在瀏覽器中開啟 http://localhost:3000。 您必須登入;預設的使用者名稱與密碼都是 admin

從漢堡功能表中選擇連線,然後輸入文字 prometheus 以選取您的端點類型。 選取 [建立 Prometheus 資料來源] 以新增資料來源。

與 prometheus 的 Grafana 連線

您必須設定下列屬性:

  • Prometheus 伺服器 URL:http://localhost:9090/,視需要變更連接埠

選取 [儲存並測試] 以驗證設定。

一旦取得成功訊息,您就可以設定儀表板。 按兩下快顯畫面中顯示的 [正在建置儀表板] 連結,以取得成功訊息。

選取 [新增視覺效果],然後選擇您剛才新增為資料來源的 Prometheus 資料來源。

儀表板面板設計工具應該會出現。 在畫面的下半部,您可以定義查詢。

使用 greetings_count 的 Grafana 查詢

選取 greetings_count 計量,然後選取 [執行查詢] 以查看結果。

使用 Grafana,您可以設計複雜的儀表板,追蹤任意數目的計量。

.NET 中的每個計量都可以具有額外的維度,這些維度是可以用來分割資料的機碼值組。 ASP.NET 計量全都配有一些適用於計數器的維度。 例如,來自 current-requestsMicrosoft.AspNetCore.Hosting 計數器具有下列維度:

屬性 類型 描述 範例 目前狀態
method string HTTP 要求方法。 GET  }, 永遠
scheme string 識別已用通訊協定的 URI 配置。 % 永遠
host string 收到要求的本機 HTTP 伺服器名稱。 localhost 永遠
port int 收到要求的本機 HTTP 伺服器連接埠。 8080 如果不是預設值,則新增 (80 用於 http 或 443 用於 https)

Grafana 中的圖表通常會根據每個唯一維度組合來分割。 維度可以用於 Grafana 查詢,以篩選或彙總資料。 例如,如果您繪製 current_requests 的圖表,則會看到根據每個維度組合分割的值。 若要僅根據主機進行篩選,請新增 Sum 的作業,並使用 host 做為標籤值。

主機的 Grafana current_requests

9.使用 Jaeger 的分散式追蹤

步驟 6 中,您看到分散式追蹤資訊正在公開至主控台。 此資訊會追蹤工作單位和活動。 某些活動是由平台自動建立,例如 ASP.NET 建立的活動,代表要求的處理,而程式庫和應用程式程式碼也可以建立活動。 問候語範例具有 Greeter 活動。 活動會使用 TraceIdSpanIdParentId 標籤相互關聯。

分散式系統中的每個流程都會產生自己的活動資訊串流,且與計量一樣,您需要系統來收集、儲存及相互關聯活動,才能視覺化針對每項交易完成的工作。 Jaeger 是一項開放原始碼專案,可啟用此集合和視覺效果。

https://www.jaegertracing.io/download/ 為您的平台下載 Jaeger 的最新二進位散發封存。

然後,將下載項目解壓縮到易於存取的本機位置。 執行 jaeger-all-in-one(.exe) 可執行檔:

./jaeger-all-in-one --collector.otlp.enabled

查看主控台輸出,以尋找其透過 gRPC 接聽 OTLP 流量的連接埠。 例如:

{"level":"info","ts":1686963686.3854616,"caller":"otlpreceiver@v0.78.2/otlp.go:83","msg":"Starting GRPC server","endpoint":"0.0.0.0:4317"}

此輸出會告訴您其正在 0.0.0.0:4317 進行接聽,因此您可以將該連接埠設定為 OTLP 匯出工具的目的地。

為我們的專案開啟 AppSettings.json 檔案,然後新增下列一行,變更連接埠 (如果適用)。

"OTLP_ENDPOINT_URL" :  "http://localhost:4317/"

重新啟動問候語流程,以便其可以挑選屬性變更,並開始將追蹤資訊導向至 Jaeger。

現在,您應該能夠從網頁瀏覽器於 http://localhost:16686/ 看到 Jaeger UI。

Jaeger 追蹤查詢

若要查看追蹤清單,請從 [服務] 下拉式清單中選取 OTel-Prometheus-grafana-Jaeger。 選取一個追蹤應該會顯示該追蹤活動的甘特圖。 按一下每個作業會顯示更多有關活動的詳細資料。

Jaeger 作業詳細資料

在分散式系統中,您想要將追蹤從所有流程傳送至相同的 Jaeger 安裝,讓其可將整個系統的交易相互關聯。

您可以使應用程式自行進行 HTTP 呼叫,讓應用程式更有趣一點。

  • HttpClient 處理站新增至應用程式

    builder.Services.AddHttpClient();
    
  • 新增用於進行巢狀問候語呼叫的端點

    app.MapGet("/NestedGreeting", SendNestedGreeting);
    
  • 實作此端點,讓其進行也可以追蹤的 HTTP 呼叫。 在此情況下,其會在人工迴圈中回呼自己 (實際上僅適用於示範情節)。

    async Task SendNestedGreeting(int nestlevel, ILogger<Program> logger, HttpContext context, IHttpClientFactory clientFactory)
    {
        // Create a new Activity scoped to the method
        using var activity = greeterActivitySource.StartActivity("GreeterActivity");
    
        if (nestlevel <= 5)
        {
            // Log a message
            logger.LogInformation("Sending greeting, level {nestlevel}", nestlevel);
    
            // Increment the custom counter
            countGreetings.Add(1);
    
            // Add a tag to the Activity
            activity?.SetTag("nest-level", nestlevel);
    
            await context.Response.WriteAsync($"Nested Greeting, level: {nestlevel}\r\n");
    
            if (nestlevel > 0)
            {
                var request = context.Request;
                var url = new Uri($"{request.Scheme}://{request.Host}{request.Path}?nestlevel={nestlevel - 1}");
    
                // Makes an http call passing the activity information as http headers
                var nestedResult = await clientFactory.CreateClient().GetStringAsync(url);
                await context.Response.WriteAsync(nestedResult);
            }
        }
        else
        {
            // Log a message
            logger.LogError("Greeting nest level {nestlevel} too high", nestlevel);
            await context.Response.WriteAsync("Nest level too high, max is 5");
        }
    }
    

這會產生一個更有趣的圖表,其中包含要求的金字塔形狀,因為每一層都會等待上一個呼叫的回應。

Jaeger 巢狀相依性結果