次の方法で共有


.NET でのログ バッファリング

.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.");
        }
    }
}

要求ごとのバッファーをフラッシュすると、グローバル バッファーもフラッシュされます。

バッファリング ルールの適用方法

ログ バッファリング ルールの評価は、各ログ レコードに対して実行されます。 各ログ レコードには、次のアルゴリズムが使用されます。

  1. ログ エントリが任意のルールと一致する場合は、すぐに出力されるのではなく、バッファーに格納されます。
  2. ログ エントリがルールに一致しない場合は、通常どおりに出力されます。
  3. バッファー サイズの上限に達すると、最も古いバッファー内のログ エントリが削除され (出力されません)、新しいログ エントリの空き容量が作成されます。
  4. ログ エントリのサイズがログ レコードの最大サイズより大きい場合、バッファーに格納されず、通常どおりに出力されます。

各ログ レコードについて、アルゴリズムは以下をチェックします。

  • ログ レベルがルールのログ レベルと一致する (等しいかそれより低い) 場合。
  • カテゴリ名がルールの 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 レベルのすべてのログがバッファーに格納されます。

パフォーマンスに関する考慮事項

ログ バッファリングは、メモリ使用量とログ ストレージ コストのトレードオフを提供します。 メモリ内のログをバッファリングすると、次のことができます。

  1. ランタイム条件に基づいてログを選択的に出力します。
  2. 不要なログをストレージに書き込まずに削除します。

ただし、特に高スループットのアプリケーションでは、メモリ消費量に注意してください。 メモリの過剰な使用を防ぐために、適切なバッファー サイズの制限を構成します。

ベスト プラクティス

  • アプリケーションのメモリ制約に基づいて適切なバッファー サイズ制限を設定します。
  • Web アプリケーションの要求ごとのバッファリングを使用して、要求ごとにログを分離します。
  • メモリ使用量とログの可用性のバランスを取るために、自動フラッシュ期間を慎重に構成します。
  • 重要なイベント (エラーや警告など) の明示的なフラッシュ トリガーを実装します。
  • 運用環境でのバッファー メモリ使用量を監視して、許容される制限内に収まるようにします。

制限事項

  • .NET 8 以前のバージョンでは、ログ バッファリングはサポートされていません。
  • ログの順序は保持されるとは限りません。 ただし、元のタイムスタンプは保持されます。
  • 各ログ プロバイダーごとのカスタム構成はサポートされていません。 すべてのプロバイダーで同じ構成が使用されます。
  • ログ スコープはサポートされていません。 つまり、 BeginScope メソッドを使用する場合、バッファー内のログ レコードはスコープに関連付けられません。
  • 元のログ レコードのすべての情報が保持されるわけではありません。 ログ バッファリングでは、フラッシュ時に BufferedLogRecord クラスが内部的に使用され、そのプロパティの次の値は常に空です。

こちらも参照ください