Latest update: switch to callback-based API to observe HttpResponseMessage for enrichment. Starting a new round of API discussion.
Background and motivation
As part of #84978, we need to enable enriching metrics logs with additional custom tags injected by the application/middleware. These tags need to be injected on per-request basis meaning that they should be provided in HttpRequestMessage. Enrichment is an advanced scenario, and metrics will be only implemented in HttpClientHandler/SocketsHttpHandler, therefore the value of exposing a property on HttpRequestMessage would be questionable. Instead, we can pass the custom tags using HttpRequestMessage.Options. The problem is that there are no good established practices to document and it's hard to discover discover it. Defining a key alone would also leave the door open to mistakes. As a compromise, I'm proposing an API that works via an extension method over HttpRequestOptions using an internal key.
The API should provide an ability inject tags based on HttpResponseMessage. Since the metrics implementation will live within HttpClientHandler/SocketsHttpHandler, it would be too late to do this in a DelegatingHandler. To handle this, the proposed API is passing user-provided callbacks to the metrics implementation which can the custom tags to be added for enrichment.
API Proposal
namespaceSystem.Net.Http.Metrics;publicsealedclassHttpMetricsEnrichmentContext{publicHttpRequestMessageRequest{get;}publicHttpResponseMessage?Response{get;}// null when an exception is thrown by the innermost handlerpublicException?Exception{get;}// null when no exception is thrownpublicvoidAddCustomTag(stringname,object?value);// Alternatively we can expose a collection to allow enumerating, removing in the future, but such functionality is not needed today.// public ICollection<KeyValuePair<string, object?>> CustomTags { get; }}// Or should it be MetricsHttpRequestOptionsExtensions? // runtime/libraries is not consistent on this, see HttpClientJsonExtensions vs OptionsServiceCollectionExtensionspublicstaticclassHttpRequestOptionsMetricsExtensions{publicstaticvoidAddMetricsEnrichmentCallback(thisHttpRequestOptionsoptions,Action<HttpMetricsEnrichmentContext>callback));}API Usage
publicclassEnricherHandler:DelegatingHandler{protectedoverrideasyncTask<HttpResponseMessage>SendAsync(HttpRequestMessagerequest,CancellationTokencancellationToken){request.Options.AddMetricsEnrichmentCallback(static ctx =>{ctx.AddCustomTag("org-name",ctx.Request.Headers.GetValue("x-org-name"));ctx.AddCustomTag("result-category",GetResultCategory(ctx.Response.StatusCode));});returnawaitbase.SendAsync(request,cancellationToken);}privatestaticstringGetResultCategory(HttpStatusCodestatusCode)=>statusCode>=200&&statusCode<300?"Good":"Bad";}Alternative Designs
Attempt to reduce heap allocations by working with value types
Note that the prerequisite for such an attempt to be successful is to resolve #83701, otherwise TagList would allocate in almost all R9 scenarios.
namespaceSystem.Net.Http.Metrics;publicreadonlystructHttpMetricsEnrichmentContext{publicreadonlyHttpRequestMessageRequest{get;}publicreadonlyHttpResponseMessage?Response{get;}publicreadonlyException?Exception{get;}}publicdelegatevoidHttpMetricsEnrichmentCallback(inHttpMetricsEnrichmentContextcontext,refTagListcustomTags);publicstaticclassHttpRequestOptionsMetricsExtensions{publicstaticvoidAddMetricsEnrichmentCallback(thisHttpRequestOptionsoptions,HttpMetricsEnrichmentCallbackcallback));}
Latest update: switch to callback-based API to observe
HttpResponseMessagefor enrichment. Starting a new round of API discussion.Background and motivation
As part of #84978, we need to enable enriching metrics logs with additional custom tags injected by the application/middleware. These tags need to be injected on per-request basis meaning that they should be provided in
HttpRequestMessage. Enrichment is an advanced scenario, and metrics will be only implemented inHttpClientHandler/SocketsHttpHandler, therefore the value of exposing a property onHttpRequestMessagewould be questionable. Instead, we can pass the custom tags usingHttpRequestMessage.Options. The problem is that there are no good established practices to document and it's hard to discover discover it. Defining a key alone would also leave the door open to mistakes. As a compromise, I'm proposing an API that works via an extension method overHttpRequestOptionsusing an internal key.The API should provide an ability inject tags based on
HttpResponseMessage. Since the metrics implementation will live withinHttpClientHandler/SocketsHttpHandler, it would be too late to do this in aDelegatingHandler. To handle this, the proposed API is passing user-provided callbacks to the metrics implementation which can the custom tags to be added for enrichment.API Proposal
API Usage
Alternative Designs
Attempt to reduce heap allocations by working with value types
Note that the prerequisite for such an attempt to be successful is to resolve #83701, otherwise
TagListwould allocate in almost all R9 scenarios.