Language

.NETで分散トレースを構成する

.NET 11 以降では、Microsoft.Extensions.Diagnostics.Tracing API を使用して、ActivityListenerインスタンスを登録し、ルールを使用して、各リスナーが受け取るActivitySourceとActivityインスタンスを選択できます。 コードでルールを定義するか、IConfiguration から読み込んでください。

Important

トレース ルールは、このトレース インフラストラクチャを通じて登録されたリスナーにのみ影響します。 テレメトリ エクスポーターが作成および管理するリスナーなど、関連のない ActivityListener インスタンスには影響しません。 これらのリスナーまたはエクスポーターを個別に設定します。

トレースを登録

API は、 Microsoft.Extensions.Diagnostics パッケージで使用できます。 Microsoft.AspNetCore.App共有フレームワークを参照するアプリには、既にアセンブリが含まれています。

AddTracingでIServiceCollectionを呼び出します。 コールバックで:

  • AddListener を呼び出して各リスナーを登録します。 リスナー名は、構成規則でリスナーを識別します。
  • ActivityListenerBuilderを使用して、リスナーのサンプリングデリゲートと通知デリゲートを構成します。 トレース インフラストラクチャは、ShouldListenTo、ソース サブスクリプション、およびリスナーの有効期間を管理します。
  • AddConfiguration を、規則を含む構成セクションを指定して呼び出します。 Tracingという名前のセクションの場合、configuration.GetSection("Tracing")を渡します。

完全なサンプルは、3 つの呼び出しをすべて示しています。 .NET ホストは、ホストの起動時にトレース サブスクリプションをアクティブ化します。 ホストなしで依存性の注入を使用する場合は、登録されている ActivitySourceFactory を解決してリスナーを構築し、サブスクリプションをアクティブ化します。 完全なサンプルでは、ファクトリを直接解決します。

トレースを構成

構成またはコードでトレース ルールを設定します。 リスナー、ソース、操作、スコープに一致するルールがない場合、トレースはその組み合わせに対して無効のままです。

コードなしでトレースを設定する

構成を使用するアプリの場合は、 セクションを Tracing または別の 構成プロバイダー に追加します。 次に、そのセクションをAddConfigurationに渡します:

tracing.AddConfiguration(configuration.GetSection("Tracing"));

次の例は、サポートされているルール セクションとルール レベルを示しています。

{
  "Tracing": {
    "EnabledTracing": {
      "Default": false,
      "Contoso": true,
      "Contoso.Orders": {
        "Default": true,
        "HealthCheck": false
      },
      "Contoso.*.Storage": true
    },
    "EnabledGlobalTracing": {
      "System.Net.Http": true
    },
    "EnabledLocalTracing": {
      "Contoso.Workers": true
    },
    "Console": {
      "EnabledLocalTracing": {
        "Contoso.Orders": {
          "Submit": false
        }
      }
    }
  }
}

EnabledTracing、EnabledGlobalTracing、またはEnabledLocalTracing セクションのすぐ下にルールを追加します。 Rules レベルを追加しないでください。

ルールのセクション

ソース スコープと、ルールが 1 つのリスナーを対象としているかどうかに基づいて、ルール セクションを選択します。

セクション 一致するリスナー 一致したアクティビティ ソース
EnabledTracing 登録されているすべてのリスナー。 グローバルソースとローカルソース。
EnabledGlobalTracing 登録されているすべてのリスナー。 グローバルソースのみ。
EnabledLocalTracing 登録されているすべてのリスナー。 ローカルのソースのみ。
{Listener}:EnabledTracing 名前付きリスナー。 グローバルおよびローカルのソース
{Listener}:EnabledGlobalTracing 名前付きリスナー。 グローバルソースのみ。
{Listener}:EnabledLocalTracing 名前付きリスナー。 ローカルソースのみ。

リスナー名は、 AddListenerに渡された名前と一致します。 セクション名とリスナー名では、大文字と小文字は区別されません。

