こんにちは、福岡も寒い日が続いていて鍋物が美味しい季節ですが、先日妻が作ってくれたおでんを見た娘が「パパ、おでんにしっぺ入ってる?」というので何のことだと頭をひねった木村です(後に、はんぺんのことだと判明しました)。
先日Azure Functionsで.NET8を使って関数アプリを書いたのですが、ログの出力でうまくログレベルが設定できずに色々調査・検証したのでその件について書こうと思います。
なお、本記事は2024年1月24日時点の情報に基づいています。また、利用している各種バージョンは以下の通りです。
- Azure Functions: v4
- Azure Functions Core Tools : 4.0.5455
- dotnet: 8.0.101
- Visual Studio 2022: 17.8.5
テンプレート通りのアプリを実行
まずはVisualStudioから新しいプロジェクトを作成し、テンプレートとしてAzure Funcitonsを選択します。そしてワーカーとして.NET8 Isolated
を選択し、トリガーとしてQueue trigger
を選択します。
そのままだとパッケージの依存関係の問題でビルドできなかったので、nugetパッケージマネージャーから全てのパッケージを更新し、Azure.Core
とSystem.Runtime
の2つのパッケージもインストールしました。
そして、今回問題になったケースが分かりやすいように、起動される関数(テンプレートで作成した場合はファイル名はFunction1.cs
)を以下のように変更します。全てのログレベルでログを出力してみて、実際にどのレベルが出力されるかを確認します。
using System; using Azure.Storage.Queues.Models; using Microsoft.Azure.Functions.Worker; using Microsoft.Extensions.Logging; namespace FunctionApp2 { public class Function1 { private readonly ILogger<Function1> _logger; public Function1(ILogger<Function1> logger) { _logger = logger; } [Function(nameof(Function1))] public void Run([QueueTrigger("cshrp-queue-items", Connection = "storageConnectionString")] QueueMessage message) { _logger.LogTrace("This is LogTrace"); _logger.LogDebug("This is LogDebug"); _logger.LogInformation("This is LogInformation"); _logger.LogWarning("This is LogWarning"); _logger.LogError("This is LogError"); _logger.LogCritical("This is LogCritical"); } } }
では、この関数をデプロイして動かしてみます。詳細は割愛しますが、Visual Studioの機能で発行プロファイルを作成し、デプロイ先のFunciton Appも作成します。この過程でApplicaiton Insightsも設定されています。
デプロイに成功したらトリガーとなるストレージキューを作成して接続文字列を設定し、ポータルからキューにメッセージを投げ込んで動作確認を行います。
Information以下のレベルのログが出ない?
さて、動作状況のログを見てみましょう。Application Insightsでtracesを表示してみます。
上記の通り、Warning以上のログしか出力されていません。デバッグのことを考えるとInformation以上が出力されるようにしておきたいところです。
ロギングの設定を調べる
これまでの経験ではデプロイするアプリケーションに同梱しているhost.json
で設定するとよかったのですが、あれこれ記載してみたのですがうまくいきません。
改めて公式ドキュメントを確認したところ、以下の記載を見つけました。
Some language stacks allow you to instead send the logs directly to Application Insights, giving you full control over how logs you write are emitted. The logging pipeline changes from worker -> Functions host -> Application Insights to worker -> Application Insights.
言語スタックによってはFunctionsのホストを経由せず、ワーカーから直接Applicaiton Insightsに送られるとなっています。
そして、上記ドキュメントで掲載されている表では、.NET(isolated model)
は
By default: host.json
Option to send logs directly: Configure Application Insights in the HostBuilder
となっています。ホストを経由しないのだから、host.json
での設定では変更できないのは納得です。
ログレベルを変更する(1)
続いて、HostBuilderで変更する手順を探してみます。
先ほどのドキュメントからリンクされている、以下のドキュメントに記載があります。
However, by default, the Application Insights SDK adds a logging filter that instructs the logger to capture only warnings and more severe logs. If you want to disable this behavior, remove the filter rule as part of service configuration:
Applicaiton Insights SDKの規定の動作としてWarning以上のみ出力するというフィルターがついており、レベルを変更する場合はこれを削除しないといけないようです。
削除するためのコードは上記ドキュメントに記載されています。それを参考に、修正したProgram.cs
は以下の通りです。ConfiguraLogging
の呼び出しを追加しています。
using Microsoft.Azure.Functions.Worker; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; var host = new HostBuilder() .ConfigureFunctionsWorkerDefaults() .ConfigureServices(services => { services.AddApplicationInsightsTelemetryWorkerService(); services.ConfigureFunctionsApplicationInsights(); }) .ConfigureLogging(logging => { logging.Services.Configure<LoggerFilterOptions>(options => { LoggerFilterRule defaultRule = options.Rules.FirstOrDefault(rule => rule.ProviderName == "Microsoft.Extensions.Logging.ApplicationInsights.ApplicationInsightsLoggerProvider"); if (defaultRule is not null) { options.Rules.Remove(defaultRule); } }); }) .Build(); host.Run();
デプロイしたらまたキューにメッセージを追加して関数を起動し、Application Insightsを確認してみます。
Information以上が出力されるようになりました。
ログレベルを変更する(2)
さて、ひとまずInformationは出るようになったので、次は他のレベルを指定できるようにしましょう。appsettings.json
やアプリケーション構成で設定したいので、以下のドキュメントに従ってアプリケーション構成にLogging__ApplicationInsights__LogLevel__Default
という設定を入れてみます。
そしてまた関数を起動してみました。うまくいけばDebug以上のメッセージが出るはずですが、残念ながらこれだけではだめでした。
さらに調査を進めると、以下のIssueに辿り着きました。
これによると、HostBuilderの中でConfiguraLogging
を呼び、ログの設定がどこにあるか(この場合はLogging
セクション)を指定しなければならないようです。
早速以下のようにProgram.cs
を修正します。
using Microsoft.Azure.Functions.Worker; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; var host = new HostBuilder() .ConfigureFunctionsWorkerDefaults() .ConfigureServices(services => { services.AddApplicationInsightsTelemetryWorkerService(); services.ConfigureFunctionsApplicationInsights(); }) .ConfigureLogging(logging => { logging.Services.Configure<LoggerFilterOptions>(options => { LoggerFilterRule defaultRule = options.Rules.FirstOrDefault(rule => rule.ProviderName == "Microsoft.Extensions.Logging.ApplicationInsights.ApplicationInsightsLoggerProvider"); if (defaultRule is not null) { options.Rules.Remove(defaultRule); } }); }) .ConfigureLogging((hostingContext, logging) => { logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging")); }) .Build(); host.Run();
デプロイロして実行してみた結果はこちらです。
無事表示されました!
まとめ
Azure Funcitons(v4)の分離ワーカーモデルで動かしている.NETの関数アプリからApplicaiton Insightsに送るログのログレベルを設定するにはちょっとした注意が必要でした。
具体的には
- ログはホストを介さず直接SDKがApplication Insightsに送るので、
host.json
ではログの設定できない - Application Insight SDKは規定でWarning以上しか送らないフィルターが設定されているので、ログレベルを設定するならばHostBuilderで削除する
- ログレベルを
appsettings.json
やアプリケーション構成で設定する場合は、そのセクション名をHostbuilderで指定する
という3つになります。
繰り返しになりますが、本記事は20204年1月24日時点の情報と検証結果に基づいていますので、将来的にはSDKの更新等で動作が変わる可能性もありますので必ず公式のドキュメントも参照いただけますようお願いいたします。
皆さんのお役に立てば幸いです。
サービス一覧 www.alterbooth.com cloudpointer.tech www.alterbooth.com