Kestrel での診断

Sourabh Shirhatti による投稿

この記事では、問題のトラブルシューティングに役立つ診断情報を Kestrel から収集するためのガイダンスを提供します。 取り上げるトピックは次のとおりです。

  • ログ: .NET Core ログに書き込まれる構造化ログ。 ILogger は、ログを書き込むためにアプリケーション フレームワークによって使用され、アプリで独自のログ記録を行うためユーザーによって使用されます。
  • メトリック: 1 秒あたりの要求数など、一定期間にわたるデータ測定値を表します。 メトリックは EventCounter を使用して出力され、dotnet-counters コマンド ライン ツールまたは Application Insights を使用して観察できます。
  • DiagnosticSource: DiagnosticSource は、プロセス内での使用に関する、リッチ データ ペイロードを含む運用時のログ記録を行うためのメカニズムです。 データがプロセスから移動することが前提であり、シリアル化可能なデータを必要とするログとは異なり、DiagnosticSource は複雑なデータに適しています。

ログの記録

ASP.NET Core のほとんどのコンポーネントと同様に、Kestrel では Microsoft.Extensions.Logging を使用してログ情報を出力します。 Kestrel では、複数のカテゴリを使用することにより、リッスンするログを選択できます。

ログ記録のカテゴリ名 ログ記録のイベント
Microsoft.AspNetCore.Server.Kestrel ApplicationErrorConnectionHeadResponseBodyWriteApplicationNeverCompletedRequestBodyStartRequestBodyDoneRequestBodyNotEntirelyReadRequestBodyDrainTimedOutResponseMinimumDataRateNotSatisfiedInvalidResponseHeaderRemovedHeartbeatSlow
Microsoft.AspNetCore.Server.Kestrel.BadRequests ConnectionBadRequestRequestProcessingErrorRequestBodyMinimumDataRateNotSatisfied
Microsoft.AspNetCore.Server.Kestrel.Connections ConnectionAccepted, ConnectionStart, ConnectionStop, ConnectionPause, ConnectionResume, ConnectionKeepAlive, ConnectionRejected, ConnectionDisconnect, NotAllConnectionsClosedGracefully, NotAllConnectionsAborted, ApplicationAbortedConnection
Microsoft.AspNetCore.Server.Kestrel.Http2 Http2ConnectionErrorHttp2ConnectionClosingHttp2ConnectionClosedHttp2StreamErrorHttp2StreamResetAbortHPackDecodingErrorHPackEncodingErrorHttp2FrameReceivedHttp2FrameSendingHttp2MaxConcurrentStreamsReached
Microsoft.AspNetCore.Server.Kestrel.Http3 Http3ConnectionError, Http3ConnectionClosing, Http3ConnectionClosed, Http3StreamAbort, Http3FrameReceived, Http3FrameSending

接続のログ記録

Kestrel では、バイトレベルの通信用に Debug レベルのログを出力する機能もサポートされます。これはエンドポイントごとに有効にできます。 接続のログ記録を有効にするには、Kestrel のエンドポイントの構成に関する記事を参照してください

メトリック

メトリックは、1 秒あたりの要求数など、一定期間にわたるデータ測定値を表します。 メトリック データを使用すると、アプリの状態を高レベルで監視できます。 Kestrel メトリックは EventCounter を使用して出力されます。

Note

connections-per-second カウンターと tls-handshakes-per-second カウンターの名前は正しく指定されていません。 これらのカウンターには以下の条件が適用されます。

  • 1 秒あたりの新しい接続または TLS ハンドシェイクの数が必ずしも含まれているわけでは "ありません"。
  • KestrelEventSource に対して filterPayloadEventCounterIntervalSec 引数を使用してイベントのコンシューマーとして要求された、前回の更新期間における新しい接続または TLS ハンドシェイクの数が表示されます。

