Skip to content

[API Proposal]: AddHttpLatencyTelemetry for incoming HTTP request logs #7729

Description

@EasyL0ver

Background and motivation

Server applications already collect a rich per-request latency breakdown through
the request latency telemetry services (AddRequestLatencyTelemetry,
AddRequestCheckpoint): named checkpoints marking stages of the pipeline,
measures, tags, and a total duration, all captured in the request's
ILatencyContext. Today that breakdown lives only in the latency exporters; it
is not attached to the request's own HTTP log entry.

AddHttpLatencyTelemetry closes that gap. It registers an IHttpLogEnricher
that reads the completed ILatencyContext for each incoming request and attaches
its latency breakdown to that request's HTTP log as a single LatencyInfo tag.
This lets an operator open one request log line and immediately see where the
time went - which checkpoints fired, the measured values, and the total duration -
without correlating across a separate latency signal.

Value:

  • Single-line diagnosis. The latency breakdown travels with the request log,
    so slow requests are explained in place rather than by joining two telemetry
    streams on a request id.
  • Uses data already collected. It consumes the existing ILatencyContext;
    no new measurement cost, only projection of that data onto the log.
  • Completes the incoming/outgoing pair. The outgoing-request counterpart,
    HttpClientLatencyTelemetryExtensions.AddHttpClientLatencyTelemetry, already
    exists for HttpClient calls. This is its incoming-request equivalent, so a
    service can carry consistent latency enrichment on both sides.

The surface is intentionally minimal: one additive extension method on
IServiceCollection, no options type and no new public abstractions. Enrichment
targets and formatting are handled internally by the enricher, so there is no
configuration surface to lock down.

API Proposal

namespace Microsoft.Extensions.DependencyInjection;

/// <summary>
/// Extensions for enriching incoming HTTP request logs with latency telemetry.
/// </summary>
public static class HttpLatencyTelemetryServiceCollectionExtensions
{
    /// <summary>
    /// Adds an enricher that appends latency information from the request's latency
    /// context to incoming HTTP request logs.
    /// </summary>
    /// <param name="services">The <see cref="IServiceCollection"/> to add to.</param>
    /// <returns>The value of <paramref name="services"/>.</returns>
    /// <exception cref="System.ArgumentNullException"><paramref name="services"/> is <see langword="null"/>.</exception>
    public static IServiceCollection AddHttpLatencyTelemetry(this IServiceCollection services);
}

The enricher writes one tag per enriched request:

  • Key: LatencyInfo (matching the outgoing-request enricher's tag key).
  • Value: a compact, ordered projection of the ILatencyContext - data version,
    originating client application name, then the request's tags, checkpoints
    (with elapsed milliseconds), measures, and total duration.

API Usage

var builder = WebApplication.CreateBuilder(args);

// Register the latency context and collect the per-request latency breakdown.
builder.Services.AddLatencyContext();
builder.Services.AddRequestLatencyTelemetry();
builder.Services.AddRequestCheckpoint();

// Attach that breakdown to each incoming request's HTTP log.
builder.Services.AddHttpLatencyTelemetry();

var app = builder.Build();
app.UseRequestCheckpoint();
app.UseHttpLogging();
app.UseRequestLatencyTelemetry();

A slow request's log line then carries a LatencyInfo tag describing where the
time was spent, so it can be diagnosed directly from the request log.

Example enriched log

For a request from client webfrontend carrying one tag, two checkpoints, and
one measure, the enricher attaches a single tag whose value is a compact,
positional string:

LatencyInfo = v1.0,webfrontend,region/,westus/,eltexm/eltltf/,12/47/,dbCalls/,3/,51

Read positionally, the nine comma-separated sections are:

# Section Value
1 data version v1.0
2 client application name webfrontend
3 tag names region/
4 tag values westus/
5 checkpoint names eltexm/eltltf/
6 checkpoint elapsed ms 12/47/
7 measure names dbCalls/
8 measure values 3/
9 total duration ms 51

Within a section, list items are separated by / (each item followed by a
trailing /), and a / occurring inside an item is escaped to _.

Alternative Designs

No response

Risks

The surface is a single additive extension method, so the commitment is small.
The main constraint is the LatencyInfo tag value format: once consumers parse
it, its layout is effectively part of the contract even though it is a string.

Notes for API review

  • Not self-contained. Registers only the enricher; caller wires AddLatencyContext + AddRequestLatencyTelemetry + AddRequestCheckpoint, matching the stable outgoing twin.
  • No-op if ILatencyContext is missing (singleton enricher, resolves per request, never throws on the log path).
  • Known escaping bug: , and the client-name field are unescaped, so a comma in the client name corrupts the positional value. Low severity, shared with the twin, tracked in Outgoing HttpClient LatencyInfo enricher value has incomplete escaping #7757.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

api-approvedAPI was approved in API review, it can be implementedarea-telemetry

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions