.NET カスタムインスツルメンテーション

このページでは、Datadog APM で可観測性を追加・カスタマイズするための一般的なユースケースを詳しく説明します。サポートされているランタイムの一覧は、.NET Framework 互換性要件または .NET Core 互換性要件を参照してください。

デフォルトの自動インスツルメンテーション以上を取得するためには、いくつかの方法があります。

  • 構成経由。これは自動インストルメンテーションに依存し、特定のタグを追加することはできません。
  • 属性の使用。これは、自動インストルメンテーションに依存し、操作名とリソース名をカスタマイズすることができます。
  • カスタムコードの使用。これは、自動インスツルメンテーションと並行して、またはそれなしで動作させることができます。スパンを最も自由にコントロールすることができます。

これらのソリューションを互いに組み合わせることで、求めるインスツルメンテーションの精度を実現することができます。

構成によるインスツルメントメソッド

: この機能を使用するには、アプリケーションの自動インストルメンテーションを有効にする必要があります。.NET トレーサーのセットアップ方法と自動インスツルメンテーションの有効化については、.NET Framework セットアップ手順または .NET Core セットアップ手順を参照してください。

環境変数 DD_TRACE_METHODS を使用すると、アプリケーションコードを変更せずに、サポートされていないフレームワークを可視化することができます。DD_TRACE_METHODS の入力フォーマットの詳細については、.NET Framework 構成手順または .NET Core 構成手順を参照してください。例えば、Store.Managers.SessionManager 型で定義された SaveSession というメソッドをインスツルメンテーションするには、次のように設定します。

DD_TRACE_METHODS=Store.Managers.SessionManager[SaveSession]

結果として得られるスパンは、trace.annotation という値を持つ operationName 属性と SaveSession という値を持つ resourceName 属性を持っています。

スパンの属性をカスタマイズしたい場合で、ソースコードを修正する能力がある場合は、代わりに属性を通してメソッドをインスツルメントすることが可能です。

属性によるインスツルメントメソッド

: この機能を使用するには、アプリケーションに Datadog.Trace.Annotations NuGet パッケージを追加し、自動インスツルメンテーションを設定する必要があります。.NET トレーサーのセットアップ方法と自動インスツルメンテーションの有効化については、.NET Framework セットアップ手順または .NET Core セットアップ手順を参照してください。

Datadog が自動インスツルメンテーションを行う際に、メソッドに [Trace] を追加し、トレースするようにします。自動インスツルメンテーションが有効でない場合、この属性はアプリケーションに何の影響も及ぼしません。

[Trace] 属性はデフォルトのオペレーション名 trace.annotation とトレースされるメソッドのリソース名を持っています。操作名リソース名[Trace] 属性の名前付き引数として設定することで、インスツルメンテーションされる内容をより良く反映させることができます。[Trace] 属性に設定できる引数は、オペレーション名とリソース名のみです。例:

using Datadog.Trace.Annotations;

namespace Store.Managers
{
    public class SessionManager
    {
        [Trace(OperationName = "database.persist", ResourceName = "SessionManager.SaveSession")]
        public static void SaveSession()
        {
            // ここにメソッドの実装
        }
    }
}

コードによるカスタムインスツルメンテーション

: この機能を使用するには、Datadog.Trace NuGet パッケージをアプリケーションに追加する必要があります。これは、トレーサーとアクティブスパンに直接アクセスするための API を提供します。
: Datadog.Trace NuGet パッケージと自動インスツルメンテーションの両方を使用する場合、バージョンを同期させることが重要です。

コードによる Datadog の構成

アプリケーションを構成する方法は複数あります。環境変数、web.config ファイル、datadog.json ファイルを使用する方法があり、 ドキュメントで説明されています。また、Datadog.Trace NuGet パッケージでは、コード内で構成を行うことができます。

構成設定をオーバーライドするには、TracerSettings のインスタンスを作成して、静的な Tracer.Configure() メソッドに渡します。

using Datadog.Trace;

// 既存の環境変数と構成ソースを使用して
// 設定オブジェクトを作成します
var settings = TracerSettings.FromDefaultSources();

// 値をオーバーライドします
settings.GlobalTags.Add("SomeKey", "SomeValue");

// トレーサーの構成を置き換えます
Tracer.Configure(settings);

Tracer.Configure() を呼び出すと、カスタムインスツルメンテーションでも自動インスツルメンテーションでも、それ以降のすべてのトレースの設定が置き換わります。

構成の置き換えは、アプリケーションで一度だけ、できるだけ早い段階で行う必要があります。

カスタムトレース/スパンの作成

自動インスツルメンテーション、 [Trace] 属性、DD_TRACE_METHODS の構成に加えて、プログラム的に任意のコードブロックの周りにスパンを作成することで、観測可能性をカスタマイズすることが可能です。

カスタムスパンを作成してアクティブにするには、Tracer.Instance.StartActive() を使用します。トレースがすでにアクティブな場合 (例えば、自動インスツルメンテーションによって作成された場合)、スパンは現在のトレースの一部となります。現在のトレースがない場合は、新しいトレースが開始されます。

警告: StartActive から返されたスコープを確実にディスポーズしてください。スコープをディスポーズすると、スパンが閉じられ、そのスパンがすべて閉じられると、トレースが Datadog にフラッシュされるようになります。
using Datadog.Trace;

// 新しいスパンを開始します
using (var scope = Tracer.Instance.StartActive("custom-operation"))
{
    // 操作を実行します
}

