-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
Add request metrics to System.Net.Http #85447
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,9 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System.Diagnostics; | ||
using System.Diagnostics.CodeAnalysis; | ||
using System.Diagnostics.Metrics; | ||
using System.IO; | ||
using System.Net.Http.Headers; | ||
using System.Runtime.Versioning; | ||
|
@@ -14,6 +17,26 @@ public class HttpMessageInvoker : IDisposable | |
private volatile bool _disposed; | ||
private readonly bool _disposeHandler; | ||
private readonly HttpMessageHandler _handler; | ||
private Meter? _meter; | ||
private HttpMetrics? _metrics; | ||
|
||
#pragma warning disable CS3003 // Type is not CLS-compliant | ||
public Meter Meter | ||
{ | ||
// TODO: Should the Meter and HttpMetrics be static and shared by default? | ||
get => _meter ??= new Meter("System.Net.Http"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should make sure only one instance is ever created in a race. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree the property isn't thread-safe, but how much do we care about thread safety? It should only be set when the message invoker is created. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The way I read it this field may be If you try sending multiple requests at the same time, you will end up creating more than one instance with the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd go farther, we should use a static so there is only one instance (in the non-DI case). Creating a new instance for every client adds a bunch of unnecessary overhead. |
||
set | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @tarekgh @JamesNK - I mentioned in an earlier email but it may have gotten missed, what do you guys think about consuming an IMeterFactory instead of a Meter? Then the Meter can be produced: _meter = _factory == null ? new Meter("System.Net.Http") : factory.CreateMeter("System.Net.Http"); This eliminates the need for the caller to understand what meter name is expected. This implies that IMeterFactory gets defined in System.Diagnostics.DiagnosticSource.dll which I don't think would be an issue but I might be overlooking something. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was expecting IMeterFactory to be in Microsoft.Extensions.Metrics and so not available here. That's part of why I chose this approach. The other reason for exposing the meter is it makes consuming the metrics for just one client quite simple: var invoker = new HttpMessageInvoker();
var currentRequestsRecorder = new InstrumentRecorder<double>(invoker.Meter, "request-duration"); But that only works if each client/invoker has its own Meter and counters copy, which might not be desirable. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Initially I imagined it there too. Do you feel like it is important for IMeterFactory to not be in System.Diagostics.Metrics?
If users were injecting a factory instead the pattern wouldn't be much different: var IMeterFactory factory = ...
var invoker = new HttpMessageInvoker();
invoker.Factory = factory; // separately, maybe we should be using a constructor overload rather than a property setter?
var currentRequestsRecorder = new InstrumentRecorder<double>(factory, "System.Net.Http", "request-duration"); If the app developer wasn't injecting the factory and using the default Meter then I wouldn't expect them to write tests for that as it would effectively just be testing runtime behavior and not their own code. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It can also be a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Logically There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
While other patterns are certainly possible (such as passing in a Meter or passing in a Func<string,Meter>), many of them aren't as easy to use with DI. I don't expect folks will be creating HttpMessageInvokers from a DI container so its fair to argue it doesn't matter if we pick a pattern that is DI-friendly. I think the counter-argument is that if the DI-friendly pattern becomes a convention then devs will anticipate that pattern regardless whether the type is created from a DI-container and APIs that don't follow the pattern feel unexpected and awkward. For example most people expect that if your type logs via ILogger then your constructor has an ILogger constructor parameter. Writing a class that has a method
Tarek and I chatted earlier. I agree that it felt intuitive for me as well initially for IMeterFactory to be in the Microsoft.Extensions namespace, but I don't think its the only reasonable association. The general guidance with DI is that the types being created from the DI container should not have any tight coupling to the DI container (for example don't have your constructor use IServiceProvider as input argument). This allows using those types without DI because a caller could always manually instantiate the same arguments. This perspective suggests that any of the types that show up in the constructor parameters, such as IMeterFactory, should not be treated as tightly coupled to DI. If the type isn't coupled to DI the fact that it would land in Microsoft.Extensions when all the other Meter types are in System.Diagnostics didn't feel so intuitive any more. Ultimately I assume its rare for .NET developers to manually create an HttpMessageInvoker so if folks think its not helpful to follow the IMeterFactory pattern on this one I'll be fine with it. If the pattern is also going to show up on HttpClient I might push a little harder due to more exposure, but still I expect the main usage for injecting a Meter will come from HttpClientFactory internally rather than .NET devs manually injecting one. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The pattern with
In that case, wouldn't it be straightforward to make
Even if it's not tightly coupled to DI, the question is if there are any other reasons to make it land in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
As @antonfirsov mentioned, Manually creating The way I imagined this hanging together is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Ah! I didn't catch that relationship. Thanks for the important info.
OK. If HttpClient already has that pattern then I guess we have clashing patterns, but it probably makes sense for HttpClient's pattern to take priority.
I agree it doesn't take a lot of code to do that wrapping, but assume it was already the plan that IMeterFactory was in System.Diagnostics.DiagnosticSource.dll - would there be any reason to wrap it in the first place?
I don't have any concrete plan to point to, but other forms of IO like files or pipes seems like potential candidates for metrics in the System.* namespaces. Its also possible we'd want System.Runtime metrics reported via a factory-produced Meter.
I don't think we need more reasons to meet the bar, but these are the pros/cons as I see it:
When I look at the balance scales I see it as small upside and effectively no downside. |
||
{ | ||
// TODO: Check that HttpMessageInvoker hasn't been started. | ||
ArgumentNullException.ThrowIfNull(value); | ||
if (value.Name != "System.Net.Http") | ||
{ | ||
throw new ArgumentException("Meter name must be 'System.Net.Http'."); | ||
} | ||
_meter = value; | ||
} | ||
} | ||
#pragma warning restore CS3003 // Type is not CLS-compliant | ||
|
||
public HttpMessageInvoker(HttpMessageHandler handler) | ||
: this(handler, true) | ||
|
@@ -30,16 +53,27 @@ public HttpMessageInvoker(HttpMessageHandler handler, bool disposeHandler) | |
_disposeHandler = disposeHandler; | ||
} | ||
|
||
[MemberNotNull(nameof(_metrics))] | ||
private void EnsureMetrics() | ||
{ | ||
_metrics ??= new HttpMetrics(Meter); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similar to |
||
} | ||
|
||
[UnsupportedOSPlatformAttribute("browser")] | ||
public virtual HttpResponseMessage Send(HttpRequestMessage request, CancellationToken cancellationToken) | ||
{ | ||
ArgumentNullException.ThrowIfNull(request); | ||
|
||
ObjectDisposedException.ThrowIf(_disposed, this); | ||
|
||
EnsureMetrics(); | ||
|
||
if (ShouldSendWithTelemetry(request)) | ||
{ | ||
long startTimestamp = Stopwatch.GetTimestamp(); | ||
|
||
HttpTelemetry.Log.RequestStart(request); | ||
_metrics.RequestStart(request); | ||
|
||
HttpResponseMessage? response = null; | ||
try | ||
|
@@ -55,6 +89,7 @@ public virtual HttpResponseMessage Send(HttpRequestMessage request, Cancellation | |
finally | ||
{ | ||
HttpTelemetry.Log.RequestStop(response); | ||
_metrics.RequestStop(request, response, startTimestamp, Stopwatch.GetTimestamp()); | ||
} | ||
} | ||
else | ||
|
@@ -69,16 +104,21 @@ public virtual Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, C | |
|
||
ObjectDisposedException.ThrowIf(_disposed, this); | ||
|
||
EnsureMetrics(); | ||
|
||
if (ShouldSendWithTelemetry(request)) | ||
{ | ||
return SendAsyncWithTelemetry(_handler, request, cancellationToken); | ||
return SendAsyncWithTelemetry(_handler, request, _metrics, cancellationToken); | ||
} | ||
|
||
return _handler.SendAsync(request, cancellationToken); | ||
|
||
static async Task<HttpResponseMessage> SendAsyncWithTelemetry(HttpMessageHandler handler, HttpRequestMessage request, CancellationToken cancellationToken) | ||
static async Task<HttpResponseMessage> SendAsyncWithTelemetry(HttpMessageHandler handler, HttpRequestMessage request, HttpMetrics metrics, CancellationToken cancellationToken) | ||
{ | ||
long startTimestamp = Stopwatch.GetTimestamp(); | ||
|
||
HttpTelemetry.Log.RequestStart(request); | ||
metrics.RequestStart(request); | ||
Comment on lines
121
to
+122
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With this, we will face all the overhead of both I would do a second check for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we make sure these checks exist in Interlocked.Increment(ref _startedRequests);
if (IsEnabled(...))
{
RequestStart(
request.RequestUri.Scheme,
request.RequestUri.IdnHost...);
} The counter fields should continue to be updated, but we don't have to read the Uri properties/serialize the event if the EventSource isn't enabled. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note that this logic does not apply to |
||
|
||
HttpResponseMessage? response = null; | ||
try | ||
|
@@ -94,12 +134,13 @@ static async Task<HttpResponseMessage> SendAsyncWithTelemetry(HttpMessageHandler | |
finally | ||
{ | ||
HttpTelemetry.Log.RequestStop(response); | ||
metrics.RequestStop(request, response, startTimestamp, Stopwatch.GetTimestamp()); | ||
} | ||
} | ||
} | ||
|
||
private static bool ShouldSendWithTelemetry(HttpRequestMessage request) => | ||
HttpTelemetry.Log.IsEnabled() && | ||
private bool ShouldSendWithTelemetry(HttpRequestMessage request) => | ||
(HttpTelemetry.Log.IsEnabled() || _metrics!.RequestCountersEnabled()) && | ||
!request.WasSentByHttpClient() && | ||
request.RequestUri is Uri requestUri && | ||
requestUri.IsAbsoluteUri; | ||
|
@@ -124,7 +165,6 @@ protected virtual void Dispose(bool disposing) | |
if (disposing && !_disposed) | ||
{ | ||
_disposed = true; | ||
|
||
if (_disposeHandler) | ||
{ | ||
_handler.Dispose(); | ||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,85 @@ | ||||||||||||||||||||||||
// Licensed to the .NET Foundation under one or more agreements. | ||||||||||||||||||||||||
// The .NET Foundation licenses this file to you under the MIT license. | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
using System.Diagnostics; | ||||||||||||||||||||||||
using System.Diagnostics.Metrics; | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
namespace System.Net.Http | ||||||||||||||||||||||||
{ | ||||||||||||||||||||||||
internal sealed class HttpMetrics | ||||||||||||||||||||||||
{ | ||||||||||||||||||||||||
private readonly Meter _meter; | ||||||||||||||||||||||||
private readonly UpDownCounter<long> _currentRequests; | ||||||||||||||||||||||||
private readonly Histogram<double> _requestsDuration; | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
public HttpMetrics(Meter meter) | ||||||||||||||||||||||||
{ | ||||||||||||||||||||||||
_meter = meter; | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
_currentRequests = _meter.CreateUpDownCounter<long>( | ||||||||||||||||||||||||
"current-requests", | ||||||||||||||||||||||||
description: "The duration of outbound HTTP requests."); | ||||||||||||||||||||||||
JamesNK marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||
|
||||||||||||||||||||||||
_requestsDuration = _meter.CreateHistogram<double>( | ||||||||||||||||||||||||
"request-duration", | ||||||||||||||||||||||||
unit: "s", | ||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If unit is specified here then changing the unit will mean we will have no option but to create a new instrument as the requirement for us is to use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is the issue specifically that the unit was specified? For example if the code didn't specify a unit but still captured measurements in seconds would that solve the problem? My understanding is that it would not. I think emitting the measurement in seconds (and marking the unit that way) is the right thing for .NET to do because it appears to be the overall approach industry at large is standardizing on. However I also get that not all consumers are aligned behind a single standard and in particular we have MS 1P customers who want ms measurements for compat with their existing practice. I believe we need to invest in techniques that let us present different views of the data to different customer segments. A couple ideas how that might happen:
(1) or (2) seem like they might be our shortest-term options but (3) and (4) are probably where I'd hope we end up eventually. |
||||||||||||||||||||||||
description: "Number of outbound HTTP requests that are currently active on the client."); | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
public void RequestStart(HttpRequestMessage request) | ||||||||||||||||||||||||
{ | ||||||||||||||||||||||||
#pragma warning disable SA1129 // Do not use default value type constructor | ||||||||||||||||||||||||
var tags = new TagList(); | ||||||||||||||||||||||||
#pragma warning restore SA1129 // Do not use default value type constructor | ||||||||||||||||||||||||
Comment on lines
+39
to
+41
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Does this work to avoid the warning? |
||||||||||||||||||||||||
InitializeCommonTags(ref tags, request); | ||||||||||||||||||||||||
_currentRequests.Add(1, tags); | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
public void RequestStop(HttpRequestMessage request, HttpResponseMessage? response, long startTimestamp, long currentTimestamp) | ||||||||||||||||||||||||
{ | ||||||||||||||||||||||||
#pragma warning disable SA1129 // Do not use default value type constructor | ||||||||||||||||||||||||
var tags = new TagList(); | ||||||||||||||||||||||||
#pragma warning restore SA1129 // Do not use default value type constructor | ||||||||||||||||||||||||
InitializeCommonTags(ref tags, request); | ||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because of redirects you may log different tags here than at There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Properties on In that case, what request values should be used? Options:
No perfect choice. I like option 2, but curious about what client networking experts think and what current diagnostics does. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yes, the request is being reused to save allocations, but I think this is a fundamental problem, regardless of our implementation.
I don't think this concern exists in case of existing counters. The only ones where we have something similar to tags are the
Wouldn't this be unexpected to users? For example by using
Isn't it also an option to record multiple requests on redirects? (Even though it means that the implementation would be bound to I have opened open-telemetry/opentelemetry-specification#3462, hoping that OTel folks can help clarifying this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yeah existing counters don't have request-specific info on them, but I was thinking about what is in logs and the activity. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree with Anton that relying on the request message's version can be quite misleading. The unfortunate thing is that we'll only know what version is actually used when we try sending the request to the wire internally, or from a higher-level perspective, once you get back the response message object. It may make sense to consider moving this logic further down into There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because the version isn't known when a request starts, it's not included as a tag on |
||||||||||||||||||||||||
_currentRequests.Add(-1, tags); | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
if (response != null) | ||||||||||||||||||||||||
{ | ||||||||||||||||||||||||
tags.Add("status-code", (int)response.StatusCode); // Boxing? | ||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One option is having a cache of boxed commonly used status codes. |
||||||||||||||||||||||||
tags.Add("protocol", $"HTTP/{response.Version}"); // Hacky | ||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hacky because it relies on formatting implementation? Shall we do something like this to avoid the formatting allocation? static string GetProtocolName6(Version httpVersion) => (httpVersion.Major, httpVersion.Minor) switch
{
(1, 1) => "HTTP/1.1",
(2, 0) => "HTTP/2.0",
(3, 0) => "HTTP/3.0",
_ => "unknown"
}; There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hacky because it allocates, some values aren't right*, and it's simple to improve such as your snippet. * There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should avoid allocation. We wouldn't want our services to pay the allocation cost for something which our library will anyways drop and not emit. Protocol is one of them |
||||||||||||||||||||||||
} | ||||||||||||||||||||||||
if (request.HasTags) | ||||||||||||||||||||||||
{ | ||||||||||||||||||||||||
foreach (var customTag in request.MetricsTags) | ||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similar to HttpRequest properties not being stable, these tags might also not be stable across the duration of the request. Your EnrichmentHandler test is an example that deliberately changes them. I think one scenario we need to sort out is the case where the caller to HttpClient is some 3rd party library that isn't specifying tags but the app developer does want to get all their traffic classified. Currently R9 is handling this case by having the app developer create a mapping from outbound URIs -> other metadata, then they register a handler in the HttpClientFactory to execute that mapping and generate the tagged telemetry. If R9 is going to switch over to use these built-in metrics rather than continuing to add their own then they will need some way to dynamically inject those tags. If the injection mechanism will continue to be a handler (similar to your EnrichmentHandler), then perhaps we need to move the metric collection point back to the DiagnosticsHandler where it is now so that other handlers have a chance to update those tags before they are captured. (I know I suggested doing it at the front earlier, I'm not sure my idea was a good one now that I consider this scenario) @dpk83 - you probably want to take a look at this if you haven't already There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another question, will the same metrics support be added for net framework or is it limited to net core only? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't understand the concern here. R9 has a DelegatingHandler that is responsible for recording to the metric. The handler also executes enrichers to build up a collection of custom tags. The difference in this prototype is recording to the metric and gathering custom tags happens in different places. The handler writes them onto the My main concern with this approach is making sure there is the opportunity for an enrichment handler to add tags after A solution that supports both goals could be to wait to record the HTTP duration counter value when an HTTP request has completed both of these actions:
It would be slightly odd that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry for confusion, upon re-reading the code I see that I misunderstood it earlier and my comment was based on that misunderstanding. I was incorrectly worried that custom tags would cause the current-requests increment operation to be recorded with different tags than the decrement which would unbalance it. Now I see that custom tags are only used for the duration measurement and my concern was unfounded. While I do still care about the scenario I described above with 3rd party libraries, I think this design would be able to handle it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Few points:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Note that Wouldn't it be better to implement metrics in a separate There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I didn't know about that switch. Good to know. If a separate handler is better, then do it. The difference we're considering is the same: counters at the HttpMessageInvoker layer vs counters at an inner handler. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
What perf overhead are you worried about @dpk83? While I don't think R9 strictly has to use this counter for correct behavior, from the consumer's perspective I'd really hope to describe the R9 stuff to 3P devs as an enhancement and not another orthogonal telemetry mechanism. I am leaving out here 1P back-compat where we probably want a different schema. For example I don't think it would be good if 3P devs see duplicate measurements. That would mean that R9 either needs to add tags to this instrument or suppress the instrument and replicate it, including the new enrichment tags. Making sure an alternate implementation stays in-sync feels possible, but error-prone and more work intensive. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I think this got answered during our meeting earlier, but reproducing it here - the new metrics would only be present in .NET 8 onwards. This code doesn't flow downlevel either to framework or to older versions of .NET Core. |
||||||||||||||||||||||||
{ | ||||||||||||||||||||||||
tags.Add(customTag); | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
var duration = Stopwatch.GetElapsedTime(startTimestamp, currentTimestamp); | ||||||||||||||||||||||||
_requestsDuration.Record(duration.TotalSeconds, tags); | ||||||||||||||||||||||||
JamesNK marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
private static void InitializeCommonTags(ref TagList tags, HttpRequestMessage request) | ||||||||||||||||||||||||
{ | ||||||||||||||||||||||||
if (request.RequestUri is { } requestUri && requestUri.IsAbsoluteUri) | ||||||||||||||||||||||||
{ | ||||||||||||||||||||||||
if (requestUri.Scheme is not null) | ||||||||||||||||||||||||
{ | ||||||||||||||||||||||||
tags.Add("scheme", requestUri.Scheme); | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
if (requestUri.Host is not null) | ||||||||||||||||||||||||
{ | ||||||||||||||||||||||||
tags.Add("host", requestUri.Host); | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
Comment on lines
+88
to
+95
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Do you want this to be the IP even if we know the host (the header is present)? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't know. OpenTelemetry has the host tag - called There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is your thought around using
I think want the name whenever it has been provided (which I assume is what Host does). @dpk83 - You may have considered this in the past, any thoughts on this one? |
||||||||||||||||||||||||
// Add port tag when not the default value for the current scheme | ||||||||||||||||||||||||
if (!requestUri.IsDefaultPort) | ||||||||||||||||||||||||
{ | ||||||||||||||||||||||||
tags.Add("port", requestUri.Port); | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
tags.Add("method", request.Method.Method); | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
internal bool RequestCountersEnabled() => _currentRequests.Enabled || _requestsDuration.Enabled; | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be able to use
[CLSCompliant(false)]
instead of these pragmas