ルールレベル

任意のルール セクションで、次のいずれかのキー フォームを使用します。

キーフォーム ルール レベル Match
Default Default. すべてのソース元と処理。
{SourceName} アクティビティのソース 一致するソースの各操作。
{SourceName}:Default アクティビティのソース。 一致するソースに対するすべての操作 このフォームでは、ソースに操作ルールも含めることもできます。
{SourceName}:{OperationName} 操作。 一致するソースから取得した名前付き操作

各リーフ値を true に設定して一致を有効にするか、false に設定して無効にします。 たとえば、上記の構成は次のようになります。

  • 既定でトレースを無効にします。
  • 名前が Contoso で始まるソースを有効にします。
  • Contoso.Ordersを有効にしますが、HealthCheck操作を無効にします。
  • Contoso.で始まり、.Storageで終わるソース パターンを有効にします。
  • グローバル ソース、ローカル ソース、または Console という名前のリスナーにのみ適用されるルールを追加します。

ブール値リーフとして Default を使用します。 Defaultにoperation 要素の子要素が含まれている場合、構成はDefaultをリテラル アクティビティ ソース名として扱います。

コードを使用してトレースを構成する

DisableTracing に渡された EnableTracing 上で AddTracing と ITracingBuilder を呼び出します。

tracing.EnableTracing(sourceName: "Contoso");
tracing.DisableTracing(
    sourceName: "Contoso.Orders",
    operationName: "HealthCheck");

sourceName、operationName、またはlistenerNameを省略して、そのレベルのすべての値と一致させます。 既定では、プログラムによる規則はグローバル ソースとローカル ソースの両方に適用されます。 ルールを絞り込むには、 Global、 Local、またはその両方を scopes 引数に渡します。

構成規則とプログラムによる規則は、1 つの順序付き規則セットを形成します。 同じように特定のルールが競合する場合は、最後に追加されたルールが優先されます。 したがって、AddTracing コールバックでの呼び出しの順序は、AddConfiguration、EnableTracing、および DisableTracing が同程度に具体的なルールを追加する場合に重要です。

アクティビティ ソース名を照合

ソース名、操作名、およびリスナー名では、大文字と小文字を区別しない ordinal 比較が使用されます。 ソース名ルールでは、次の一致形式に対応しています。

  • 空のソース名は、すべてのソースと一致します。
  • ワイルドカードのないソース名は、プレフィックスで照合されます。 たとえば、 Contoso は Contoso.Orders と ContosoPaymentsに一致します。マッチャーでは、プレフィックスの後に区切り記号は必要ありません。
  • 1 つの * ワイルドカードを含むソース名は、接頭辞と接尾辞を持つ名前に一致します。 たとえば、 Contoso.*.Storage は Contoso.Orders.Storageに一致しますが、 Contoso.Orders.Apiには一致しません。
  • 「*」というソース名は、すべてのソースに一致します。

操作名は正確に一致します。 前方一致またはワイルドカード一致は使用されません。

ルールの優先順位

リスナー、ソース、操作、スコープの組み合わせごとに、トレース インフラストラクチャは一致する規則を 1 つ選択します。 一致ルールを次の順序で比較します。

  • リスナー固有のルールは、すべてのリスナーのルールよりも優先されます。
  • ソース ルールは、既定のルールよりも優先されます。 2 つのソース式が一致する場合は、長い式が優先されます。 比較では、* ワイルドカードを含む式の長さを使用します。
  • 操作規則は、ソース レベルの規則よりも優先されます。
  • 両方のスコープを対象とするルールよりも、グローバル専用またはローカル専用のルールが優先されます。
  • 前のすべての要素が等しい場合は、最後のルールが優先されます。

比較は最初の相違点で停止します。 たとえば、リスナー固有の既定の規則は、リスナーの特定性が最初に評価されるため、すべてのリスナーに適用されるソース固有の規則よりも優先されます。

