Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Customized SocketsHttpHandler does not support diagnostics tracking #31261

Closed
rangp opened this issue Oct 23, 2019 · 20 comments · Fixed by #55392
Closed

Customized SocketsHttpHandler does not support diagnostics tracking #31261

rangp opened this issue Oct 23, 2019 · 20 comments · Fixed by #55392
Assignees
Labels
api-suggestion Early API idea and discussion, it is NOT ready for implementation area-System.Net.Http
Milestone

Comments

@rangp
Copy link

rangp commented Oct 23, 2019

I want to use custom HttpConnectionSettings for my requests since I need to decrease DefaultMaxConnectionsPerServer. As far as I know HttpConnectionSettings are only exposed when creating a new SocketsHttpHandler. However if I create a new handler with custom settings and use the handler as message handler for my HttpClient I lose my dependency tracking.

Upon debugging I noticed that the default message handler is HttpClientHandler which includes a DiagnosticsHandler that is not publicly exposed.

How can I customize HttpConnectionSettings while still seeing request diagnostics?

I don't necessarily want to copy DiagnosticsHandler into my code to accomplish this.

On first glance I could see two possible solutions:

  1. Expose DiagnosticsHandler publicly, so consumers can wrap their custom HttpHandlers with it.
  2. Provide a way to customize HttpConnectionSettings without creating a new message handler, so consumers can use the HttpClientHandler while still being able to configure connection behavior.
@stephentoub
Copy link
Member

cc: @lmolkova

@scalablecory
Copy link
Contributor

Can you give more detail on how explicitly using a SocketsHttpHandler breaks dependency tracking?

Max connections per server can be customized via the HttpClientHandler API already, if your goal is to continue using diagnostics:

using var handler = new HttpClientHandler { MaxConnectionsPerServer = 10 };
using var client = new HttpClient(handler);

@stephentoub
Copy link
Member

@scalablecory, DiagnosticHandler is created and wrapped around SocketsHttpHandler in HttpClientHandler, e.g.
https://github.com/dotnet/corefx/blob/a3f71bfe819463e388fa7afe77a579ebf99ac866/src/System.Net.Http/src/System/Net/Http/HttpClientHandler.Windows.cs#L31
If you construct a different handler than HttpClientHandler, DiagnosticHandler isn't created.

@scalablecory
Copy link
Contributor

@stephentoub yes, that is clear.

@rangp
Copy link
Author

rangp commented Oct 24, 2019

@scalablecory Thanks for the info, I didn't see that HttpClientHandler is customizable this way. However I would also like to adjust MaxResponseDrainSize which I can't see as a property of HttpClientHandler.

I think that my dependency tracking is broken because DiagnosticsHandler is missing around my SocketsHttpHandler. I'm using application insights' automatic dependency tracking that seems to integrate with the diagnostics propagated by DiagnosticsHandler.

In general I would like the freedom to adjust the settings of the SocketsHttpHandler contained in my HttpClient without losing the diagnostics. The issue I'm having is that I can't access the SocketsHttpHandler that's automatically created by HttpClientHandler and as explained I can't just create a SocketsHttpHandler myself without losing diagnostic information.

I just posted this as an issue because I expected to be able to adjust the full set of HttpConnectionSettings without trade-offs like losing dependency tracking

@msftgits msftgits transferred this issue from dotnet/corefx Feb 1, 2020
@ctaggart
Copy link
Contributor

The dotnet ApplicationInsights SDK also builds on this. If we create a custom SocketsHttpHandler, we loose http request tracking.

@karelz karelz added enhancement Product code improvement that does NOT require public API changes/additions untriaged New issue has not been triaged by the area owner labels Feb 22, 2020
@karelz
Copy link
Member

karelz commented Mar 26, 2020

Triage: We should do something -- there is multiple options - expose it as type (not a very interesting one), static factory, or HttpClient property to enable it.
Xamarin workloads have native handlers and that's why we need to make it work outside of just SocketsHttpHandler.

We should make it happen in 5.0 as AppInsights is important scenario for custom handler -- Xamarin and SocketsHttpHandler.

@karelz karelz added api-suggestion Early API idea and discussion, it is NOT ready for implementation and removed enhancement Product code improvement that does NOT require public API changes/additions untriaged New issue has not been triaged by the area owner labels Mar 26, 2020
@karelz karelz added this to the 5.0 milestone Mar 26, 2020
@Tratcher
Copy link
Member

Workaround: you can probably copy DiagnosticsHandler into your project. Has anyone tried that?

@JamesNK
Copy link
Member

JamesNK commented Jul 17, 2020

What about making DiagnosticsHandler public?

gRPC client will be customizing the SocketsHttpHandler and so will lose the automatic diagnostics functionality that HttpClientHandler currently offers. This isn't a big deal because the gRPC client already has its own Activity, but I'll need to add some logic to propagate the trace ID in the header. Copy and paste this I think.

@karelz
Copy link
Member

karelz commented Jul 17, 2020

@JamesNK it is one of the options listed above - #31261 (comment) ... we just didn't get to making a decision here :(.

@lmolkova
Copy link