カスタムスパンタグスパンに追加して、Datadog 内の可観測性をカスタマイズします。スパンタグは受信トレースに適用されるため、観測された動作を、マーチャントの階層、チェックアウト金額、ユーザー ID などのコードレベルの情報と関連付けることができます。

新しいスパンを手動で作成する

手動で作成したスパンは、他のトレースメカニズムからのスパンと自動的にインテグレーションされます。つまり、トレースがすでに開始されている場合、手動スパンはその呼び出し元を親スパンとして持っています。同様に、ラップされたコードブロックから呼び出されたトレースされたメソッドは、その親として手動スパンを持ちます。

using (var parentScope =
       Tracer.Instance.StartActive("manual.sortorders"))
{
    parentScope.Span.ResourceName = "<RESOURCE NAME>";
    using (var childScope =
           Tracer.Instance.StartActive("manual.sortorders.child"))
    {
        // トレースするコードの周囲にあるステートメントを使用してネストします
        childScope.Span.ResourceName = "<RESOURCE NAME>";
        SortOrders();
    }
}

カスタムスパンタグを追加する

customer.id などのアプリケーションコード内の動的な値に対応するカスタムタグをスパンに追加します。

using Datadog.Trace;

public class ShoppingCartController : Controller
{
    private IShoppingCartRepository _shoppingCartRepository;

    [HttpGet]
    public IActionResult Index(int customerId)
    {
        // グローバルトレーサーでアクティブスコープにアクセスする
        // 注: アクティブなスパンが存在しない場合は null を返すことがあります
        var scope = Tracer.Instance.ActiveScope;

        if (scope != null)
        {
            // Datadog の Web UI で使用するためのタグをスパンに追加する
            scope.Span.SetTag("customer.id", customerId.ToString());
        }

        var cart = _shoppingCartRepository.Get(customerId);

        return View(cart);
    }
}

スパンにエラーを設定する

コードで発生したエラーをマークするには、Span.SetException(Exception) メソッドを使用します。このメソッドは、スパンをエラーとしてマークし、関連するスパンメタデータを追加して、例外の情報を提供します。

try
{
    // 例外をスローする可能性のある作業を行います
}
catch(Exception e)
{
    span.SetException(e);
}

これは、スパンに以下のタグを設定します。

  • "error.msg":exception.Message
  • "error.stack":exception.ToString()
  • "error.type":exception.GetType().ToString()

ヘッダーの抽出と挿入

Datadog APM トレーサーは、分散型トレーシングのための B3W3C のヘッダー抽出と注入をサポートしています。詳細については、セットアップドキュメントを参照してください。

ほとんどの場合、ヘッダーの抽出と注入は透過的に行われます。分散トレースが切断される可能性があるケースも知られています。例えば、分散キューからメッセージを読み込むとき、ライブラリによってはスパンコンテキストを失うことがあります。また、Kafka メッセージを消費する際に DD_TRACE_KAFKA_CREATE_CONSUMER_SCOPE_ENABLEDfalse に設定した場合にも発生することがあります。そのような場合、以下のコードを使ってカスタムトレースを追加することができます。

var spanContextExtractor = new SpanContextExtractor();
var parentContext = spanContextExtractor.Extract(headers, (headers, key) => GetHeaderValues(headers, key));
var spanCreationSettings = new SpanCreationSettings() { Parent = parentContext };
using var scope = Tracer.Instance.StartActive("operation", spanCreationSettings);

GetHeaderValues メソッドを提供します。このメソッドの実装方法は、SpanContext を保持する構造に依存します。

以下はその例です。

// Confluent.Kafka
IEnumerable<string> GetHeaderValues(Headers headers, string name)
{
    if (headers.TryGetLastBytes(name, out var bytes))
    {
        try
        {
            return new[] { Encoding.UTF8.GetString(bytes) };
        }
        catch (Exception)
        {
            // 無視
        }
    }

    return Enumerable.Empty<string>();
}

// RabbitMQ
IEnumerable<string> GetHeaderValues(IDictionary<string, object> headers, string name)
{
    if (headers.TryGetValue(name, out object value) && value is byte[] bytes)
    {
        return new[] { Encoding.UTF8.GetString(bytes) };
    }

    return Enumerable.Empty<string>();
}

Kafka コンシューマースパンをトレースするために SpanContextExtractor API を使用する場合、DD_TRACE_KAFKA_CREATE_CONSUMER_SCOPE_ENABLEDfalse に設定します。これにより、メッセージがトピックから消費された直後にコンシューマースパンが正しく閉じられ、メタデータ (partitionoffset など) が正しく記録されることが保証されます。SpanContextExtractor API を使用して Kafka メッセージから作成されたスパンは、プロデューサーのスパンの子であり、コンシューマーのスパンの兄弟になります。

すべてのスパンにグローバルにタグを追加する

DD_TAGS 環境変数を使用して、アプリケーションに対して生成されたすべてのスパンにタグを設定します。これは、Datadog UI 内でアプリケーション、データセンター、または地域の統計データをグループ化するのに役立ちます。例:

DD_TAGS=datacenter:njc,key2:value2

リソースのフィルター

リソース名に基づいてトレースを除外することで、ヘルスチェックなどの Synthetic トラフィックを削除することができます。セキュリティや追加構成については、データセキュリティのための Datadog Agent またはトレーサーの構成を参照してください。

その他の参考資料