Start Debugging

.NET Extensions 10.11: バッファーされたログがついにスコープを保持できるようになりました

2026-10-15 にリリースされた Microsoft.Extensions.Telemetry 10.11.0 は、グローバルおよびリクエスト単位のログ バッファリングに実験的な IncludeScopes オプションを追加しました。これにより BeginScope の OrderId や TenantId が、黙って失われることなく Flush() 後も残ります。

.NET Extensions 10.11.0 が 2026-10-15 に NuGet で公開されました。AI 関連の修正はほとんどが小さなものですが、テレメトリの変更が 1 つ、ログ バッファリングの登場以来ずっと残っていた穴を塞いでいます。バッファーされたログ レコードは、ILogger.BeginScope の値をすべて失っていました。Information ログをバッファーし、例外が発生して Flush() を呼ぶと、スコープに入れておいた OrderId、TenantId、リクエスト ID が抜けたレコードが出力されていたのです。障害のデバッグに必要なコンテキストそのものです。

これまでの Flush の出力

ログ バッファリングは Microsoft.Extensions.Telemetry (グローバル バッファー) と Microsoft.AspNetCore.Diagnostics.Middleware (リクエスト単位のバッファー) にあります。ルールに一致したレコードは循環バッファーに保持され、Flush() を呼んだときにだけ書き出されます。次は JSON コンソール ロガーを使った最小の再現コードです。このロガーは通常、IncludeScopes が有効ならスコープを出力します。

var builder = Host.CreateApplicationBuilder();
builder.Logging.ClearProviders();
builder.Logging.AddJsonConsole(o => o.IncludeScopes = true);
builder.Logging.AddGlobalBuffer(o =>
{
    o.Rules.Add(new LogBufferingFilterRule(logLevel: LogLevel.Information));
    o.IncludeScopes = true; // new in 10.11.0, EXTEXP0003
});
using var host = builder.Build();
var logger = host.Services.GetRequiredService<ILogger<Program>>();
var buffer = host.Services.GetRequiredService<GlobalLogBuffer>();

using (logger.BeginScope(new Dictionary<string, object?> { ["OrderId"] = 4711, ["TenantId"] = "contoso" }))
{
    logger.LogInformation("Reserving stock for {Sku}", "SKU-42");
    buffer.Flush();
}

SDK 10.0.302 上でファイルベース アプリとして実行しました。Microsoft.Extensions.Telemetry 10.10.0 (そこではコンパイルできない IncludeScopes の行は削除) では、Flush がスコープの内側で行われているにもかかわらず、出力されたレコードにスコープの痕跡はまったくありません。

{"EventId":0,"LogLevel":"Information","Category":"Program","Message":"Reserving stock for SKU-42","Exception":"","State":{"Message":null,"Sku":"SKU-42","{OriginalFormat}":"Reserving stock for {Sku}"}}

10.11.0 で IncludeScopes = true にした場合です。

{"EventId":0,"LogLevel":"Information","Category":"Program","Message":"Reserving stock for SKU-42","Exception":"","State":{"Message":null,"Sku":"SKU-42","OrderId":"4711","TenantId":"contoso","{OriginalFormat}":"Reserving stock for {Sku}"}}

仕組み

PR #7813 は、Flush の時点ではなく、レコードがバッファーされた瞬間にスコープを取得します。これが望ましい動作です。誰かが Flush() を呼ぶころには、元のスコープはたいてい終わっているからです。バッファーは DI から IExternalScopeProvider を受け取り (ExtendedLoggerFactory と共有)、各スコープの名前と値のペアをレコードにコピーします。

知っておくべき点がいくつかあります。

ASP.NET Core のリクエスト単位のバッファリングでは、同じスイッチが PerRequestLogBufferingOptions にあります。

builder.Logging.AddPerIncomingRequestBuffer(o =>
{
    o.Rules.Add(new LogBufferingFilterRule(logLevel: LogLevel.Information));
    o.IncludeScopes = true;
});

10.11.0 のその他の変更

このリリースでは、リクエスト終了時のリクエスト単位バッファリングで発生する ArgumentNullException (#7811) と、ルール セレクターのキャッシュが原因のバッファリングの競合状態 (#7803) も修正されています。実験的な HTTP レイテンシ エンリッチャーを使っている場合、AddHttpLatencyTelemetry は AddHttpServerLatencyTelemetry に名前が変わりました (#7730)。AI 側では、関数が例外をスローした場合にも FunctionInvocationContext.Terminate が尊重されるようになり (#7801)、ChatResponseUpdate.Clone が ContinuationToken を保持するようになりました (#7699)。このシリーズの前回のリリースは Microsoft.Extensions.AI 10.10.1 でした。

アップグレードするには次を実行します。

dotnet add package Microsoft.Extensions.Telemetry --version 10.11.0

ログをバッファーしてスコープの値で相関付けているなら、IncludeScopes を有効にしてください。これがないと、障害時に Flush したログには、相関付けに必要な ID が含まれません。

Comments

Sign in with GitHub to comment. Reactions and replies thread back to the comments repo.

< 戻る