Skip to content

Commit 9501d58

Browse files
authored
Prefer meter registration via scanning with special case for .NET 9 (#255)
Follow up from #253
1 parent 7576078 commit 9501d58

File tree

4 files changed

+37
-35
lines changed

4 files changed

+37
-35
lines changed

src/Elastic.OpenTelemetry/Extensions/MeterProviderBuilderExtensions.cs

Lines changed: 8 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
using Microsoft.Extensions.DependencyInjection;
1515
using Microsoft.Extensions.Logging;
1616
using OpenTelemetry.Exporter;
17-
using OpenTelemetry.Logs;
1817
using OpenTelemetry.Metrics;
1918
using OpenTelemetry.Resources;
2019

@@ -151,11 +150,11 @@ internal static MeterProviderBuilder WithElasticDefaultsCore(
151150

152151
static void ConfigureBuilder(MeterProviderBuilder builder, BuilderState builderState, IServiceCollection? services)
153152
{
154-
const string loggingProviderName = nameof(MeterProviderBuilder);
153+
const string meterProviderBuilderName = nameof(MeterProviderBuilder);
155154
var components = builderState.Components;
156155
var logger = components.Logger;
157156

158-
logger.LogConfiguringBuilder(loggingProviderName, builderState.InstanceIdentifier);
157+
logger.LogConfiguringBuilder(meterProviderBuilderName, builderState.InstanceIdentifier);
159158

160159
builder.ConfigureResource(r => r.WithElasticDefaults(builderState, services));
161160

@@ -167,20 +166,17 @@ static void ConfigureBuilder(MeterProviderBuilder builder, BuilderState builderS
167166
o.TemporalityPreference = MetricReaderTemporalityPreference.Delta));
168167

169168
#if NET9_0_OR_GREATER
170-
// On .NET 9, the contrib HTTP instrumentation is no longer required. If the dependency exists,
171-
// it will be registered via the reflection-based assembly scanning.
169+
// .NET 9 introduced semantic convention compatible instrumentation in System.Net.Http so it's recommended to no longer
170+
// use the contrib instrumentation. We don't bring in the dependency for .NET 9+. However, if the consuming app depends
171+
// on it, it will be assumed that the user prefers it and therefore we allow the assembly scanning to add it. We don't
172+
// add the native meter to avoid doubling up on metrics.
172173
if (SignalBuilder.InstrumentationAssemblyExists("OpenTelemetry.Instrumentation.Http.dll"))
173174
{
174-
logger.LogHttpInstrumentationFound("metric", nameof(MeterProviderBuilder), builderState.InstanceIdentifier);
175+
logger.LogHttpInstrumentationFound("metric", meterProviderBuilderName, builderState.InstanceIdentifier);
175176

176-
// For native AOT scenarios, the reflection-based assembly scanning will not run.
177-
// Therefore, we log a warning since no HTTP instrumentation will be automatically registered.
178-
// In this scenario, the consumer must register the contrib instrumentation manually, or
179-
// remove the dependency so that the native .NET 9 HTTP instrumentation source will be added
180-
// instead.
181177
if (!RuntimeFeature.IsDynamicCodeSupported)
182178
logger.LogWarning("The OpenTelemetry.Instrumentation.Http.dll was found alongside the executing assembly. " +
183-
"When using Native AOT publishing on .NET, the metric instrumentation is not registered automatically. Either register it manually, " +
179+
"When using Native AOT publishing on .NET, the metrics instrumentation is not registered automatically. Either register it manually, " +
184180
"or remove the dependency so that the native `System.Net.Http` instrumentation (available in .NET 9) is observed instead.");
185181
}
186182
else
@@ -208,12 +204,7 @@ static void ConfigureBuilder(MeterProviderBuilder builder, BuilderState builderS
208204
{
209205
AddMeterWithLogging(builder, logger, "System.Runtime", builderState.InstanceIdentifier);
210206
}
211-
#else
212-
AddWithLogging(builder, logger, "HTTP", b => b.AddHttpClientInstrumentation(), builderState.InstanceIdentifier);
213-
AddWithLogging(builder, logger, "Runtime", b => b.AddRuntimeInstrumentation(), builderState.InstanceIdentifier);
214207
#endif
215-
// We explicity include this dependency and add it, since the current curated metric dashboard requires the memory metric.
216-
AddWithLogging(builder, logger, "Process", b => b.AddProcessInstrumentation(), builderState.InstanceIdentifier);
217208

218209
if (SignalBuilder.InstrumentationAssemblyExists("OpenTelemetry.Instrumentation.AspNetCore.dll"))
219210
{
@@ -254,12 +245,5 @@ static void AddMeterWithLogging(MeterProviderBuilder builder, ILogger logger, st
254245
builder.AddMeter(meterName);
255246
logger.LogMeterAdded(meterName, builderIdentifier);
256247
}
257-
258-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
259-
static void AddWithLogging(MeterProviderBuilder builder, ILogger logger, string name, Action<MeterProviderBuilder> add, string builderIdentifier)
260-
{
261-
add.Invoke(builder);
262-
logger.LogAddedInstrumentation(name, nameof(MeterProviderBuilder), builderIdentifier);
263-
}
264248
}
265249
}

src/Elastic.OpenTelemetry/Extensions/TracerProviderBuilderExtensions.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,16 @@ private static void ConfigureBuilder(TracerProviderBuilder builder, BuilderState
159159
// use the contrib instrumentation. We don't bring in the dependency for .NET 9+. However, if the consuming app depends
160160
// on it, it will be assumed that the user prefers it and therefore we allow the assembly scanning to add it. We don't
161161
// add the native source to avoid doubling up on spans.
162-
if (!SignalBuilder.InstrumentationAssemblyExists("OpenTelemetry.Instrumentation.Http.dll"))
162+
if (SignalBuilder.InstrumentationAssemblyExists("OpenTelemetry.Instrumentation.Http.dll"))
163+
{
164+
logger.LogHttpInstrumentationFound("trace", tracerProviderBuilderName, builderState.InstanceIdentifier);
165+
166+
if (!RuntimeFeature.IsDynamicCodeSupported)
167+
logger.LogWarning("The OpenTelemetry.Instrumentation.Http.dll was found alongside the executing assembly. " +
168+
"When using Native AOT publishing on .NET, the trace instrumentation is not registered automatically. Either register it manually, " +
169+
"or remove the dependency so that the native `System.Net.Http` instrumentation (available in .NET 9) is observed instead.");
170+
}
171+
else
163172
{
164173
TracerProvderBuilderExtensions.AddActivitySourceWithLogging(builder, logger, "System.Net.Http", builderState.InstanceIdentifier);
165174
}

src/Elastic.OpenTelemetry/Instrumentation/ContribMetricsInstrumentation.cs

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -71,19 +71,28 @@ public static InstrumentationAssemblyInfo[] GetMetricsInstrumentationAssembliesI
7171
InstrumentationMethod = "AddEventCountersInstrumentation"
7272
},
7373

74-
#if NET9_0_OR_GREATER
75-
// On .NET 9, we add the `System.Net.Http` source for native instrumentation, rather than referencing
76-
// the contrib instrumentation. However, if the consuming application has their own reference to
77-
// `OpenTelemetry.Instrumentation.Http`, then we use that since it signals the consumer prefers the
78-
// contrib instrumentation. Therefore, on .NET 9+ targets, we attempt to dynamically load the contrib
79-
// instrumentation, when available.
8074
new()
8175
{
82-
Name = "Http",
76+
Name = "HTTP",
8377
Filename = "OpenTelemetry.Instrumentation.Http.dll",
8478
FullyQualifiedType = "OpenTelemetry.Metrics.HttpClientInstrumentationMeterProviderBuilderExtensions",
8579
InstrumentationMethod = "AddHttpClientInstrumentation"
8680
},
87-
#endif
81+
82+
new()
83+
{
84+
Name = "Runtime",
85+
Filename = "OpenTelemetry.Instrumentation.Runtime.dll",
86+
FullyQualifiedType = "OpenTelemetry.Metrics.MeterProviderBuilderExtensions",
87+
InstrumentationMethod = "AddRuntimeInstrumentation"
88+
},
89+
90+
new()
91+
{
92+
Name = "Process",
93+
Filename = "OpenTelemetry.Instrumentation.Process.dll",
94+
FullyQualifiedType = "OpenTelemetry.Metrics.MeterProviderBuilderExtensions",
95+
InstrumentationMethod = "AddProcessInstrumentation"
96+
}
8897
];
8998
}

