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

[http] Add http.client.request.duration metric and .NET Framework support #4870

Merged

Conversation

matt-hensley
Copy link
Contributor

@matt-hensley matt-hensley commented Sep 20, 2023

Fixes #4764
Towards #4484

Changes

  • Adds the new http.client.request.duration metric in the HttpClient Instrumentation library.

  • Adds support for http.client.duration & http.client.request.duration metrics on .NET Framework. Unlike Add HttpClient metrics for .NET Framework #4768, this new implementation does not require tracing to be enabled for metrics generation to work.

Tests have been updated to cover:

  • Metrics and tracing enabled
  • Only metrics enabled
  • Only tracing enabled
  • Metrics and tracing disabled

Merge requirement checklist

  • CONTRIBUTING guidelines followed (nullable enabled, static analysis, etc.)
  • Unit tests added/updated
  • Appropriate CHANGELOG.md files updated for non-trivial changes
  • Changes in public API reviewed (if applicable)

matt-hensley and others added 30 commits September 14, 2023 20:35
…ensley/opentelemetry-dotnet into net462httpclientmetricsv3

# Conflicts:
#	src/OpenTelemetry.Instrumentation.Http/MeterProviderBuilderExtensions.cs
…equestActivitySource.netfx.cs

Co-authored-by: Johannes Tax <johannes@johannes.tax>
…equestActivitySource.netfx.cs

Co-authored-by: Johannes Tax <johannes@johannes.tax>
…equestActivitySource.netfx.cs

Co-authored-by: Johannes Tax <johannes@johannes.tax>
@codecov
Copy link

codecov bot commented Sep 27, 2023

Codecov Report

Merging #4870 (f2e864b) into main (8ea6bb9) will increase coverage by 0.23%.
The diff coverage is 97.61%.

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #4870      +/-   ##
==========================================
+ Coverage   82.95%   83.19%   +0.23%     
==========================================
  Files         294      294              
  Lines       12206    12279      +73     
