Kestrel에서의 진단

작성자: Sourabh Shirhatti

이 문서에서는 문제 해결을 위해 Kestrel에서 진단을 수집하는 방법에 대한 참고 자료를 제공합니다. 다음 내용을 다룹니다.

  • 로깅: .NET Core 로깅에 기록되는 구조적 로그입니다. ILogger는 앱 프레임워크에서 로그를 기록하고 사용자가 앱에서 자체 로깅을 유지하는 데 사용됩니다.
  • 메트릭: 시간 간격에 따른 데이터 측정값 표시입니다(예: 초당 요청 수). 메트릭은 EventCounter를 사용하여 내보내고 dotnet-counters 명령줄 도구 또는 Application Insights를 사용하여 확인할 수 있습니다.
  • DiagnosticSource: DiagnosticSource는 프로세스에서 사용할 수 있는 풍부한 데이터 페이로드가 있는 프로덕션 시간 로깅 메커니즘입니다. 데이터가 프로세스를 벗어난다고 가정하고 직렬화 가능한 데이터를 예상하는 로깅과 달리, DiagnosticSource는 복잡한 데이터에서도 잘 작동합니다.

로깅

대부분의 ASP.NET Core 구성 요소처럼 Kestrel은 Microsoft.Extensions.Logging을 사용하여 로그 정보를 내보냅니다. Kestrel은 수신 대기할 로그를 선택할 수 있도록 하는 여러 범주를 사용합니다.

범주 이름 로깅 이벤트 로깅
Microsoft.AspNetCore.Server.Kestrel ApplicationError, ConnectionHeadResponseBodyWrite, ApplicationNeverCompleted, RequestBodyStart, RequestBodyDone, RequestBodyNotEntirelyRead, RequestBodyDrainTimedOut, ResponseMinimumDataRateNotSatisfied, InvalidResponseHeaderRemoved, HeartbeatSlow
Microsoft.AspNetCore.Server.Kestrel.BadRequests ConnectionBadRequest, RequestProcessingError, RequestBodyMinimumDataRateNotSatisfied
Microsoft.AspNetCore.Server.Kestrel.Connections ConnectionAccepted, ConnectionStart, ConnectionStop, ConnectionPause, ConnectionResume, ConnectionKeepAlive, ConnectionRejected, ConnectionDisconnect, NotAllConnectionsClosedGracefully, NotAllConnectionsAborted, ApplicationAbortedConnection
Microsoft.AspNetCore.Server.Kestrel.Http2 Http2ConnectionError, Http2ConnectionClosing, Http2ConnectionClosed, Http2StreamError, Http2StreamResetAbort, HPackDecodingError, HPackEncodingError, Http2FrameReceived, Http2FrameSending, Http2MaxConcurrentStreamsReached
Microsoft.AspNetCore.Server.Kestrel.Http3 Http3ConnectionError, Http3ConnectionClosing, Http3ConnectionClosed, Http3StreamAbort, Http3FrameReceived, Http3FrameSending

연결 로깅

Kestrel은 바이트 수준 통신을 위해 Debug 수준 로그를 내보낼 수 있는 기능을 지원하며 엔드포인트별로 활성화할 수 있습니다. 연결 로깅을 활성화하는 방법은 Kestrel용 엔드포인트 구성을 참조하세요.

메트릭

메트릭은 시간 간격에 따른 데이터 측정값 표시입니다(예: 초당 요청 수). 메트릭 데이터를 통해 앱 상태를 상위 수준에서 확인할 수 있습니다. Kestrel 메트릭은 EventCounter를 사용하여 내보냅니다.

참고 항목

connections-per-secondtls-handshakes-per-second 카운터의 이름이 올바르지 않습니다. 카운터는 다음 조건을 충족해야 합니다.

  • 초당 새 연결이나 TLS 핸드셰이크 수를 항상 포함하지는 않습니다.
  • 이벤트 소비자가 요청하는 경우 최신 업데이트 간격의 새 연결이나 TLS 핸드셰이크 수를 filterPayloadEventCounterIntervalSec 인수를 통해 KestrelEventSource에 표시합니다.

이러한 카운터의 소비자는 1초 동안의 DisplayRateTimeScale을 기준으로 메트릭 값의 크기를 조정하는 것이 좋습니다.

이름 표시 이름 설명
connections-per-second 연결 속도 업데이트 간격당 새 수신 연결 수
total-connections 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 현재 업그레이드된 요청(WebSockets) 활성 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 연결된 경우 적용되지 않습니다. 자세한 내용은 디버거가 연결된 동작을 참조하세요.