操作規則では、ソース サブスクリプションも制御します。 リスナーは、操作ルールによってそのソースの少なくとも 1 つの操作が有効になると、既定で無効になっているソースをサブスクライブします。 その後、リスナーは None を返し、無効な操作のコールバックをスキップします。

グローバルまたはローカルのアクティビティ ソースを選択

トレース インフラストラクチャでは、次の 2 つのアクティビティ ソース スコープが区別されます。

  • グローバル ソースは、そのコンストラクターを通じて作成された ActivitySource です。 EnabledGlobalTracing ルールは、これらのソースに適用されます。
  • ローカル ソースは、依存性注入から解決されたActivitySourceによって作成されたActivitySourceFactoryです。 EnabledLocalTracing ルールは、そのファクトリによって作成されたソースに適用されます。

依存関係挿入コンテナーでソースとそのトレース リスナーを管理する場合は、 Create を使用します。 ファクトリのリスナーは、別のファクトリによって作成されたローカル ソースを無視します。 EnabledTracing内のルールは、両方のソースの種類に適用されます。

これらのスコープは、AddTracing が登録するリスナーのみを制限します。 独立したActivityListenerは、ShouldListenToによって作成されたアクティビティ ソースに対しても、独自のActivitySourceFactoryおよびサンプリング コールバックを使用します。

トレース構成の再読み込み

AddConfiguration は、指定された構成の変更トークンを購読します。 構成プロバイダーが再読み込みをサポートし、変更を報告する場合、トレース インフラストラクチャは:

  • ルール セットを再構築します。
  • 登録されている各リスナーのソース サブスクリプションを更新します。
  • 更新されたルールを後続のアクティビティに適用します。

再読み込み後も、アプリは同じ ActivitySource インスタンスを使用し続けることができます。 後続のアクティビティでは、更新されたルールが使用されます。

変更を自動的に報告しない変更可能なプロバイダーの場合は、構成を更新し、 Reloadを呼び出します。 完全なサンプルでは、インメモリ構成プロバイダーでこの方法を使用します。

無効な設定を処理

トレース構成は、TryParse を使用して値を解析します。 大文字と小文字に関係なく true と false を受け入れ、周囲の空白を許可します。 1、0、yes、またはonは受け入れられません。

構成ローダーは、有効なブール値ではないリーフ値を自動的に無視します。 また、サポートされているスキーマと一致しない不明なセクションや形式が正しくないセクションも無視されます。 これらのケースでは例外が生成されないため、キー名を注意深く確認してください。

ソース式には、* ワイルドカードは最大 1 つまで含めることができます。 複数のワイルドカードを持つルールでは、アプリの起動時または構成の再読み込み後に、ルールが実体化されると、ArgumentException がスローされます。 プログラムによるルールの場合、 None によって ArgumentOutOfRangeExceptionも発生します。

サンプルを実行する

次の.NET 11 コンソール アプリは、1 つの有効な操作と 1 つの無効な操作を作成します。 次に、無効化された操作のルールを更新し、Reloadを呼び出し、同じローカル ActivitySourceを使用して操作を再度開始します。

using System.Diagnostics;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.Tracing;

IConfigurationRoot configuration = new ConfigurationBuilder()
    .AddInMemoryCollection(new Dictionary<string, string?>
    {
        ["Tracing:EnabledTracing:Default"] = "false",
        ["Tracing:EnabledTracing:Contoso.Orders:Default"] = "true",
        ["Tracing:EnabledTracing:Contoso.Orders:DisabledOperation"] = "false",
    })
    .Build();

ServiceCollection services = new();
services.AddTracing(tracing =>
{
    tracing.AddListener("Console", listener =>
    {
        listener.Sample = static (ref ActivityCreationOptions<ActivityContext> _) =>
            ActivitySamplingResult.AllData;
        listener.SampleUsingParentId = static (ref ActivityCreationOptions<string> _) =>
            ActivitySamplingResult.AllData;
        listener.ActivityStarted = activity =>
            Console.WriteLine($"Listener started: {activity.OperationName}");
    });
    tracing.AddConfiguration(configuration.GetSection("Tracing"));
});