これらのカウンターのコンシューマーでは、1 秒の DisplayRateTimeScale に基づいてメトリック値を拡大縮小することをお勧めします

Name 表示名 説明
connections-per-second 接続速度 更新間隔ごとの新しい着信接続の数
total-connections 合計接続数 接続の総数
tls-handshakes-per-second TLS ハンドシェイク速度 更新間隔ごとの新しい TLS ハンドシェイクの数
total-tls-handshakes 合計 TLS ハンドシェイク数 TLS ハンドシェイクの総数
current-tls-handshakes 現在の TLS ハンドシェイク数 プロセス内の TLS ハンドシェイクの数
failed-tls-handshakes 失敗した TLS ハンドシェイク数 失敗した TLS ハンドシェイクの合計数
current-connections 現在の接続数 接続の総数 (アイドル状態の接続を含む)
connection-queue-length 接続キューの長さ スレッド プールのキューに登録された接続の総数。 状態が安定した正常なシステムでは、これが常に 0 に近い数値であることが必要です。
request-queue-length 要求キューの長さ スレッド プールのキューに登録された要求の総数。 状態が安定した正常なシステムでは、これが常に 0 に近い数値であることが必要です。 このメトリックは IIS/Http.Sys 要求キューとは異なり、比較できません。
current-upgraded-requests 現在アップグレードされている要求数 (WebSocket) アクティブな WebSocket の要求の数

DiagnosticSource

Kestrel では、形式に誤りがある要求やプロトコル違反など、サーバー レイヤーで拒否された HTTP 要求に対して DiagnosticSource イベントを出力します。 そのため、これらの要求が ASP.NET Core のホスティング レイヤーに到達することはありません。

Kestrel では、Microsoft.AspNetCore.Server.Kestrel.BadRequest イベントの名前と IFeatureCollection をオブジェクトのペイロードとして使用して、これらのイベントを出力します。 基になる例外は、機能コレクションの IBadRequestExceptionFeature にアクセスすることで取得できます。

これらのイベントの解決は、2 つのステップから成るプロセスです。 DiagnosticListener のオブザーバーを作成する必要があります。

class BadRequestEventListener : IObserver<KeyValuePair<string, object>>, IDisposable
{
    private readonly IDisposable _subscription;
    private readonly Action<IBadRequestExceptionFeature> _callback;

    public BadRequestEventListener(DiagnosticListener diagnosticListener, Action<IBadRequestExceptionFeature> callback)
    {
        _subscription = diagnosticListener.Subscribe(this!, IsEnabled);
        _callback = callback;
    }
    private static readonly Predicate<string> IsEnabled = (provider) => provider switch
    {
        "Microsoft.AspNetCore.Server.Kestrel.BadRequest" => true,
        _ => false
    };
    public void OnNext(KeyValuePair<string, object> pair)
    {
        if (pair.Value is IFeatureCollection featureCollection)
        {
            var badRequestFeature = featureCollection.Get<IBadRequestExceptionFeature>();

            if (badRequestFeature is not null)
            {
                _callback(badRequestFeature);
            }
        }
    }
    public void OnError(Exception error) { }
    public void OnCompleted() { }
    public virtual void Dispose() => _subscription.Dispose();
}

オブザーバーを使用して ASP.NET Core DiagnosticListener をサブスクライブします。 次の例では、基になる例外をログに記録するコールバックを作成します。

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
var diagnosticSource = app.Services.GetRequiredService<DiagnosticListener>();
using var badRequestListener = new BadRequestEventListener(diagnosticSource, (badRequestExceptionFeature) =>
{
    app.Logger.LogError(badRequestExceptionFeature.Error, "Bad request received");
});
app.MapGet("/", () => "Hello world");
app.Run();

デバッガーがアタッチされているときの動作

デバッガーが Kestrel プロセスにアタッチされているときは、適用されないタイムアウトとレート制限があります。 詳しくは、「デバッガーがアタッチされているときの動作」をご覧ください。