==========================================
+ Hits        10125    10215      +90     
+ Misses       2081     2064      -17     
Flag Coverage Δ
unittests 83.19% <97.61%> (+0.23%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files Coverage Δ
...elemetry.Instrumentation.Http/HttpClientMetrics.cs 100.00% <100.00%> (ø)
...ementation/HttpHandlerMetricsDiagnosticListener.cs 97.61% <100.00%> (+20.47%) ⬆️
...rumentation.Http/MeterProviderBuilderExtensions.cs 100.00% <100.00%> (ø)
...umentation.Http/TracerProviderBuilderExtensions.cs 100.00% <100.00%> (ø)
...plementation/HttpWebRequestActivitySource.netfx.cs 82.93% <96.96%> (+2.06%) ⬆️

... and 8 files with indirect coverage changes

@CodeBlanch
Copy link
Member

@vishweshbankwar @alanwest @Kielek This ended up being a collaboration between me and @matt-hensley so I'm not going to self-review. Please take a look when you have time.

Comment on lines +106 to 128
internal static HttpClientInstrumentationOptions TracingOptions
{
get => options;
get => tracingOptions;
set
{
options = value;
tracingOptions = value;

emitOldAttributes = value.HttpSemanticConvention.HasFlag(HttpSemanticConvention.Old);
emitNewAttributes = value.HttpSemanticConvention.HasFlag(HttpSemanticConvention.New);
tracingEmitOldAttributes = value.HttpSemanticConvention.HasFlag(HttpSemanticConvention.Old);
tracingEmitNewAttributes = value.HttpSemanticConvention.HasFlag(HttpSemanticConvention.New);
}
}

internal static HttpClientMetricInstrumentationOptions MetricsOptions
{
get => metricsOptions;
set
{
metricsOptions = value;

metricsEmitOldAttributes = value.HttpSemanticConvention.HasFlag(HttpSemanticConvention.Old);
metricsEmitNewAttributes = value.HttpSemanticConvention.HasFlag(HttpSemanticConvention.New);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fact that old & new attributes is separate for Tracing and Metrics seems wrong to me. The HttpSemanticConvention comes from an environment variable. It should be set once for the whole process.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ya it is weird. I think though if our plan is to remove the new/old/dupe stuff soon we should just live with it for a bit 😄

Having the two does allow you to do some interesting things. Imagine this...

        using var tracerProvider = Sdk.CreateTracerProviderBuilder()
            .AddHttpClientInstrumentation()
            .AddOtlpExporter()
            .ConfigureServices(s => s.AddSingleton<IConfiguration>(new ConfigurationBuilder()
                .AddInMemoryCollection(new Dictionary<string, string> { ["OTEL_SEMCONV_STABILITY_OPT_IN"] = "http" })
                .Build()))
            .Build();

        using var meterProvider = Sdk.CreateTracerProviderBuilder()
            .AddHttpClientInstrumentation()
            .AddPrometheusExporter()
            .ConfigureServices(s => s.AddSingleton<IConfiguration>(new ConfigurationBuilder()
                .AddInMemoryCollection(new Dictionary<string, string> { ["OTEL_SEMCONV_STABILITY_OPT_IN"] = "http/dup" })
                .Build()))
            .Build();

What that would do is send "new" semantic conventions via OTLP for traces and send "dupe" semantic conventions via Prometheus for metrics. That is because we support IConfiguration for each provider not just envvars for a process.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think though if our plan is to remove the new/old/dupe stuff soon we should just live with it for a bit 😄

👍

if (semanticConvention != null && semanticConvention.Value.HasFlag(HttpSemanticConvention.New))
{
Assert.Contains(attributes, kvp => kvp.Key == SemanticConventions.AttributeHttpRequestMethod && kvp.Value.ToString() == normalizedAttributesTestCase[SemanticConventions.AttributeHttpMethod]);
Assert.Contains(attributes, kvp => kvp.Key == SemanticConventions.AttributeServerAddress && kvp.Value.ToString() == normalizedAttributesTestCase[SemanticConventions.AttributeNetPeerName]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a blocker for this PR, but this has a bad smell.
Because the json file wasn't updated, it's using the old attribute names from the test case to validate the new attributes.
When we finally ship stable, the json should be updated to have only the New attributes.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When we finally ship stable, the json should be updated to have only the New attributes.

👍

}
}

HttpClientDuration.Record(durationMs, tags);
Copy link
Member

@vishweshbankwar vishweshbankwar Oct 2, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to use different histogram based on the configured option

metricsEmitOldAttributes - > http.client.duration
metricsEmitNewAttributes -> http.client.request.duration

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this PR is focused on adding metrics to NetFx, where my PR was adding the NEW metric to the library. I don't think we should try to accomplish both goals in a single PR. :)

Copy link
Member

@vishweshbankwar vishweshbankwar Oct 2, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@TimothyMothra I think it is ok to do this here as your PR only updates HttpHandlerMetricsDiagnosticListener. But we need two different Histograms for this to be correct.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the style is "dupe" what happens? You get both old + new histograms each with the full set of tags? Or you get old histogram with the old tags and new histogram with the new tags?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you get old histogram with the old tags and new histogram with the new tags

This^

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These HttpClient tests currently are exercising both the .NET Framework & .NET/Core style instrumentations. If we make this change here the tests will start failing for the .NET/Core style. I'm thinking we should tackle both on @TimothyMothra's PR?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we remove the new conventions from here then?

Copy link
Contributor

@TimothyMothra TimothyMothra Oct 2, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the style is "dupe" what happens? You get both old + new histograms each with the full set of tags? Or you get old histogram with the old tags and new histogram with the new tags?

We would emit old metric using old histogram and old attributes (http.client.duration + miliseconds) AND new metric using new histogram and new attributes (http.client.request.duration + seconds).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we remove the new conventions from here then?

I just pulled @TimothyMothra's work from #4891 into this PR. Seemed like more work to remove it all and then put it back in.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just pulled @TimothyMothra's work from #4891 into this PR.

😲

@CodeBlanch CodeBlanch changed the title Add HttpClient metrics for .NET Framework [http] Add http.client.request.duration metric and .NET Framework support Oct 2, 2023
CodeBlanch and others added 3 commits October 2, 2023 16:05
…lerMetricsDiagnosticListener.cs

Co-authored-by: Vishwesh Bankwar <vishweshbankwar@users.noreply.github.com>
internal static readonly string MeterVersion = AssemblyName.Version.ToString();
internal static readonly Meter Meter = new(MeterName, MeterVersion);
private static readonly Histogram<double> HttpClientDuration = Meter.CreateHistogram<double>("http.client.duration", "ms", "Measures the duration of outbound HTTP requests.");
private static readonly Histogram<double> HttpClientRequestDuration = Meter.CreateHistogram<double>("http.client.request.duration", "s", "Measures the duration of outbound HTTP requests.");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: we are creating an instrument here even in when it will not be used (based on the option selected by user).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree, but I wasn't worried about it. For example if user is using only tracing they'll have a Meter + Histogram today that is just sitting there of no use to them. So what's one more thingy? 🤣 Keeping them static readonlys is nice for hot path perf when they are used though. Plus I figured when @TimothyMothra cleans this up we'll go back to just a single histogram in play. Decided to just keep it dumb/simple, basically.

Copy link
Member

@vishweshbankwar vishweshbankwar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM - We need to work on changelog and readme

Changelog needs to clearly indicate added support for metrics in HttpWebRequest

Readme needs to be updated https://github.com/open-telemetry/opentelemetry-dotnet/blob/main/src/OpenTelemetry.Instrumentation.Http/README.md#list-of-metrics-produced

I am ok with merging this as is and do follow up for items above (In favor of making progress).

Thanks @matt-hensley and @CodeBlanch!

@CodeBlanch
Copy link
Member

@vishweshbankwar

Changelog needs to clearly indicate added support for metrics in HttpWebRequest

Just FYI it will work on .NET Framework for either HttpClient or HttpWebRequest. For that reason I thought it was well enough to just mention support being added "for .NET Framework" on the CHANGELOG. But if you want to make it more detailed feel free 😄

@vishweshbankwar
Copy link
Member

@vishweshbankwar

Changelog needs to clearly indicate added support for metrics in HttpWebRequest

Just FYI it will work on .NET Framework for either HttpClient or HttpWebRequest. For that reason I thought it was well enough to just mention support being added "for .NET Framework" on the CHANGELOG. But if you want to make it more detailed feel free 😄

👍 I meant to clarify this. It may not be clear to users what is the difference between those two metrics. We should consolidate it with details here. @TimothyMothra will work on the follow up.

@CodeBlanch CodeBlanch merged commit 4124d21 into open-telemetry:main Oct 3, 2023
66 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Implement support for metrics in HttpClient for .NET framework
6 participants