using ServiceProvider serviceProvider = services.BuildServiceProvider();
ActivitySourceFactory factory =
    serviceProvider.GetRequiredService<ActivitySourceFactory>();
using ActivitySource source = factory.Create("Contoso.Orders");

Console.WriteLine("Before reload:");
WriteResult(source, "EnabledOperation");
WriteResult(source, "DisabledOperation");

configuration["Tracing:EnabledTracing:Contoso.Orders:DisabledOperation"] = "true";
configuration.Reload();

Console.WriteLine("After reload:");
WriteResult(source, "DisabledOperation");

static void WriteResult(ActivitySource source, string operationName)
{
    using Activity? activity = source.StartActivity(operationName);
    string state = activity is null ? "disabled" : "enabled";
    Console.WriteLine($"{operationName}: {state}");
}
Imports System.Diagnostics
Imports Microsoft.Extensions.Configuration
Imports Microsoft.Extensions.DependencyInjection
Imports Microsoft.Extensions.Diagnostics.Tracing

Module Program
    Sub Main()
        Dim initialData As New Dictionary(Of String, String) From {
            {"Tracing:EnabledTracing:Default", "false"},
            {"Tracing:EnabledTracing:Contoso.Orders:Default", "true"},
            {"Tracing:EnabledTracing:Contoso.Orders:DisabledOperation", "false"}
        }

        Dim configuration As IConfigurationRoot =
            New ConfigurationBuilder().AddInMemoryCollection(initialData).Build()

        Dim services As New ServiceCollection()
        services.AddTracing(
            Sub(tracing)
                tracing.AddListener(
                    "Console",
                    Sub(listener)
                        listener.Sample = AddressOf SampleActivity
                        listener.SampleUsingParentId = AddressOf SampleActivityUsingParentId
                        listener.ActivityStarted =
                            Sub(activity) Console.WriteLine(
                                $"Listener started: {activity.OperationName}")
                    End Sub)
                tracing.AddConfiguration(configuration.GetSection("Tracing"))
            End Sub)

        Using serviceProvider As ServiceProvider = services.BuildServiceProvider()
            Dim factory As ActivitySourceFactory =
                serviceProvider.GetRequiredService(Of ActivitySourceFactory)()

            Using source As ActivitySource = factory.Create("Contoso.Orders")
                Console.WriteLine("Before reload:")
                WriteResult(source, "EnabledOperation")
                WriteResult(source, "DisabledOperation")

                configuration(
                    "Tracing:EnabledTracing:Contoso.Orders:DisabledOperation") = "true"
                configuration.Reload()

                Console.WriteLine("After reload:")
                WriteResult(source, "DisabledOperation")
            End Using
        End Using
    End Sub

    Private Function SampleActivity(
        ByRef options As ActivityCreationOptions(Of ActivityContext)
    ) As ActivitySamplingResult
        Return ActivitySamplingResult.AllData
    End Function

    Private Function SampleActivityUsingParentId(
        ByRef options As ActivityCreationOptions(Of String)
    ) As ActivitySamplingResult
        Return ActivitySamplingResult.AllData
    End Function

    Private Sub WriteResult(source As ActivitySource, operationName As String)
        Using activity As Activity = source.StartActivity(operationName)
            Dim state As String = If(activity Is Nothing, "disabled", "enabled")
            Console.WriteLine($"{operationName}: {state}")
        End Using
    End Sub
End Module

C# サンプルと Visual Basic サンプルでは、同じ結果が生成されます。

Before reload:
Listener started: EnabledOperation
EnabledOperation: enabled
DisabledOperation: disabled
After reload:
Listener started: DisabledOperation
DisabledOperation: enabled

参照