Application Insights .NET SDK でカスタム操作を追跡する
Application Insights SDK では、受信 HTTP 要求と、依存関係にあるサービスへの呼び出し (HTTP 要求や SQL クエリなど) を自動的に追跡します。 要求と依存関係の追跡と関連付けによってアプリケーションを構成しているすべてのマイクロサービスの応答性と信頼性を確認することで、アプリケーション全体の状態を把握することができます。
ただし、一律に対応することのできない類のアプリケーション パターンも存在します。 そうしたパターンを適切に監視するためには、手動によるコードのインストルメンテーションが必要となります。 この記事では、手動のインストルメンテーションを必要とする可能性があるいくつかのパターン (カスタムのキュー処理や実行時間の長いバックグラウンド タスクの実行など) を取り上げます。
この記事では、Application Insights SDK を使用してカスタム操作を追跡する方法についてのガイドラインを示します。 このドキュメントの対象を以下に示します。
- Application Insights for .NET (別名 Base SDK) バージョン 2.4 以降。
- Application Insights for Web Applications (ASP.NET 実行) バージョン 2.4 以降。
- Application Insights for ASP.NET Core バージョン 2.1 以降。
注意事項
Azure Monitor Application Insights を利用する新規のアプリケーションやお客様には、Azure Monitor OpenTelemetry Distro をお勧めします。 Azure Monitor OpenTelemetry Distro では、Application Insights SDK と同様の機能とエクスペリエンスが提供されます。 .NET、Node.js、Python 用の移行ガイドを使用して Application Insights SDK から移行することはできますが、私たちはまだ、下位互換性を確保するための機能をいくつか追加していくことに取り組んでいます。
概要
操作は、アプリケーションによって実行される処理の 1 つの論理部分です。 操作には、名前、開始時刻、継続時間、および実行コンテキストがあります (ユーザー名、プロパティ、結果など)。 操作 A が 操作 B によって開始された場合は、操作 B が A の親として設定されます。1 つの操作は、親を 1 つだけ持つことができますが、複数の子操作を持つことができます。 操作とテレメトリの関連付けの詳細については、「Application Insights におけるテレメトリの相関付け」を参照してください。
Application Insights .NET SDK では、操作は、抽象クラス OperationTelemetry とその子孫である RequestTelemetry と DependencyTelemetry によって表されます。
受信操作の追跡
Application Insights Web SDK は、IIS パイプラインで実行される ASP.NET アプリケーションとすべての ASP.NET Core アプリケーションを対象に、HTTP 要求を自動的に収集します。 それ以外のプラットフォームとフレームワークについては、コミュニティによってサポートされるソリューションが存在します。 標準のソリューションやコミュニティでサポートされているソリューションでサポートされていないアプリケーションについては、手動でインストルメント化できます。
キューからアイテムを受け取る worker も、独自の追跡が必要なケースです。 キューによっては、そのキューにメッセージを追加する呼び出しが、依存関係として追跡されます。 メッセージの処理を表す上位の操作については、自動的には収集されません。
そのような操作がどのように追跡されるかを見てみましょう。
簡潔に言うと、このタスクとは、RequestTelemetry
を作成して既知のプロパティを設定することです。 操作が完了したら、このテレメトリを追跡します。 次の例は、このタスクを示しています。
Owin 自己ホスト型アプリにおける HTTP 要求
この例では、トレース コンテキストは、関連付け用の HTTP プロトコルに従って反映されます。 ここに記載されているヘッダーが受信されることを予期してください。
public class ApplicationInsightsMiddleware : OwinMiddleware
{
// You may create a new TelemetryConfiguration instance, reuse one you already have,
// or fetch the instance created by Application Insights SDK.
private readonly TelemetryConfiguration telemetryConfiguration = TelemetryConfiguration.CreateDefault();
private readonly TelemetryClient telemetryClient = new TelemetryClient(telemetryConfiguration);
public ApplicationInsightsMiddleware(OwinMiddleware next) : base(next) {}
public override async Task Invoke(IOwinContext context)
{
// Let's create and start RequestTelemetry.
var requestTelemetry = new RequestTelemetry
{
Name = $"{context.Request.Method} {context.Request.Uri.GetLeftPart(UriPartial.Path)}"
};
// If there is a Request-Id received from the upstream service, set the telemetry context accordingly.
if (context.Request.Headers.ContainsKey("Request-Id"))
{
var requestId = context.Request.Headers.Get("Request-Id");
// Get the operation ID from the Request-Id (if you follow the HTTP Protocol for Correlation).
requestTelemetry.Context.Operation.Id = GetOperationId(requestId);
requestTelemetry.Context.Operation.ParentId = requestId;
}
// StartOperation is a helper method that allows correlation of
// current operations with nested operations/telemetry
// and initializes start time and duration on telemetry items.
var operation = telemetryClient.StartOperation(requestTelemetry);
// Process the request.
try
{
await Next.Invoke(context);
}
catch (Exception e)
{
requestTelemetry.Success = false;
requestTelemetry.ResponseCode;
telemetryClient.TrackException(e);
throw;
}
finally
{
// Update status code and success as appropriate.
if (context.Response != null)
{
requestTelemetry.ResponseCode = context.Response.StatusCode.ToString();
requestTelemetry.Success = context.Response.StatusCode >= 200 && context.Response.StatusCode <= 299;
}
else
{
requestTelemetry.Success = false;
}
// Now it's time to stop the operation (and track telemetry).
telemetryClient.StopOperation(operation);
}
}
public static string GetOperationId(string id)
{
// Returns the root ID from the '|' to the first '.' if any.
int rootEnd = id.IndexOf('.');
if (rootEnd < 0)
rootEnd = id.Length;
int rootStart = id[0] == '|' ? 1 : 0;
return id.Substring(rootStart, rootEnd - rootStart);
}
}
この関連付け用の HTTP プロトコルも、Correlation-Context
ヘッダーを宣言しますが、 ここでは、簡潔にするために省略されています。
キューのインストルメンテーション
W3C トレース コンテキストと関連付け用の HTTP プロトコルは、関連付けの詳細を HTTP 要求で渡しますが、すべてのキュー プロトコルでは、同じ詳細がキュー メッセージでどのように渡されるかを定義する必要があります。 AMQP などの一部のキュー プロトコルでは、より多くのメタデータを渡すことができます。 Azure Storage Queue などの他のプロトコルでは、コンテキストをメッセージ ペイロードにエンコードする必要があります。
Note
コンポーネント間のトレースは、キューではまだサポートされていません。
HTTP を使用すると、プロデューサーとコンシューマーが、異なる Application Insights リソースにテレメトリを送信する場合、トランザクションの診断エクスペリエンスとアプリケーション マップでトランザクションが表示され、エンドツーエンドでマップされます。 キューの場合、この機能はまだサポートされていません。
Service Bus キュー
トレース情報については、「Azure Service Bus メッセージングを介した分散トレースと相関付け」を参照してください。
Azure Storage キュー
Azure Storage キューの操作を追跡し、プロデューサー、コンシューマー、Azure Storage 間でテレメトリを相互に関連付ける例を次に示します。
Storage キューには HTTP API があります。 キューに対するすべての呼び出しは、Application Insights の HTTP 要求の依存関係コレクターによって追跡されます。 既定では、ASP.NET と ASP.NET Core アプリケーションで構成されます。 他の種類のアプリケーションについては、コンソール アプリケーションに関するドキュメントを参照してください。
Application Insights の操作 ID を Storage の要求 ID に関連付けることもできます。 Storage の要求クライアントとサーバーの要求 ID の設定および取得方法については、「Azure Storage の監視、診断、およびトラブルシューティング」を参照してください。
Enqueue
Storage キューは HTTP API をサポートしているため、キューを使ったすべての操作は自動的に ApplicationInsights によって追跡されます。 多くのケースには、このインストルメンテーションで対応することができます。 コンシューマー側のトレースとプロデューサー側のトレースを相互に関連付けるには、関連付け用の HTTP プロトコルでの実行方法に似た関連付けコンテキストを渡す必要があります。
この例は、Enqueue
操作を追跡する方法を示しています。 次のようにすることができます。
- 再試行を関連付ける (存在する場合) :すべての再試行には、
Enqueue
操作という共通の親が 1 つ存在します。 また、受信要求の子として追跡されます。 キューに対する論理要求が複数存在する場合、どの呼び出しが再試行されたかを見極めることは難しいことがあります。 - Storage ログを関連付ける (存在する場合に必要に応じて) :Storage ログは Application Insights のテレメトリに関連付けられます。
Enqueue
操作は、親の操作の子です。 たとえば、受信 HTTP 要求です。 HTTP の依存関係呼び出しは、Enqueue
操作の子であり、受信要求の孫でもあります。
public async Task Enqueue(CloudQueue queue, string message)
{
var operation = telemetryClient.StartOperation<DependencyTelemetry>("enqueue " + queue.Name);
operation.Telemetry.Type = "Azure queue";
operation.Telemetry.Data = "Enqueue " + queue.Name;
// MessagePayload represents your custom message and also serializes correlation identifiers into payload.
// For example, if you choose to pass payload serialized to JSON, it might look like
// {'RootId' : 'some-id', 'ParentId' : '|some-id.1.2.3.', 'message' : 'your message to process'}
var jsonPayload = JsonConvert.SerializeObject(new MessagePayload
{
RootId = operation.Telemetry.Context.Operation.Id,
ParentId = operation.Telemetry.Id,
Payload = message
});
CloudQueueMessage queueMessage = new CloudQueueMessage(jsonPayload);
// Add operation.Telemetry.Id to the OperationContext to correlate Storage logs and Application Insights telemetry.
OperationContext context = new OperationContext { ClientRequestID = operation.Telemetry.Id};
try
{
await queue.AddMessageAsync(queueMessage, null, null, new QueueRequestOptions(), context);
}
catch (StorageException e)
{
operation.Telemetry.Properties.Add("AzureServiceRequestID", e.RequestInformation.ServiceRequestID);
operation.Telemetry.Success = false;
operation.Telemetry.ResultCode = e.RequestInformation.HttpStatusCode.ToString();
telemetryClient.TrackException(e);
}
finally
{
// Update status code and success as appropriate.
telemetryClient.StopOperation(operation);
}
}
アプリケーションから報告されるテレメトリの量を減らすため、またはその他の理由で Enqueue
操作を追跡しない場合は、Activity
API を直接使用します。
- Application Insights 操作を開始する代わりに、新しい
Activity
を作成 (および開始) します。 操作名以外のプロパティを割り当てる必要はありません。 operation.Telemetry.Id
ではなくyourActivity.Id
をメッセージ ペイロードに対してシリアル化します。Activity.Current.Id
を使用することもできます。
Dequeue
Enqueue
と同様、Storage キューに対する実際の HTTP 要求は Application Insights によって自動的に追跡されます。 Enqueue
操作の発生元は親のコンテキスト (受信要求のコンテキストなど) であると推測できます。 Application Insights SDK では、このような操作やその HTTP 部分を、親要求や同じスコープ内で報告される他のテレメトリと自動的に関連付けます。
Dequeue
操作は注意が必要です。 Application Insights SDK は、自動的に HTTP 要求を追跡します。 ただし、メッセージが解析されるまでは、関連付けのコンテキストは不明です。 特に複数のメッセージが受信される場合、メッセージを取得するための HTTP 要求を他のテレメトリに関連付けることはできません。
public async Task<MessagePayload> Dequeue(CloudQueue queue)
{
var operation = telemetryClient.StartOperation<DependencyTelemetry>("dequeue " + queue.Name);
operation.Telemetry.Type = "Azure queue";
operation.Telemetry.Data = "Dequeue " + queue.Name;
try
{
var message = await queue.GetMessageAsync();
}
catch (StorageException e)
{
operation.telemetry.Properties.Add("AzureServiceRequestID", e.RequestInformation.ServiceRequestID);
operation.telemetry.Success = false;
operation.telemetry.ResultCode = e.RequestInformation.HttpStatusCode.ToString();
telemetryClient.TrackException(e);
}
finally
{
// Update status code and success as appropriate.
telemetryClient.StopOperation(operation);
}
return null;
}
Process
次の例では、受信メッセージは受信 HTTP 要求と同様の方法で追跡されます。
public async Task Process(MessagePayload message)
{
// After the message is dequeued from the queue, create RequestTelemetry to track its processing.
RequestTelemetry requestTelemetry = new RequestTelemetry { Name = "process " + queueName };
// It might also make sense to get the name from the message.
requestTelemetry.Context.Operation.Id = message.RootId;
requestTelemetry.Context.Operation.ParentId = message.ParentId;
var operation = telemetryClient.StartOperation(requestTelemetry);
try
{
await ProcessMessage();
}
catch (Exception e)
{
telemetryClient.TrackException(e);
throw;
}
finally
{
// Update status code and success as appropriate.
telemetryClient.StopOperation(operation);
}
}
他のキュー操作も、同じようにインストルメント化できます。 Peek 操作をインストルメント化する場合は、デキューと同様の方法を使用する必要があります。 キューの管理操作をインストルメント化する必要はありません。 HTTP などの操作は Application Insights によって追跡されます。ほとんどの場合はそれで十分です。
メッセージの削除をインストルメント化するときは、必ず操作 (関連付け) ID を設定してください。 別の方法として、Activity
API を使用することもできます。 その場合は、テレメトリ項目に対する操作 ID を自分で設定する必要はありません。それは Application Insights SDK によって自動的に行われます。
- キューから項目を取得した後、新しい
Activity
を作成します。 Activity.SetParentId(message.ParentId)
を使用して、コンシューマーとプロデューサーのログを相互に関連付けます。Activity
を開始します。Start/StopOperation
ヘルパーを使用して、Dequeue、Process、Delete の各操作を追跡します。 追跡は、同じ非同期制御フロー (実行コンテキスト) から行ってください。 そうすることで、相互の関連付けが適切に行われます。Activity
を停止します。Start/StopOperation
を使用するか、Track
テレメトリを手動で呼び出します。
依存関係の種類
Application Insights では、依存関係の種類を使用して UI エクスペリエンスがカスタマイズされます。 キューの場合、トランザクションの診断エクスペリエンスを向上させる次の種類の DependencyTelemetry
が認識されます。
- Azure Storage キューの
Azure queue
- Azure Event Hubs の
Azure Event Hubs
- Azure Service Bus の
Azure Service Bus
バッチ処理
一部のキューでは、1 つの要求で複数のメッセージをデキューできます。 このようなメッセージの処理には依存関係はなく、異なる論理操作に属していると推測されます。 Dequeue
操作を処理対象の特定のメッセージに関連付けることはできません。
各メッセージは、独自の非同期制御フローの中で処理する必要があります。 詳細については、「出力方向の依存関係の追跡」セクションを参照してください。
長時間実行されるバックグラウンド タスク
アプリケーションの中には、ユーザーの要求によって発生する、長時間実行される操作を開始するものがあります。 トレース/インストルメンテーションの観点から見ると、これは要求や依存関係のインストルメンテーションと変わりません。
async Task BackgroundTask()
{
var operation = telemetryClient.StartOperation<DependencyTelemetry>(taskName);
operation.Telemetry.Type = "Background";
try
{
int progress = 0;
while (progress < 100)
{
// Process the task.
telemetryClient.TrackTrace($"done {progress++}%");
}
// Update status code and success as appropriate.
}
catch (Exception e)
{
telemetryClient.TrackException(e);
// Update status code and success as appropriate.
throw;
}
finally
{
telemetryClient.StopOperation(operation);
}
}
この例では、telemetryClient.StartOperation
で DependencyTelemetry
を作成し、関連付けのコンテキストを設定しています。 操作のスケジュールが設定された受信要求によって作成された親操作があるとします。 BackgroundTask
が受信要求と同じ非同期制御フローで開始されていれば、受信要求はその親操作と関連付けられます。 BackgroundTask
と、入れ子になっているすべてのテレメトリ項目は、(要求の終了後も) それを発生させた要求と自動的に関連付けられます。
いずれの操作 (Activity
) も関連付けられていないバックグラウンド スレッドからタスクが開始された場合、BackgroundTask
には親が存在しません。 ただし、入れ子になった操作が存在する可能性があります。 そのタスクから報告されるすべてのテレメトリ項目は、BackgroundTask
で作成された DependencyTelemetry
に関連付けられます。
出力方向の依存関係の追跡
Application Insights がサポートしていない独自の依存関係や操作を追跡することができます。
このようなカスタム追跡の例として、Service Bus キューまたは Azure Storage キューの Enqueue
メソッドを挙げることができます。
カスタム依存関係の追跡に使用される一般的な手法は次のとおりです。
- 関連付けに必要な
DependencyTelemetry
プロパティと、start、time stamp、duration などのその他のプロパティを設定するTelemetryClient.StartOperation
(拡張機能) メソッドを呼び出します。 DependencyTelemetry
のその他のカスタム プロパティ (名前やその他の必要なプロパティ) を設定します。- 依存関係呼び出しを行い、応答が返るまで待機します。
- 操作が完了したら、
StopOperation
を使用して操作を停止します。 - 例外を処理します。
public async Task RunMyTaskAsync()
{
using (var operation = telemetryClient.StartOperation<DependencyTelemetry>("task 1"))
{
try
{
var myTask = await StartMyTaskAsync();
// Update status code and success as appropriate.
}
catch(...)
{
// Update status code and success as appropriate.
}
}
}
操作を破棄すると操作が停止されるため、StopOperation
を呼び出す代わりに実行することもできます。
警告
場合によっては、未処理の例外によって finally
の呼び出しが妨げられる可能性があるため、操作が追跡されないことがあります。
並列操作の処理と追跡
StopOperation
の呼び出しは、開始された操作を停止するだけです。 現在実行中の操作が停止する操作と一致しない場合、StopOperation
は何も行いません。 この状況は、同じ実行コンテキストで複数の操作を並行して開始した場合に起こることがあります。
var firstOperation = telemetryClient.StartOperation<DependencyTelemetry>("task 1");
var firstTask = RunMyTaskAsync();
var secondOperation = telemetryClient.StartOperation<DependencyTelemetry>("task 2");
var secondTask = RunMyTaskAsync();
await firstTask;
// FAILURE!!! This will do nothing and will not report telemetry for the first operation
// as currently secondOperation is active.
telemetryClient.StopOperation(firstOperation);
await secondTask;
並行して実行されている操作を分離するには、常に同じ async メソッドで StartOperation
を呼び出して操作を処理する必要があります。 操作が同期の場合 (つまり非同期ではない場合) は、プロセスをラップし、Task.Run
を使用して追跡します。
public void RunMyTask(string name)
{
using (var operation = telemetryClient.StartOperation<DependencyTelemetry>(name))
{
Process();
// Update status code and success as appropriate.
}
}
public async Task RunAllTasks()
{
var task1 = Task.Run(() => RunMyTask("task 1"));
var task2 = Task.Run(() => RunMyTask("task 2"));
await Task.WhenAll(task1, task2);
}
ApplicationInsights 操作と System.Diagnostics.Activity
System.Diagnostics.Activity
は、分散トレース コンテキストを表し、プロセスの内部および外部でコンテキストを作成および伝達し、テレメトリ項目を関連付けるために、フレームワークおよびライブラリで使用されます。 Activity
は、フレームワーク/ライブラリ間の通知メカニズムとして System.Diagnostics.DiagnosticSource
と連携し、受信や送信要求、例外などの重要なイベントについて通知します。
アクティビティは、Application Insights の第一級オブジェクトです。 依存関係と要求の自動収集は、DiagnosticSource
イベントと共にそれらに大きく依存しています。 アプリケーションで Activity
を作成する場合、Application Insights テレメトリが作成される結果にはなりません。 Application Insights は、DiagnosticSource
イベントを受信してイベント名とペイロードを認識し、Activity
をテレメトリに変換する必要があります。
Application Insights 操作 (要求または依存関係) には、それぞれ Activity
が含まれます。 StartOperation
が呼び出されると、その下に Activity
が作成されます。 StartOperation
は、要求または依存関係テレメトリを追跡し、すべてが関連付けられていることを確認する推奨の方法です。
次のステップ
- Application Insights におけるテレメトリの相関付けについて基本的な知識を身に付けます。
- 相関データでトランザクションの診断エクスペリエンスとアプリケーション マップがどのように機能するかを確認します。
- Application Insights の型とデータ モデルについては、データ モデルに関するページを参照してください。
- カスタムのイベントとメトリックを Application Insights にレポートします。
- コンテキスト プロパティ コレクションの標準的な構成を確認します。
- テレメトリを相互に関連付ける方法を System.Diagnostics.Activity ユーザー ガイドで確認します。