Application Insights SDK 3.0 がリリースされて内部実装が OpenTelemetry ベースになりましたが、一部の API が削除されたのでシンプルに使っているケース以外ではそれなりに移行コストがかかるようになっています。
以下のドキュメントで Application Insights SDK の v2.x から v3.0 への移行について説明されていますが、特に ITelemetryInitializer と ITelemetryProcessor が削除されたことが大きなブロッカーになるかと思います。
結局は OpenTelemetry の Processor などを使う必要があるので、全体的に Application Insights SDK を捨てて Azure Monitor OpenTelemetry Distro への移行を決意しました。既に Azure Functions の監視を OpenTelemetry に移行したという内容は書いているので、今回は ASP.NET Core をターゲットに Application Insights SDK v2.x でサポートされていた機能を移行する観点で確認します。
Azure Monitor OpenTelemetry Distro を有効化する手順は以下を参照してください。ASP.NET Core 向け公式の NuGet パッケージをインストールすると HttpClient などのテレメトリは自動で収集してくれるので便利です。
Application Insights SDK をカスタマイズせずに使っていた場合はこの時点で移行は完了ですが、フィルタリングなどカスタマイズをしていた場合にはそれぞれのフィルタ実装を OpenTelemetry 向けに移行する必要があります。
実際にフィルタ実装を移行する前に .NET 向けの OpenTelemetry は Activity ベースになっていることを理解しておいた方が楽です。以下のドキュメントの図がわかりやすいので事前に確認しておいた方が良いです。
ドキュメントには地味にリンク切れが多いですが、ASP.NET Core や HttpClient 向けのライブラリは以下のリポジトリに移動しているので、注意しておかないと古い実装を見続けることになります。
ここからは実際に Application Insights SDK から Azure Monitor OpenTelemetry Distro へ移行を行った際に必要だった修正をまとめました。主にテレメトリのカスタマイズ周りが問題になるので、その部分を中心に書いています。
ITelemetryInitializer を移行する
Application Insights SDK ではテレメトリにカスタムプロパティを追加するのに使っていた ITelemetryInitializer は OpenTelemetry の世界では Instrumentation ライブラリに用意されている Enrich 系の拡張ポイントを使うのが簡単です。
Enrich 系の拡張ポイント以外には BaseProcessor<Activity> を実装する方法もありますが、固定のパラメータを追加する以外は扱いにくいので基本は Enrich 系を使うのが良いです。
Instrumentation ライブラリに用意された Enrich を使う
殆どのライブラリには拡張ポイントが用意されているので、今回はシンプルに ASP.NET Core で試していきます。サンプルとしてユーザー情報をテレメトリに含めるようにしていきます。
ASP.NET Core の場合は複数の Enrich が用意されていますが、今回は EnrichWithHttpRequest を使って ClaimsPrincipal を取得し、ログイン済みの場合はユーザー ID を user.id として設定するようにします。
builder.Services.AddOpenTelemetry()
.WithTracing(tracing =>
{
tracing.AddAspNetCoreInstrumentation(options =>
{
options.EnrichWithHttpRequest = (activity, httpRequest) =>
{
var user = httpRequest.HttpContext.User;
if (user.Identity?.IsAuthenticated == true)
{
var userId = user.FindFirst(ClaimTypes.NameIdentifier)?.Value;
if (!string.IsNullOrEmpty(userId))
{
activity.SetTag("user.id", userId);
}
}
};
});
})
.UseAzureMonitor();
安全に HttpRequest を触れるような拡張ポイントになっているので、ITelemetryInitializer よりも扱いやすいケースも多いと思います。今回は ASP.NET Core 向けですが HttpClient 向けの場合は独立してカスタマイズ可能なのも良いです。
カスタムプロセッサを実装する
これまでの ITelemetryInitializer に近い使い勝手なのが BaseProcessor<Activity> になります。いくつか拡張可能なメソッドが用意されているので、Activity をカスタマイズするタイミングをコントロールできます。
DI 経由で扱われるので IHttpContextAccessor を触ることもできましたが、公式の Enrich を使う方法に比べると安全性に欠けるので、ある程度固定の値を追加するといった用途になってしまうと思います。
builder.Services.AddOpenTelemetry()
.WithTracing(tracing =>
{
tracing.AddProcessor<CustomTagProcessor>();
})
.UseAzureMonitor();
public class CustomTagProcessor : BaseProcessor<Activity>
{
public override void OnEnd(Activity activity)
{
activity.SetTag("customTag", "customValue");
}
}
このサンプルはカスタムタグを追加しているだけですが、コンパイル時にコミットハッシュなどを埋め込むようにすると有用かもしれません。最近なら Source Generator で触れる気もします。
ITelemetryProcessor を移行する
主にテレメトリのフィルタに使われていた ITelemetryProcessor についても、OpenTelemetry では Enrich のように Filter 用の拡張ポイントが用意されているライブラリが多いので、それを使って対応すると個別にコントロールできるので扱いやすくなっています。
こちらも Enrich の時と同様に BaseProcessor<Activity> を使ってフィルタを実装することもできます。
Instrumentation ライブラリに用意された Filter を使う
推奨されている方法は Instrumentation ライブラリに用意されたフィルタ用の拡張ポイントを使うことです。ASP.NET Core の場合は 1 つの Filter だけ用意されていますが、ライブラリによっては複数用意されていることもあります。
個人的には Front Door からのヘルスチェックは膨大な量になるためテレメトリを送信したくないので、以下のような Filter を書いて /health はフィルタするように対応しました。
builder.Services.AddOpenTelemetry()
.WithTracing(tracing =>
{
tracing.AddAspNetCoreInstrumentation(options =>
{
options.Filter = httpContext => httpContext.Request.Path != "/health";
});
})
.UseAzureMonitor();
非常にシンプルなコードで実現できるのは良いですが、相変わらず Program.cs は油断すると肥大化するなという印象です。上手く拡張メソッドなどを使って隠していった方が良いとは思います。
カスタムプロセッサを実装する
フィルタについても BaseProcessor<Activity> が使えますが、直感的な書き方ではないので正直あまりお勧めは出来ないです。しかし知っておいて損はないと思うので、一応書いておきます。
先ほどのヘルスチェックを無視する処理を BaseProcessor<Activity> を使って書いてみると以下のようになります。この中で HttpRequest を触るのが不安が残るのでタグの値を見て ActivityTraceFlags の値を変えて対応します。
builder.Services.AddOpenTelemetry()
.WithTracing(tracing =>
{
tracing.AddProcessor<HealthCheckFilterProcessor>();
})
.UseAzureMonitor();
public class HealthCheckFilterProcessor : BaseProcessor<Activity>
{
public override void OnEnd(Activity activity)
{
if (activity.OperationName == "Microsoft.AspNetCore.Hosting.HttpRequestIn")
{
if ((string?)activity.GetTagItem("url.path") == "/health")
{
activity.ActivityTraceFlags &= ~ActivityTraceFlags.Recorded;
}
}
}
}
分かりにくいですが ActivityTraceFlags.Recorded を外すと Application Insights に送信されなくなります。可能な限り Instrumentation ライブラリのフィルタ機能を使って実現するのを強くお勧めします。
Cosmos DB SDK のテレメトリを送信する
HttpClient や SqlClient については専用の Instrumentation ライブラリが用意されているので、インストールして追加すれば自動でテレメトリの収集が行われます。しかし Cosmos DB については v3 SDK で OpenTelemetry 対応が行われていますが追加の設定が必要です。
この追加の設定が若干分かりにくいのでメモも兼ねて書いておきます。具体的には以下のように AppContext.SetSwitch を使ってプレビュー機能を有効化して、ソースとして Azure.Cosmos.Operation を追加し、更に DisableDistributedTracing に false を設定して分散トレーシングを有効化します。
AppContext.SetSwitch("Azure.Experimental.EnableActivitySource", true); builder.Services.AddSingleton(_ => { return new CosmosClient(builder.Configuration["Cosmos:ConnectionString"], new CosmosClientOptions { CosmosClientTelemetryOptions = new CosmosClientTelemetryOptions { DisableDistributedTracing = false } }); }); builder.Services.AddOpenTelemetry() .WithTracing(tracing => { tracing.AddSource("Azure.Cosmos.Operation"); }) .UseAzureMonitor();
これで Cosmos DB のリクエストについても OpenTelemetry として Application Insights に送信されるようになるので、実行したクエリや RU といった情報の確認が行えるようになります。