xin9le.net

Microsoft の製品/技術が大好きな Microsoft MVP な管理人の技術ブログです。

Azure Functions (Isolated) における ITelemetryInitializer / ITelemetryProcessor の注意点

最近 Azure Functions (Isolated) から出力される Application Insights のテレメトリと格闘していました。何を今更そんなに格闘することがあるのかというと、テレメトリの一種である RequestTelemetry に対するアクセスが全くできないという問題に遭遇したからです。今回はそんな挙動について調べたことを残していきます。

tl;dr;

  • Azure Functions (Isolated) において、一部のテレメトリ (RequestTelemetry など) にアクセスできない
  • Azure Functions (Isolated) が Host と Worker に分離したことに起因
  • Worker 側で ITelemetryInitializer 等を利用してテレメトリを操作しようにも、Host 側が出力したテレメトリには触れられない

同様の問題に対する Issue は既に挙がっています。

前提知識

まず Azure Functions の前提として、InProc Model と Isolated Model の違いを押さえておく必要があります。詳細は公式のドキュメントに譲りますが、Azure Functions を利用しているとこの違いがあれこれと面倒を起こします。

InProc Model

  • C#/.NET で動作させる場合の従来モデル
    • .NET 6 まではこちら
  • Function Host にアプリケーション .dll を読み込ませ、Host と一体となってコードを実行させる
  • .NET の最新バージョンを使いたい場合、Function Host が対応してくれない限りアプリ側が利用できない問題があった
    • それを解消するのが Isolated Model
    • Node.js など C#/.NET 以外で作る場合は最初から Isolated Model だったが、C#/.NET は特別に一体型だった

Isolated Model

  • Functions Host とアプリケーション側 (Worker) を分離させ、各々好き勝手できるようにしたモデル
    • .NET 6 以降で利用可能で、.NET 7 以降では完全にこちらのみ
  • Host / Worker の間は gRPC で繋がれている
    • これが色々と厄介な問題や制限を抱えるのだけど、一旦それはさておき...

検証

ということで、Isolated Model における Application Insights へのテレメトリ送信の実験をしてみます。Application Insights SDK はテレメトリ送信前をフックして内容を編集するための拡張ポイントである ITelemetryInitializerITelemetryProcessor を提供しています。今回は動作検証ということで ITelemetryInitializer のみで試してみましょう。以下のようなコードを書きます。

// テレメトリに投げる前に編集しちゃう君
internal class MyTelemetryInitializer : ITelemetryInitializer
{
    public void Initialize(ITelemetry telemetry)
    {
        var props = (ConcurrentDictionary<string, string>)telemetry.Context.Properties;
        props.TryAdd("SayHello", "Hello, World!!");  // カスタムプロパティを追加
        telemetry.Context.Component.Version = "1.2.3.4";  // バージョンを設定
    }
}
// Isolated Host を利用した Azure Functions の初期化処理
new HostBuilder()
    .ConfigureFunctionsWorkerDefaults(static (context, builder) =>
    {
        // DI に登録
        builder.Services.AddSingleton<ITelemetryInitializer>(new MyTelemetryInitializer());
    })
    .Build()
    .Run();
// いわゆる HttpTrigger
internal class Http_Isolated_DebugStart
{
    [Function(nameof(Http_Isolated_DebugStart))]
    public async Task<HttpResponseData> EntryPoint(
        [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "debug/start")] HttpRequestData request,
        FunctionContext context)
    {
        var logger = context.GetLogger<Http_Isolated_DebugStart>();
        logger.LogInformation($"Hello, Isolated!!");  // ログを吐いておく
        return request.CreateResponse(HttpStatusCode.OK);
    }
}

この状態で Azure にデプロイして Application Insights を確認してみます。すると次のような感じのテレメトリが出力されます。全プロパティを表示した画像のため拡大しないと見られないかもですが、その点はご了承ください。

RequestTelemetry (※A) TraceTelemetry (※A) TraceTelemetry (※B)
  • ※A : Host 側が出力
  • ※B : Worker 側が出力

※A で示した方には ITelemetryInitializer によるプロパティやバージョン値が入っていないことが分かります。一方で ※B で示した Worker 側で明示的に出力したログには値が入っていますね。Host と Worker が分離したことで、一部のテレメトリにはアクセスできていないことが確認できます。

まとめ

とういうことで Azure Functions (Isolated) におけるテレメトリ操作の注意点を簡単に見てきました。今回はサボッて載せませんが、InProc Model では (当然) すべてのテレメトリをフックして値を操作することができます。こういった部分の差分を認識して、運用時に困らないようにしていきたいですね。

Azure Functions (Isolated) は、まだまだ InProc と動作が異なる部分や InProc との機能差 (= Isolated の方が機能が足りてない) が残っています。Azure Functions チームはこれらを埋める作業を毎年地道に頑張っていて、最近では Durable Functions に対応したりもしています。応援!