Kestrel 中的诊断

注意

此版本不是本文的最新版本。 有关当前版本,请参阅本文.NET 9 版本。

警告

此版本的 ASP.NET Core 不再受支持。 有关详细信息,请参阅 .NET 和 .NET Core 支持策略。 对于当前版本,请参阅此文的 .NET 8 版本

重要

此信息与预发布产品相关,相应产品在商业发布之前可能会进行重大修改。 Microsoft 对此处提供的信息不提供任何明示或暗示的保证。

有关当前版本,请参阅本文.NET 9 版本。

作者:Sourabh Shirhatti

本文提供从 Kestrel 收集诊断以帮助解决问题的指南。 涵盖的主题包括:

  • 日志记录:写入 .NET Core 日志记录的结​​构化日志。 应用框架使用 ILogger 来编写日志,用户使用它在应用中进行用户自己的日志记录。
  • 指标:一段时间间隔内数据度量值的表示形式,例如每秒请求数。 指标是使用 EventCounter 发出的,可以使用 dotnet-counters 命令行工具或 Application Insights 进行观察。
  • DiagnosticSource:DiagnosticSource 是一种用于记录生产时间的机制,具有丰富的数据有效负载可供进程使用。 与日志记录不同,后者假设数据将离开进程并且需要可序列化的数据,DiagnosticSource 可以很好地处理复杂数据。

Logging

与 ASP.NET Core 中的大多数组件一样,Kestrel 使用 Microsoft.Extensions.Logging 发出日志信息。 Kestrel 使用多个类别,你可以选择要侦听的日志。

日志记录类别名称 日志记录事件
Microsoft.AspNetCore.Server.Kestrel ApplicationError、、ConnectionHeadResponseBodyWriteRequestBodyStartApplicationNeverCompletedRequestBodyDoneRequestBodyNotEntirelyReadRequestBodyDrainTimedOutResponseMinimumDataRateNotSatisfied、、 InvalidResponseHeaderRemovedHeartbeatSlow
Microsoft.AspNetCore.Server.Kestrel.BadRequests ConnectionBadRequestRequestProcessingError、、 RequestBodyMinimumDataRateNotSatisfied
Microsoft.AspNetCore.Server.Kestrel.Connections ConnectionAccepted、、ConnectionStartConnectionPauseConnectionStopConnectionResumeConnectionKeepAliveConnectionRejectedConnectionDisconnect、、NotAllConnectionsClosedGracefully、、 NotAllConnectionsAbortedApplicationAbortedConnection
Microsoft.AspNetCore.Server.Kestrel.Http2 Http2ConnectionError、、Http2ConnectionClosingHttp2StreamErrorHttp2ConnectionClosedHttp2StreamResetAbortHPackDecodingErrorHPackEncodingErrorHttp2FrameReceived、、 Http2FrameSendingHttp2MaxConcurrentStreamsReached
Microsoft.AspNetCore.Server.Kestrel.Http3 Http3ConnectionError、、Http3ConnectionClosingHttp3ConnectionClosedHttp3StreamAbortHttp3FrameReceivedHttp3FrameSending

连接日志记录

Kestrel 还支持为字节级别的通信发出 Debug 级别日志,并可根据每个终结点启用该功能。 若要启用连接日志记录,请参阅为 Kestrel 配置终结点

指标

指标是一段时间间隔内数据度量值的表示形式,例如每秒请求数。 使用指标数据可以在高级别观察应用的状态。 Kestrel 指标是使用 EventCounter 发出的。

注意

connections-per-secondtls-handshakes-per-second 计数器命名不正确。 计数器:

  • 并不总是包含新建连接数或每秒 TLS 握手数
  • 显示上次更新间隔内新建连接数或 TLS 握手数,正如事件使用者通过 filterPayloadKestrelEventSource 中的 EventCounterIntervalSec 参数请求的那样。

建议这些计数器的使用者按照 DisplayRateTimeScale 为一秒的情况来缩放指标值。

名称 显示名称 说明
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 连接队列长度 排队到线程池的总连接数。 在处于稳定状态的正常系统中,此数字应始终接近零
request-queue-length 请求队列长度 排队到线程池的请求总数。 在处于稳定状态的正常系统中,此数字应始终接近零。 此指标与 IIS/Http.Sys 请求队列不同,不能进行比较
current-upgraded-requests 当前升级请求 (Websocket) 活动的 WebSocket 请求数

DiagnosticSource

Kestrel 为服务器层上被拒绝的 HTTP 请求发出 DiagnosticSource 事件,例如格式错误的请求和协议冲突。 因此,这些请求决不会进入 ASP.NET Core 的宿主层。

Kestrel 使用 Microsoft.AspNetCore.Server.Kestrel.BadRequest 事件名称和 IFeatureCollection 作为对象有效负载发出这些事件。 可以通过访问功能集合上的 IBadRequestExceptionFeature 来检索基础异常。

解决这些事件的过程分为两个步骤。 必须创建 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 进程时,不会强制实施某些超时和速率限制。 有关详细信息,请参阅附加了调试器的行为