I was OOF for quite a while, sorry for not commenting earlier.

I agree that exposing DiagnosticasHandler make sense as it enabled diagnostics for custom/customized innermost handler, and makes instrumentation testable.
I guess we might want to expose some configuration and perhaps different defaults (so we don't enable back-compat stuff).

@karelz assuming we make decision to expose it , did we miss .NET 5 train or there is still a chance ?

@cijothomas
Copy link
Contributor

I was OOF for quite a while, sorry for not commenting earlier.

I agree that exposing DiagnosticasHandler make sense as it enabled diagnostics for custom/customized innermost handler, and makes instrumentation testable.
I guess we might want to expose some configuration and perhaps different defaults (so we don't enable back-compat stuff).

@karelz assuming we make decision to expose it , did we miss .NET 5 train or there is still a chance ?

Please consider making backports to 2.1 and 3.1 as well to benefit more customers. :) Many of the affected customers are not going to be in 5.0 anytime soon :(

@karelz
Copy link
Member

karelz commented Jul 23, 2020

The chance to get it into 5.0 is very low at this point. The team is busy, time is short, this is not the highest priority issue for 5.0 and the design is unclear and will require iterations - #31261 (comment)

@cijothomas .NET Core does not add new APIs in servicing -- I think it is because we don't ship new ref assemblies to use the new APIs.
Let's first focus on getting it fixed properly somewhere, before we start muddying water with backport requests, etc.

If targeting older versions is top need, then we should think about temporary alternarives like standalone package outside of .NET Core.

@avaneerd
Copy link

avaneerd commented Jan 18, 2021

Workaround: you can probably copy DiagnosticsHandler into your project. Has anyone tried that?

I have tried this and this causes other issues.

Using reflection does work:

        private static Func<SocketsHttpHandler, DelegatingHandler> CreateDiagnosticsHandlerFactory()
        {
            var diagnosticsHandler = typeof(SocketsHttpHandler).Assembly.GetType("System.Net.Http.DiagnosticsHandler");

            var ctor = diagnosticsHandler!.GetConstructors().First();
            var param = Expression.Parameter(typeof(HttpMessageHandler), "innerHandler");
            var newExp = Expression.New(ctor, param);
            var lambda = Expression.Lambda<Func<SocketsHttpHandler, DelegatingHandler>>(newExp, param);

            return lambda.Compile();
        }
var createDiagnosticsHandler= CreateDiagnosticsHandlerFactory();
var diagnosticsHandler = createDiagnosticsHandler(socketsHttpHandler);

@JamesNK
Copy link
Member

JamesNK commented Feb 24, 2021

Problem again: grpc/grpc-dotnet#1210

I have fixed it in grpc-dotnet by basically duplicating all of DiagnosticsSource: grpc/grpc-dotnet#1211

@jeffreyrivor
Copy link

@karelz @JamesNK is there a plan of record for this in .NET 6.0? With a preview already out and 6.0 being the LTS version, it seems like we should make a fix or at least some forward progress on some scenarios.

Perhaps getting a DiagnosticsHandler equivalent added in SocketsHttpHandler.SetupHandlerChain() when SocketsHttpHandler is constructed directly instead of through HttpClientHandler as a stopgap improvement if we can't make DiagnosticsHandler public in 6.0? At least that would fix microsoft/ApplicationInsights-dotnet#1434 but we have to be careful to use the same private activity classes because AppInsights implicitly depends on them when extracting activity data via reflection (which is why @avaneerd's workaround is reflection-based).

@karelz
Copy link
Member

karelz commented Apr 6, 2021

@jeffreyrivor yes, it is on our 6.0 list to solve. Technical details how it will be solved are still TBD.

@JamesNK
Copy link
Member

JamesNK commented Apr 6, 2021

I have fixed it in grpc-dotnet by basically duplicating all of DiagnosticsSource: grpc/grpc-dotnet#1211

FYI, AppInsights doesn't like events it listens to being raised by multiple libraries: microsoft/ApplicationInsights-dotnet#2194

I submitted a PR to them.

@paulomorgado
Copy link
Contributor

Regardless of Application Insights (or OpenTelemetry, for that matter) a tracing handler should exist to be injected via IHttpClientFactory.

@ghost ghost added the in-pr There is an active PR which will close this issue when it is merged label Jul 9, 2021
@ghost ghost removed the in-pr There is an active PR which will close this issue when it is merged label Jul 13, 2021
@MihaZupan
Copy link
Member

#55392 changed the behavior of SocketsHttphandler to match HttpClientHandler in honoring the global DistributedContextPropagator.Current.
As such, SocketsHttphandler will now inject trace headers by default.

On top of the global default, the behavior can also be customized on a per-handler basis (#55556):

using var handler = new SocketsHttpHandler
{
    ActivityHeadersPropagator = DistributedContextPropagator.CreatePassThroughPropagator()
};

@ghost ghost locked as resolved and limited conversation to collaborators Sep 2, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
api-suggestion Early API idea and discussion, it is NOT ready for implementation area-System.Net.Http
Projects
None yet
Development

Successfully merging a pull request may close this issue.