tests/Elastic.OpenTelemetry.Tests/InstrumentationScanningTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@ public partial class InstrumentationScanningTests(WebApplicationFactory<Program>
2424
private readonly ITestOutputHelper _output = output;
2525

2626
#if NET8_0
27-
[GeneratedRegex(@"^\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}\]\[\d{6}\]\[-*\]\[Debug\]\s+Added contrib instrumentation 'HTTP' to TracerProviderBuilder.*")]
27+
[GeneratedRegex(@"^\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}\]\[\d{6}\]\[-*\]\[Debug\]\s+Added contrib instrumentation 'HTTP' to TracerProviderBuilder*")]
2828
private static partial Regex HttpTracerProviderBuilderRegex();
2929

30-
[GeneratedRegex(@"^\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}\]\[\d{6}\]\[-*\]\[Debug\]\s+Added contrib instrumentation 'HTTP' to MeterProviderBuilder.*")]
30+
[GeneratedRegex(@"^\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}\]\[\d{6}\]\[-*\]\[Debug\]\s+Added contrib instrumentation 'HTTP' to MeterProviderBuilder*")]
3131
private static partial Regex HttpMeterProviderBuilderRegex();
3232
#elif NET9_0
3333
[GeneratedRegex(@"^\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}\]\[\d{6}\]\[-*\]\[Debug\]\s+Added 'System.Net.Http' to TracerProviderBuilder.*")]

0 commit comments

Comments
 (0)