From 07ed4cd6489ad8e4369709a222238f9f5f1a353a Mon Sep 17 00:00:00 2001 From: Sourabh Shirhatti Date: Wed, 15 Sep 2021 12:55:29 -0700 Subject: [PATCH 1/7] WIP: Kestrel diagnostics --- .../servers/kestrel/diagnostics.md | 132 ++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 aspnetcore/fundamentals/servers/kestrel/diagnostics.md diff --git a/aspnetcore/fundamentals/servers/kestrel/diagnostics.md b/aspnetcore/fundamentals/servers/kestrel/diagnostics.md new file mode 100644 index 000000000000..ff9e2de42fea --- /dev/null +++ b/aspnetcore/fundamentals/servers/kestrel/diagnostics.md @@ -0,0 +1,132 @@ +--- +title: Logging and diagnostics in Kestrel +author: shirhatti +description: Learn how to gather diagnostics from Kestrel. +monikerRange: '>= aspnetcore-6.0' +ms.author: soshir +ms.date: 07/01/2021 +uid: kestrel/diagnostics +--- + +# Diagnostics in Kestrel + +By [Sourabh Shirhatti](https://twitter.com/sshirhatti) + +This article provides guidance for gathering diagnostics from Kestrel to help troubleshoot issues. Topics covered include: + +* **Logging** - Structured logs written to [.NET Core logging](xref:fundamentals/logging/index). is used by app frameworks to write logs, and by users for their own logging in an app. +* **Metrics** - Representation of data measures over intervals of time, for example, requests per second. Metrics are emitted using `EventCounter` and can be observed using [dotnet-counters](/dotnet/core/diagnostics/dotnet-counters) command line tool or with [Application Insights](/azure/azure-monitor/app/eventcounters). +* **DiagnosticSource** - DiagnosticsSource is a mechanism for production-time logging for rich data payloads for consumption within the process. Unlike logging, which assumes data will leave the process and expects serializable data, DiagnosticSource works well with complex data. + +## Logging + +Like most components in ASP.NET Core, Kestrel uses `Microsoft.Extensions.Logging` to emit log information. Kestrel employs the use of multiple [categories](xref:fundamentals/logging#log-category-1) which allows you to be selective on which logs you listen to. + +| Logging Category Name | Logging Events | +|--|--| +| `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 | + +### Connection logging + +// TODO: WIP + +```csharp +using System.Diagnostics; +using System.Net; +var builder = WebApplication.CreateBuilder(args); +builder.WebHost.ConfigureLogging(logging => + logging.AddFilter((category, level) => + category.Equals("Microsoft.AspNetCore.Server.Kestrel.Core.Internal.LoggingConnectionMiddleware") && level >= LogLevel.Trace)); +builder.WebHost.ConfigureKestrel(o => +{ + o.Listen(IPAddress.Loopback, 5000, listenOptions => listenOptions.UseConnectionLogging()); + o.Listen(IPAddress.Loopback, 5001, listenOptions => + { + listenOptions.UseHttps(); + // For logging decrypted traffic + listenOptions.UseConnectionLogging(); + }); +}); +var app = builder.Build(); +app.MapGet("/", () => "Hello world"); +app.Run(); +``` + +## Metrics + +Metrics is a representation of data measures over intervals of time, for example, requests per second. Metrics data allows observation of the state of an app at a high-level. Kestrel metrics are emitted using `EventCounter`. + +> Unfortunately, the `connections-per-second` and `tls-handshakes-per-second` counters are named incorrectly. Unlike, as implied by them the name, they do not always contain the number of new connections or TLS handshakes per second, but rather the number of new connection or TLS handshakes in the last update interval as requested as the consumer of Events via the `EventCounterIntervalSec` argument in the `filterPayload` to `KestrelEventSource`. It is **recommended** that consumers of these counters scale the metric value based on the `DisplayRateTimeScale` of one second. + +| Name | Display Name | Description | +|--|--|--| +| `connections-per-second` | Connection Rate| The number of new incoming connections per update interval | +| `total-connections` | Total Connection | The total number of connections | +| `tls-handshakes-per-second` | TLS Handshake Rate | The number of new TLS handshakes per update interval | +| `total-tls-handshakes` | Total TLS Handshake | The total number of TLS handshakes | +| `current-tls-handshakes` | Current TLS Handshakes | The number of TLS handshakes in process | +| `failed-tls-handshakes` | Failed TLS Handshakes| The total number of failed TLS handshakes | +| `current-connections` | Current Connections | The total number of connections (including idle connections) +| `connection-queue-length` | Connection Queue Length | The total number connections queued to the thread pool. In a healthy system at steady state, this number should always be close to zero | +| `request-queue-length` | Request Queue Length | The total number requests queued to the thread pool. In a healthy system at steady state, this number should always be close to zero. This metric is unlike the IIS/Http.Sys request queue and cannot be compared | +| `current-upgraded-requests` | Current Upgraded Requests (WebSockets) | The number of active WebSocket requests | + +## DiagnosticSource + +Kestrel emits a `DiagnosticSource` event for HTTP requests rejected at server layer such as malformed requests and protocols violations. As such, these requests never make it into the hosting layer of ASP.NET Core. + +Kestrel emits these events with the `Microsoft.AspNetCore.Server.Kestrel.BadRequest` event name and an `IFeatureCollection` as the object payload. The underlying exception can be retrieved by accessing the `IBadRequestExceptionFeature` on the feature collection. + +Resolving these events is a two-step process. First, an observer for DiagnosticListener must be created: + +```csharp +class BadRequestEventListener : IObserver>, IDisposable +{ + private readonly IDisposable _subscription; + private readonly Action _callback; + + public BadRequestEventListener(DiagnosticListener diagnosticListener, Action callback) + { + _subscription = diagnosticListener.Subscribe(this!, IsEnabled); + _callback = callback; + } + private static readonly Predicate IsEnabled = (provider) => provider switch + { + "Microsoft.AspNetCore.Server.Kestrel.BadRequest" => true, + _ => false + }; + public void OnNext(KeyValuePair pair) + { + if (pair.Value is IFeatureCollection featureCollection) + { + var badRequestFeature = featureCollection.Get(); + + if (badRequestFeature is not null) + { + _callback(badRequestFeature); + } + } + } + public void OnError(Exception error) { } + public void OnCompleted() { } + public virtual void Dispose() => _subscription.Dispose(); +} +``` + +Second, you need subscribe to the ASP.NET Core DiagnosticListener with your observer. In our example, we will be creating a callback that logs the underlying exception. + +```csharp +var builder = WebApplication.CreateBuilder(args); +var app = builder.Build(); +var diagnosticSource = app.Services.GetRequiredService(); +using var badRequestListener = new BadRequestEventListener(diagnosticSource, (badRequestExceptionFeature) => +{ + app.Logger.LogError(badRequestExceptionFeature.Error, "Bad request received"); +}); +app.MapGet("/", () => "Hello world"); +app.Run(); +``` From c24c093cacc4bb7e143106ffb03b5bd38a550ef8 Mon Sep 17 00:00:00 2001 From: Sourabh Shirhatti Date: Wed, 15 Sep 2021 13:25:34 -0700 Subject: [PATCH 2/7] Update aspnetcore/fundamentals/servers/kestrel/diagnostics.md Co-authored-by: Rick Anderson <3605364+Rick-Anderson@users.noreply.github.com> --- aspnetcore/fundamentals/servers/kestrel/diagnostics.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aspnetcore/fundamentals/servers/kestrel/diagnostics.md b/aspnetcore/fundamentals/servers/kestrel/diagnostics.md index ff9e2de42fea..68d1448be96d 100644 --- a/aspnetcore/fundamentals/servers/kestrel/diagnostics.md +++ b/aspnetcore/fundamentals/servers/kestrel/diagnostics.md @@ -20,7 +20,7 @@ This article provides guidance for gathering diagnostics from Kestrel to help tr ## Logging -Like most components in ASP.NET Core, Kestrel uses `Microsoft.Extensions.Logging` to emit log information. Kestrel employs the use of multiple [categories](xref:fundamentals/logging#log-category-1) which allows you to be selective on which logs you listen to. +Like most components in ASP.NET Core, Kestrel uses `Microsoft.Extensions.Logging` to emit log information. Kestrel employs the use of multiple [categories](xref:fundamentals/logging/index#log-category-1) which allows you to be selective on which logs you listen to. | Logging Category Name | Logging Events | |--|--| From 8367d719ba138ae9b3a4b74280e4d4ee7dd1de10 Mon Sep 17 00:00:00 2001 From: Sourabh Shirhatti Date: Thu, 16 Sep 2021 11:31:36 -0700 Subject: [PATCH 3/7] Apply suggestions from code review Co-authored-by: Rick Anderson <3605364+Rick-Anderson@users.noreply.github.com> Co-authored-by: Aditya Mandaleeka --- .../servers/kestrel/diagnostics.md | 33 +++++++++++-------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/aspnetcore/fundamentals/servers/kestrel/diagnostics.md b/aspnetcore/fundamentals/servers/kestrel/diagnostics.md index 68d1448be96d..db15586481b5 100644 --- a/aspnetcore/fundamentals/servers/kestrel/diagnostics.md +++ b/aspnetcore/fundamentals/servers/kestrel/diagnostics.md @@ -14,9 +14,9 @@ By [Sourabh Shirhatti](https://twitter.com/sshirhatti) This article provides guidance for gathering diagnostics from Kestrel to help troubleshoot issues. Topics covered include: -* **Logging** - Structured logs written to [.NET Core logging](xref:fundamentals/logging/index). is used by app frameworks to write logs, and by users for their own logging in an app. -* **Metrics** - Representation of data measures over intervals of time, for example, requests per second. Metrics are emitted using `EventCounter` and can be observed using [dotnet-counters](/dotnet/core/diagnostics/dotnet-counters) command line tool or with [Application Insights](/azure/azure-monitor/app/eventcounters). -* **DiagnosticSource** - DiagnosticsSource is a mechanism for production-time logging for rich data payloads for consumption within the process. Unlike logging, which assumes data will leave the process and expects serializable data, DiagnosticSource works well with complex data. +* **Logging**: Structured logs written to [.NET Core logging](xref:fundamentals/logging/index). is used by app frameworks to write logs, and by users for their own logging in an app. +* **Metrics**: Representation of data measures over intervals of time, for example, requests per second. Metrics are emitted using `EventCounter` and can be observed using the [dotnet-counters](/dotnet/core/diagnostics/dotnet-counters) command line tool or with [Application Insights](/azure/azure-monitor/app/eventcounters). +* **DiagnosticSource**: `DiagnosticsSource` is a mechanism for production-time logging with rich data payloads for consumption within the process. Unlike logging, which assumes data will leave the process and expects serializable data, `DiagnosticSource` works well with complex data. ## Logging @@ -24,11 +24,11 @@ Like most components in ASP.NET Core, Kestrel uses `Microsoft.Extensions.Logging | Logging Category Name | Logging Events | |--|--| -| `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 | +| `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` | ### Connection logging @@ -60,17 +60,22 @@ app.Run(); Metrics is a representation of data measures over intervals of time, for example, requests per second. Metrics data allows observation of the state of an app at a high-level. Kestrel metrics are emitted using `EventCounter`. -> Unfortunately, the `connections-per-second` and `tls-handshakes-per-second` counters are named incorrectly. Unlike, as implied by them the name, they do not always contain the number of new connections or TLS handshakes per second, but rather the number of new connection or TLS handshakes in the last update interval as requested as the consumer of Events via the `EventCounterIntervalSec` argument in the `filterPayload` to `KestrelEventSource`. It is **recommended** that consumers of these counters scale the metric value based on the `DisplayRateTimeScale` of one second. +> [!NOTE] +> The `connections-per-second` and `tls-handshakes-per-second` counters are named incorrectly. The counters: +> * Do ***not*** always contain the number of new connections or TLS handshakes per second +> * Display the number of new connection or TLS handshakes in the last update interval as requested as the consumer of Events via the `EventCounterIntervalSec` argument in the `filterPayload` to `KestrelEventSource`. +> +> We **recommend** consumers of these counters scale the metric value based on the `DisplayRateTimeScale` of one second. | Name | Display Name | Description | |--|--|--| | `connections-per-second` | Connection Rate| The number of new incoming connections per update interval | -| `total-connections` | Total Connection | The total number of connections | +| `total-connections` | Total Connections | The total number of connections | | `tls-handshakes-per-second` | TLS Handshake Rate | The number of new TLS handshakes per update interval | -| `total-tls-handshakes` | Total TLS Handshake | The total number of TLS handshakes | +| `total-tls-handshakes` | Total TLS Handshakes | The total number of TLS handshakes | | `current-tls-handshakes` | Current TLS Handshakes | The number of TLS handshakes in process | | `failed-tls-handshakes` | Failed TLS Handshakes| The total number of failed TLS handshakes | -| `current-connections` | Current Connections | The total number of connections (including idle connections) +| `current-connections` | Current Connections | The total number of connections, including idle connections | `connection-queue-length` | Connection Queue Length | The total number connections queued to the thread pool. In a healthy system at steady state, this number should always be close to zero | | `request-queue-length` | Request Queue Length | The total number requests queued to the thread pool. In a healthy system at steady state, this number should always be close to zero. This metric is unlike the IIS/Http.Sys request queue and cannot be compared | | `current-upgraded-requests` | Current Upgraded Requests (WebSockets) | The number of active WebSocket requests | @@ -81,7 +86,7 @@ Kestrel emits a `DiagnosticSource` event for HTTP requests rejected at server la Kestrel emits these events with the `Microsoft.AspNetCore.Server.Kestrel.BadRequest` event name and an `IFeatureCollection` as the object payload. The underlying exception can be retrieved by accessing the `IBadRequestExceptionFeature` on the feature collection. -Resolving these events is a two-step process. First, an observer for DiagnosticListener must be created: +Resolving these events is a two-step process. An observer for `DiagnosticListener` must be created: ```csharp class BadRequestEventListener : IObserver>, IDisposable @@ -117,7 +122,7 @@ class BadRequestEventListener : IObserver>, IDispos } ``` -Second, you need subscribe to the ASP.NET Core DiagnosticListener with your observer. In our example, we will be creating a callback that logs the underlying exception. +Subscribe to the ASP.NET Core `DiagnosticListener` with the observer. In this example, we create a callback that logs the underlying exception. ```csharp var builder = WebApplication.CreateBuilder(args); From 90a24cdc5d83d92efd8688bc3557bbdf66e83441 Mon Sep 17 00:00:00 2001 From: Sourabh Shirhatti Date: Thu, 16 Sep 2021 11:34:15 -0700 Subject: [PATCH 4/7] Update diagnostics.md --- aspnetcore/fundamentals/servers/kestrel/diagnostics.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aspnetcore/fundamentals/servers/kestrel/diagnostics.md b/aspnetcore/fundamentals/servers/kestrel/diagnostics.md index db15586481b5..d7f97fe1c423 100644 --- a/aspnetcore/fundamentals/servers/kestrel/diagnostics.md +++ b/aspnetcore/fundamentals/servers/kestrel/diagnostics.md @@ -16,7 +16,7 @@ This article provides guidance for gathering diagnostics from Kestrel to help tr * **Logging**: Structured logs written to [.NET Core logging](xref:fundamentals/logging/index). is used by app frameworks to write logs, and by users for their own logging in an app. * **Metrics**: Representation of data measures over intervals of time, for example, requests per second. Metrics are emitted using `EventCounter` and can be observed using the [dotnet-counters](/dotnet/core/diagnostics/dotnet-counters) command line tool or with [Application Insights](/azure/azure-monitor/app/eventcounters). -* **DiagnosticSource**: `DiagnosticsSource` is a mechanism for production-time logging with rich data payloads for consumption within the process. Unlike logging, which assumes data will leave the process and expects serializable data, `DiagnosticSource` works well with complex data. +* **DiagnosticSource**: `DiagnosticSource` is a mechanism for production-time logging with rich data payloads for consumption within the process. Unlike logging, which assumes data will leave the process and expects serializable data, `DiagnosticSource` works well with complex data. ## Logging From 3f951a6db92bc26cb5744313135fad7df70b3c62 Mon Sep 17 00:00:00 2001 From: Sourabh Shirhatti Date: Fri, 17 Sep 2021 16:06:14 -0700 Subject: [PATCH 5/7] x-link to endpoint doc --- .../servers/kestrel/diagnostics.md | 28 ++----------------- 1 file changed, 3 insertions(+), 25 deletions(-) diff --git a/aspnetcore/fundamentals/servers/kestrel/diagnostics.md b/aspnetcore/fundamentals/servers/kestrel/diagnostics.md index d7f97fe1c423..7d6e3883e2ff 100644 --- a/aspnetcore/fundamentals/servers/kestrel/diagnostics.md +++ b/aspnetcore/fundamentals/servers/kestrel/diagnostics.md @@ -32,29 +32,7 @@ Like most components in ASP.NET Core, Kestrel uses `Microsoft.Extensions.Logging ### Connection logging -// TODO: WIP - -```csharp -using System.Diagnostics; -using System.Net; -var builder = WebApplication.CreateBuilder(args); -builder.WebHost.ConfigureLogging(logging => - logging.AddFilter((category, level) => - category.Equals("Microsoft.AspNetCore.Server.Kestrel.Core.Internal.LoggingConnectionMiddleware") && level >= LogLevel.Trace)); -builder.WebHost.ConfigureKestrel(o => -{ - o.Listen(IPAddress.Loopback, 5000, listenOptions => listenOptions.UseConnectionLogging()); - o.Listen(IPAddress.Loopback, 5001, listenOptions => - { - listenOptions.UseHttps(); - // For logging decrypted traffic - listenOptions.UseConnectionLogging(); - }); -}); -var app = builder.Build(); -app.MapGet("/", () => "Hello world"); -app.Run(); -``` +Kestrel also supports the ability to emit `Debug` level logs for byte-level communication and can be enabled on a per-endpoint basis. To enable connection logging, see [configure endpoints for Kestrel](xref:fundamentals/servers/kestrel/endpoints) ## Metrics @@ -62,8 +40,8 @@ Metrics is a representation of data measures over intervals of time, for example > [!NOTE] > The `connections-per-second` and `tls-handshakes-per-second` counters are named incorrectly. The counters: -> * Do ***not*** always contain the number of new connections or TLS handshakes per second -> * Display the number of new connection or TLS handshakes in the last update interval as requested as the consumer of Events via the `EventCounterIntervalSec` argument in the `filterPayload` to `KestrelEventSource`. +> * Do ***not*** always contain the number of new connections or TLS handshakes per second +> * Display the number of new connection or TLS handshakes in the last update interval as requested as the consumer of Events via the `EventCounterIntervalSec` argument in the `filterPayload` to `KestrelEventSource`. > > We **recommend** consumers of these counters scale the metric value based on the `DisplayRateTimeScale` of one second. From 0a10837967c2629f45d151da2806c637e20e195d Mon Sep 17 00:00:00 2001 From: Sourabh Shirhatti Date: Fri, 17 Sep 2021 16:09:47 -0700 Subject: [PATCH 6/7] Update toc --- aspnetcore/toc.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/aspnetcore/toc.yml b/aspnetcore/toc.yml index e64f35f9e661..2ca24d17b836 100644 --- a/aspnetcore/toc.yml +++ b/aspnetcore/toc.yml @@ -1007,6 +1007,9 @@ - name: Options displayName: deploy, publish, server uid: fundamentals/servers/kestrel/options + - name: Diagnostics + displayName: diagnostics + uid: fundamentals/servers/kestrel/diagnostics - name: HTTP/2 displayName: deploy, publish, server uid: fundamentals/servers/kestrel/http2 From 90588fa1fb545f49b23dff74d9bf6f6cdcd6559e Mon Sep 17 00:00:00 2001 From: Sourabh Shirhatti Date: Fri, 17 Sep 2021 16:22:14 -0700 Subject: [PATCH 7/7] Fix doc uid --- aspnetcore/fundamentals/servers/kestrel/diagnostics.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aspnetcore/fundamentals/servers/kestrel/diagnostics.md b/aspnetcore/fundamentals/servers/kestrel/diagnostics.md index 7d6e3883e2ff..4ce29dc08068 100644 --- a/aspnetcore/fundamentals/servers/kestrel/diagnostics.md +++ b/aspnetcore/fundamentals/servers/kestrel/diagnostics.md @@ -5,7 +5,7 @@ description: Learn how to gather diagnostics from Kestrel. monikerRange: '>= aspnetcore-6.0' ms.author: soshir ms.date: 07/01/2021 -uid: kestrel/diagnostics +uid: fundamentals/servers/kestrel/diagnostics --- # Diagnostics in Kestrel