diff --git a/docs/core/extensions/httpclient-latency-extensions.md b/docs/core/extensions/httpclient-latency-extensions.md new file mode 100644 index 0000000000000..58fd84fa1d958 --- /dev/null +++ b/docs/core/extensions/httpclient-latency-extensions.md @@ -0,0 +1,252 @@ +--- +title: Monitor and analyze HTTP client performance +description: Learn how to use the HttpClientLatency with dependency injection in your .NET workloads. +author: IEvangelist +ms.author: dapine +ms.date: 09/29/2025 +ai-usage: ai-assisted +--- + +# HTTP client latency telemetry in .NET + +### Get started + +When you build applications that communicate over HTTP, it's important to observe request performance characteristics. +The +extension enables collection of detailed timing information for outgoing HTTP calls with no changes to calling code. +It plugs into the existing `HttpClientFactory` pipeline to capture stage timings across the request lifecycle, record +HTTP protocol details, measure garbage collection impact where the runtime exposes that data, and emit a uniform +telemetry shape suitable for performance analysis and tuning. + +### [.NET CLI](#tab/dotnet-cli) + +```dotnetcli +dotnet add package Microsoft.Extensions.Http.Diagnostics --version 10.0.0 +``` + +### [PackageReference](#tab/package-reference) + +```xml + +``` + +--- + +For more information, see [dotnet package add](../tools/dotnet-package-add.md) or [Manage package dependencies in .NET applications](../tools/dependencies.md). + +### Register HTTP client latency telemetry + +To add HTTP client latency telemetry to your application, call the extension method when configuring your services: + +```csharp +using Microsoft.Extensions.DependencyInjection; + +var builder = WebApplication.CreateBuilder(args); + +// Add HTTP client factory +builder.Services.AddHttpClient(); + +// Add HTTP client latency telemetry +builder.Services.AddHttpClientLatencyTelemetry(); +``` + +This registration adds a `DelegatingHandler` to all HTTP clients created through , collecting detailed latency information for each request. + +### Configure telemetry options + +You configure telemetry collection through the `HttpClientLatencyTelemetryOptions` (standard options pattern). +You can supply values either with a delegate or by binding configuration (for example, appsettings.json). +The options instance is resolved once per handler pipeline so changes apply to new clients/handlers. + +```csharp +// Configure with delegate +builder.Services.AddHttpClientLatencyTelemetry(options => +{ + options.EnableDetailedLatencyBreakdown = true; +}); + +// Or configure from configuration +builder.Services.AddHttpClientLatencyTelemetry( +builder.Configuration.GetSection("HttpClientTelemetry")); +``` + +### Configuration options + +The class offers the following settings: + +| Option | Type | Default | Description | When to disable | +|--------------------------------|---------|---------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------| +| EnableDetailedLatencyBreakdown | Boolean | `true` | Enables fine-grained phase timing for each HttpClient request (for example, connection establishment, headers sent, first byte, completion) to produce a breakdown of total latency. Adds a small extra CPU/time measurement cost, no wire overhead. | Set to `false` only in very high-throughput scenarios where minimal overhead is required and total duration alone is sufficient. | + +### Collected telemetry data + +When HTTP client latency telemetry is enabled, it captures phase timestamps, selected measures (where supported), and protocol attributes used for performance analysis. + +#### Timing checkpoints + +Timestamps are recorded for key stages of the HTTP request lifecycle: + +| Phase | Start Event | End Event | Notes | +|-------------------------|----------------------------|----------------------------|---------------------------------------------------------| +| DNS resolution | Http.NameResolutionStart | Http.NameResolutionEnd | Host name lookup (may be cached and skipped). | +| Socket connection | Http.SocketConnectStart | Http.SocketConnectEnd | CP (and TLS handshake if combined by handler). | +| Connection establishment| | Http.ConnectionEstablished | Marks usable connection after handshake. | +| Request headers | Http.RequestHeadersStart | Http.RequestHeadersEnd | Writing request headers to the wire/buffer. | +| Request content | Http.RequestContentStart | Http.RequestContentEnd | Streaming or buffering request body. | +| Response headers | Http.ResponseHeadersStart | Http.ResponseHeadersEnd | First byte to completion of header parsing. | +| Response content | Http.ResponseContentStart | Http.ResponseContentEnd | Reading full response body (to completion or disposal). | + +#### Measures (platform dependent) + +| Name | Description | +|--------------------------|-------------------------------------------------------------------------| +| Http.GCPauseTime | Total GC pause duration overlapping the request. | +| Http.ConnectionInitiated | Emitted when a new underlying connection (not pooled reuse) is created. | + +#### Tags + +| Tag | Description | +|--------------|-----------------------------------------------------------------| +| Http.Version | HTTP protocol version negotiated/used (for example, 1.1, 2, 3). | + +## Usage example + +### HTTP client logs enrichment and redaction + +These components enable enriching and redacting `HttpClient` request logs. They remove built-in HTTP client logging. + +When using this package, some of the log properties are redacted by default (like full routes), which means that you will need to make sure that a redactor provider is registered in the Dependency Injection container. You can do this by making sure that you call `builder.Services.AddRedaction()`, which requires a reference to the `Microsoft.Extensions.Compliance.Redaction` package. + +```csharp +public static IServiceCollection AddExtendedHttpClientLogging(this IServiceCollection services) +public static IServiceCollection AddExtendedHttpClientLogging(this IServiceCollection services, IConfigurationSection section) +public static IServiceCollection AddExtendedHttpClientLogging(this IServiceCollection services, Action configure) +public static IServiceCollection AddHttpClientLogEnricher(this IServiceCollection services) where T : class, IHttpClientLogEnricher +``` + +For example: + +```csharp +var builder = Host.CreateApplicationBuilder(args); + +// Register IHttpClientFactory: +builder.Services.AddHttpClient(); + +// Register redaction services: +builder.Services.AddRedaction(); + +// Register HttpClient logging enrichment & redaction services: +builder.Services.AddExtendedHttpClientLogging(); + +// Register a logging enricher (the type should implement IHttpClientLogEnricher): +builder.Services.AddHttpClientLogEnricher(); + +var host = builder.Build(); +``` + + adds information to the logs using *enrichment*. This means that the information is added as tags to the structured logs but **isn't visible** in the log message that's printed by default in the console. To view the information, you need to use a logging provider that supports structured logs. One quick and built-in way to do this is to call `AddJsonConsole()` to your logging builder, which will print out the full structured logs to the console. Here's an example that uses the `ExtendedHttpClientLogging()` method to automatically log all `HttpClient` request and response bodies, and then prints the full structured logs to the console: + +```csharp +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +var services = new ServiceCollection(); + +services.AddLogging(o => o.SetMinimumLevel(LogLevel.Trace).AddJsonConsole()); // <-- Enable structured logging to the console + +// Adding default redactor provider to the DI container. This is required when using the AddExtendedHttpClientLogging() method. +services.AddRedaction(); + +services.AddHttpClient("foo") + .AddExtendedHttpClientLogging(o => + { + // Enable logging of request and response bodies: + o.LogBody = true; + + // We also need to specify the content types that we want to log: + o.ResponseBodyContentTypes.Add("application/json"); + }); + +var sp = services.BuildServiceProvider(); + +var client = sp.GetRequiredService().CreateClient("foo"); + +var response = await client.GetAsync(new Uri("https://httpbin.org/json")).ConfigureAwait(false); +``` + +By default, request and response routes are redacted for privacy reasons. You can change this behavior by using the `RequestPathParameterRedactionMode` option like so: + +```csharp + .AddExtendedHttpClientLogging(o => +{ + //.. Other options + + o.RequestPathParameterRedactionMode = HttpRouteParameterRedactionMode.None; // <-- Disable redaction of request/response routes +}); +``` + +You can also use the following extension methods to apply the logging to the specific `IHttpClientBuilder`: + +```csharp +public static IHttpClientBuilder AddExtendedHttpClientLogging(this IHttpClientBuilder builder) +public static IHttpClientBuilder AddExtendedHttpClientLogging(this IHttpClientBuilder builder, IConfigurationSection section) +public static IHttpClientBuilder AddExtendedHttpClientLogging(this IHttpClientBuilder builder, Action configure) +``` + +For example: + +```csharp +var builder = Host.CreateApplicationBuilder(args); + +// Register redaction services: +builder.Services.AddRedaction(); + +// Register named HttpClient: +var httpClientBuilder = builder.Services.AddHttpClient("MyNamedClient"); + +// Configure named HttpClient to use logging enrichment & redaction: +httpClientBuilder.AddExtendedHttpClientLogging(); + +var host = builder.Build(); +``` + +### Track HTTP request client latency + +These components enable tracking and reporting the latency of HTTP client request processing. + +You can register the services using the following methods: + +```csharp +public static IServiceCollection AddHttpClientLatencyTelemetry(this IServiceCollection services) +public static IServiceCollection AddHttpClientLatencyTelemetry(this IServiceCollection services, IConfigurationSection section) +public static IServiceCollection AddHttpClientLatencyTelemetry(this IServiceCollection services, Action configure) +``` + +For example: + +```csharp +var builder = Host.CreateApplicationBuilder(args); + +// Register IHttpClientFactory: +builder.Services.AddHttpClient(); + +// Register redaction services: +builder.Services.AddRedaction(); + +// Register latency context services: +builder.Services.AddLatencyContext(); + +// Register HttpClient logging enrichment & redaction services: +builder.Services.AddExtendedHttpClientLogging(); + +// Register HttpClient latency telemetry services: +builder.Services.AddHttpClientLatencyTelemetry(); + +var host = builder.Build(); +``` + +### Platform considerations + +HTTP client latency telemetry runs on all supported targets (.NET 9, .NET 8, .NET Standard 2.0, and .NET Framework 4.6.2). +Core timing checkpoints are always collected. The GC pause metric (Http.GCPauseTime) is emitted only when running on .NET 8 or .NET 9. +The implementation detects platform capabilities at run time and enables what is supported without additional configuration. diff --git a/docs/fundamentals/toc.yml b/docs/fundamentals/toc.yml index 5fd27bcf17177..7b74f1965d772 100644 --- a/docs/fundamentals/toc.yml +++ b/docs/fundamentals/toc.yml @@ -1139,6 +1139,9 @@ items: - name: HTTP client factory href: ../core/extensions/httpclient-factory.md displayName: networking,httpclient,http,dependency injection,client,factory,named client,named httpclient,typed client,typed httpclient + - name: Monitor and analyze HTTP client performance + href: ../core/extensions/httpclient-latency-extensions.md + displayName: httpclient,http,client,factory,dependency injection,keyed,keyed di,keyed services - name: HTTP client factory troubleshooting href: ../core/extensions/httpclient-factory-troubleshooting.md displayName: httpclient,http,client,factory,named client,named httpclient,typed client,typed httpclient