.NET には、特定の条件が満たされるまでログの出力を遅延できるログ バッファリング機能が用意されています。 ログ バッファリングは、次のようなシナリオで役立ちます。
- 出力するかどうかを決定する前に、特定の操作からすべてのログを収集します。
- 通常の操作中にログが出力されないようにしますが、エラーが発生したときに出力されます。
- ストレージに書き込まれるログの数を減らすことで、パフォーマンスを最適化します。
バッファーされたログは、プロセス メモリ内の一時的な循環バッファーに格納され、次の条件が適用されます。
- バッファーがいっぱいの場合、最も古いログは削除され、出力されません。
- バッファーされたログを出力する場合は、Flush()または GlobalLogBuffer クラスでPerRequestLogBufferを呼び出すことができます。
- バッファーをフラッシュしない場合、バッファーされたログは最終的にアプリケーションの実行時に削除されるため、これらのログが無効になっているように効果的に動作します。
次の 2 つのバッファリング戦略を使用できます。
- グローバル バッファリング: アプリケーション全体でログをバッファーします。
- 要求ごとのバッファリング: 使用可能な場合は、個々の HTTP 要求ごとにログをバッファーします。それ以外の場合は、グローバル バッファーにバッファーします。
注
ログ バッファリングは、.NET 9 以降のバージョンで使用できます。
ログ バッファリングは、すべてのログ プロバイダーで機能します。 使用するログ プロバイダーが IBufferedLogger インターフェイスを実装していない場合、ログ バッファリングでは、バッファーをフラッシュするときに、バッファーに格納された各ログ レコードに対してログ メソッドが直接呼び出されます。
ログ バッファリングは、ログを一時的にキャプチャして格納できるようにすることで 、フィルター機能 を拡張します。 バッファリングでは、即時の出力または破棄の決定を行うのではなく、ログをメモリに保持し、後で出力するかどうかを決定できます。
概要
作業を開始するには、📦用の Microsoft.Extensions.Telemetry NuGet パッケージをインストールします。 または、📦のために、 Microsoft.AspNetCore.Diagnostics.Middleware NuGet パッケージをインストールします。
dotnet add package Microsoft.Extensions.Telemetry
dotnet add package Microsoft.AspNetCore.Diagnostics.Middleware
パッケージの追加の詳細については、 dotnet でのパッケージの追加 または .NET アプリケーションでのパッケージの依存関係の管理に関するページを参照してください。
グローバル バッファリング
グローバル バッファリングを使用すると、アプリケーション全体でログをバッファーできます。 フィルター規則を使用してバッファーするログを構成し、必要に応じてバッファーをフラッシュしてそれらのログを出力できます。
シンプルな構成
特定のログ レベル以下でグローバル バッファリングを有効にするには、そのレベルを指定します。
// Add the Global buffer to the logging pipeline.
hostBuilder.Logging.AddGlobalBuffer(LogLevel.Information);
上記の構成では、レベル LogLevel.Information 以下のログをバッファリングできます。
ファイルベースの構成
appsettings.jsonに構成セクションを作成します。次に例を示します。
{
"Logging": {
"LogLevel": {
"Default": "Information"
},
"GlobalLogBuffering": {
"MaxBufferSizeInBytes": 104857600,
"MaxLogRecordSizeInBytes": 51200,
"AutoFlushDuration": "00:00:30",
"Rules": [
{
"CategoryName": "BufferingDemo",
"LogLevel": "Information"
},
{
"EventId": 1001
}
]
}
}
}
上記の構成:
- レベル
BufferingDemo以下のLogLevel.Informationで始まるカテゴリのログをバッファーします。 - すべてのログをイベント ID 1001 でバッファーします。
- 最大バッファー サイズを約 100 MB に設定します。
- ログ レコードの最大サイズを 50 KB に設定します。
- 手動フラッシュ後 30 秒の自動フラッシュ期間を設定します。
ログ バッファリングを構成に登録するには、次のコードを検討してください。
// Add the Global buffer to the logging pipeline.
hostBuilder.Logging.AddGlobalBuffer(hostBuilder.Configuration.GetSection("Logging"));
インライン コードの構成
// Add the Global buffer to the logging pipeline.
hostBuilder.Logging.AddGlobalBuffer(options =>
{
options.MaxBufferSizeInBytes = 104857600; // 100 MB
options.MaxLogRecordSizeInBytes = 51200; // 50 KB
options.AutoFlushDuration = TimeSpan.FromSeconds(30);
options.Rules.Add(new LogBufferingFilterRule(
categoryName: "BufferingDemo",
logLevel: LogLevel.Information));
options.Rules.Add(new LogBufferingFilterRule(eventId: 1001));
});
上記の構成:
- レベル
BufferingDemo以下のLogLevel.Informationで始まるカテゴリのログをバッファーします。 - すべてのログをイベント ID 1001 でバッファーします。
- 最大バッファー サイズを約 100 MB に設定します。
- ログ レコードの最大サイズを 50 KB に設定します。
- 手動フラッシュ後 30 秒の自動フラッシュ期間を設定します。
バッファをフラッシュする
バッファーされたログをフラッシュするには、 GlobalLogBuffer 抽象クラスを挿入し、 Flush() メソッドを呼び出します。
public class MyService
{
private readonly GlobalLogBuffer _buffer;
public MyService(GlobalLogBuffer buffer)
{
_buffer = buffer;
}
public void HandleException(Exception ex)
{
_buffer.Flush();
// After flushing, log buffering will be temporarily suspended (= all logs will be emitted immediately)
// for the duration specified by AutoFlushDuration.
}
}
要求ごとのバッファリング
要求ごとのバッファリングは、ASP.NET Core アプリケーションに固有であり、HTTP 要求ごとにログを個別にバッファーできます。 各要求のバッファーは、要求の開始時に作成され、要求の終了時に破棄されるため、バッファーをフラッシュしないと、要求の終了時にログが失われます。 この方法では、エラーが発生したときなど、本当に必要な場合にのみバッファーをフラッシュすると便利です。
要求ごとのバッファリングは、 グローバル バッファリングと密接に結び付けられます。 ログ エントリが要求ごとのバッファーにバッファリングされることになっているが、バッファー試行の時点でアクティブな HTTP コンテキストがない場合は、代わりにグローバル バッファーにバッファーされます。 バッファー フラッシュがトリガーされると、要求ごとのバッファーが最初にフラッシュされ、次にグローバル バッファーがフラッシュされます。
シンプルな構成
特定のログ レベル以下のログのみをバッファー処理するには:
builder.Logging.AddPerIncomingRequestBuffer(LogLevel.Information);
ファイルベースの構成
appsettings.jsonで構成セクションを作成します。
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.*": "None"
},
"PerIncomingRequestLogBuffering": {
"AutoFlushDuration": "00:00:05",
"Rules": [
{
"CategoryName": "PerRequestLogBufferingFileBased.*",
"LogLevel": "Information"
}
]
}
}
}
上記の構成:
- レベル
PerRequestLogBufferingFileBased.以下のLogLevel.Informationで始まるカテゴリのログをバッファーします。 - 手動フラッシュ後 5 秒の自動フラッシュ期間を設定します。
ログ バッファリングを構成に登録するには、次のコードを検討してください。
builder.Logging.AddPerIncomingRequestBuffer(builder.Configuration.GetSection("Logging"));
インライン コードの構成
builder.Logging.AddPerIncomingRequestBuffer(options =>
{
options.AutoFlushDuration = TimeSpan.FromSeconds(5);
options.Rules.Add(new Microsoft.Extensions.Diagnostics.Buffering.LogBufferingFilterRule("PerRequestLogBufferingCodeBased.*", LogLevel.Information));
});
上記の構成:
- レベル
PerRequestLogBufferingFileBased.以下のLogLevel.Informationで始まるカテゴリのログをバッファーします。 - 手動フラッシュ後 5 秒の自動フラッシュ期間を設定します。
要求ごとのバッファーのフラッシュ
現在の要求のバッファーされたログをフラッシュするには、 PerRequestLogBuffer 抽象クラスを挿入し、その Flush() メソッドを呼び出します。
[ApiController]
[Route("[controller]")]
public class HomeController : ControllerBase
{
private readonly ILogger<HomeController> _logger;
private readonly PerRequestLogBuffer _buffer;
public HomeController(ILogger<HomeController> logger, PerRequestLogBuffer buffer)
{
_logger = logger;
_buffer = buffer;
}
[HttpGet("index/{id}")]
public IActionResult Index(int id)
{
try
{
_logger.RequestStarted(id);
// Simulate exception every 10th request
if (id % 10 == 0)
{
throw new Exception("Simulated exception in controller");
}
_logger.RequestEnded(id);
return Ok();
}
catch
{
_logger.ErrorMessage(id);
_buffer.Flush();
_logger.ExceptionHandlingFinished(id);
return StatusCode(500, "An error occurred.");
}
}
}
注
要求ごとのバッファーをフラッシュすると、グローバル バッファーもフラッシュされます。
バッファリング ルールの適用方法
ログ バッファリング ルールの評価は、各ログ レコードに対して実行されます。 各ログ レコードには、次のアルゴリズムが使用されます。
- ログ エントリが任意のルールと一致する場合は、すぐに出力されるのではなく、バッファーに格納されます。
- ログ エントリがルールに一致しない場合は、通常どおりに出力されます。
- バッファー サイズの上限に達すると、最も古いバッファー内のログ エントリが削除され (出力されません)、新しいログ エントリの空き容量が作成されます。
- ログ エントリのサイズがログ レコードの最大サイズより大きい場合、バッファーに格納されず、通常どおりに出力されます。
各ログ レコードについて、アルゴリズムは以下をチェックします。
- ログ レベルがルールのログ レベルと一致する (等しいかそれより低い) 場合。
- カテゴリ名がルールの
CategoryNameプレフィックスで始まる場合。 - イベント ID がルールの
EventIdと一致する場合。 - イベント名がルールの
EventNameと一致する場合。 - いずれかの属性がルールの
Attributesと一致する場合。
実行中のアプリでバッファー フィルター規則を変更する
グローバル バッファリングと要求ごとのバッファリングの両方で、IOptionsMonitor<TOptions> インターフェイスを介したランタイム構成の更新がサポートされます。 ファイル構成プロバイダーなどの再読み込みをサポートする構成プロバイダーを使用している場合は、アプリケーションを再起動せずに実行時にフィルター処理規則を更新できます。
たとえば、次の appsettings.jsonを使用してアプリケーションを起動できます。これにより、LogLevel.Information以降のPerRequestLogBufferingFileBased. レベルとカテゴリを持つログのログ バッファリングが有効になります。
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.*": "None"
},
"PerIncomingRequestLogBuffering": {
"AutoFlushDuration": "00:00:05",
"Rules": [
{
"CategoryName": "PerRequestLogBufferingFileBased.*",
"LogLevel": "Information"
}
]
}
}
}
アプリの実行中に、次の構成で appsettings.json を更新できます。
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.*": "None"
},
"PerIncomingRequestLogBuffering": {
"Rules": [
{
"LogLevel": "Information"
}
]
}
}
}
新しいルールは自動的に適用されます。 たとえば、上記の構成では、 LogLevel.Information レベルのすべてのログがバッファーに格納されます。
パフォーマンスに関する考慮事項
ログ バッファリングは、メモリ使用量とログ ストレージ コストのトレードオフを提供します。 メモリ内のログをバッファリングすると、次のことができます。
- ランタイム条件に基づいてログを選択的に出力します。
- 不要なログをストレージに書き込まずに削除します。
ただし、特に高スループットのアプリケーションでは、メモリ消費量に注意してください。 メモリの過剰な使用を防ぐために、適切なバッファー サイズの制限を構成します。
ベスト プラクティス
- アプリケーションのメモリ制約に基づいて適切なバッファー サイズ制限を設定します。
- Web アプリケーションの要求ごとのバッファリングを使用して、要求ごとにログを分離します。
- メモリ使用量とログの可用性のバランスを取るために、自動フラッシュ期間を慎重に構成します。
- 重要なイベント (エラーや警告など) の明示的なフラッシュ トリガーを実装します。
- 運用環境でのバッファー メモリ使用量を監視して、許容される制限内に収まるようにします。
制限事項
- .NET 8 以前のバージョンでは、ログ バッファリングはサポートされていません。
- ログの順序は保持されるとは限りません。 ただし、元のタイムスタンプは保持されます。
- 各ログ プロバイダーごとのカスタム構成はサポートされていません。 すべてのプロバイダーで同じ構成が使用されます。
- ログ スコープはサポートされていません。 つまり、 BeginScope メソッドを使用する場合、バッファー内のログ レコードはスコープに関連付けられません。
- 元のログ レコードのすべての情報が保持されるわけではありません。 ログ バッファリングでは、フラッシュ時に BufferedLogRecord クラスが内部的に使用され、そのプロパティの次の値は常に空です。
こちらも参照ください
.NET