2
2
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3
3
// See the LICENSE file in the project root for more information
4
4
5
+ using System . Diagnostics ;
5
6
using System . Diagnostics . CodeAnalysis ;
7
+ using System . Reflection ;
6
8
using System . Runtime . CompilerServices ;
7
9
using Elastic . OpenTelemetry ;
8
10
using Elastic . OpenTelemetry . Configuration ;
@@ -135,9 +137,9 @@ private static TracerProviderBuilder WithElasticDefaultsCore(
135
137
return SignalBuilder . WithElasticDefaults ( builder , Signals . Traces , options , components , services , ConfigureBuilder ) ;
136
138
}
137
139
138
- [ UnconditionalSuppressMessage ( "ReflectionAnalysis" , "IL2026" , Justification = "The calls to `AddSqlClientInstrumentation` and " +
139
- "`AssemblyScanning.AddInstrumentationViaReflection` are guarded by a RuntimeFeature.IsDynamicCodeSupported` check and therefore " +
140
- "this method is safe to call in AoT scenarios." ) ]
140
+ [ UnconditionalSuppressMessage ( "ReflectionAnalysis" , "IL2026" , Justification = "The call to AssemblyScanning.AddInstrumentationViaReflection` " +
141
+ "is guarded by a RuntimeFeature.IsDynamicCodeSupported` check and, therefore, this method is safe to call in AoT scenarios." ) ]
142
+
141
143
private static void ConfigureBuilder ( TracerProviderBuilder builder , BuilderState builderState , IServiceCollection ? services )
142
144
{
143
145
const string tracerProviderBuilderName = nameof ( TracerProviderBuilder ) ;
@@ -153,38 +155,28 @@ private static void ConfigureBuilder(TracerProviderBuilder builder, BuilderState
153
155
builder . ConfigureServices ( sc => sc . Configure < OtlpExporterOptions > ( OtlpExporterDefaults . OtlpExporterOptions ) ) ;
154
156
155
157
#if NET9_0_OR_GREATER
156
- if ( SignalBuilder . InstrumentationAssemblyExists ( "OpenTelemetry.Instrumentation.Http.dll" ) )
157
- {
158
- logger . LogHttpInstrumentationFound ( "trace" , tracerProviderBuilderName , builderState . InstanceIdentifier ) ;
159
-
160
- if ( ! RuntimeFeature . IsDynamicCodeSupported )
161
- logger . LogWarning ( "The OpenTelemetry.Instrumentation.Http.dll was found alongside the executing assembly. " +
162
- "When using Native AOT publishing on .NET, the trace instrumentation is not registered automatically. Either register it manually, " +
163
- "or remove the dependency so that the native `System.Net.Http` instrumentation (available in .NET 9) is observed instead." ) ;
164
- }
165
- else
158
+ // .NET 9 introduced semantic convention compatible instrumentation in System.Net.Http so it's recommended to no longer
159
+ // use the contrib instrumentation. We don't bring in the dependency for .NET 9+. However, if the consuming app depends
160
+ // on it, it will be assumed that the user prefers it and therefore we allow the assembly scanning to add it. We don't
161
+ // add the native source to avoid doubling up on spans.
162
+ if ( ! SignalBuilder . InstrumentationAssemblyExists ( "OpenTelemetry.Instrumentation.Http.dll" ) )
166
163
{
167
164
TracerProvderBuilderExtensions . AddActivitySourceWithLogging ( builder , logger , "System.Net.Http" , builderState . InstanceIdentifier ) ;
168
165
}
169
- #else
170
- AddWithLogging ( builder , logger , "HTTP" , b => b . AddHttpClientInstrumentation ( ) , builderState . InstanceIdentifier ) ;
171
166
#endif
172
167
173
- AddWithLogging ( builder , logger , "GrpcClient" , b => b . AddGrpcClientInstrumentation ( ) , builderState . InstanceIdentifier ) ;
174
-
175
168
TracerProvderBuilderExtensions . AddActivitySourceWithLogging ( builder , logger , "Elastic.Transport" , builderState . InstanceIdentifier ) ;
176
169
177
- // NOTE: Despite them having no dependencies. We cannot add the OpenTelemetry.Instrumentation.ElasticsearchClient or
178
- // OpenTelemetry.Instrumentation.EntityFrameworkCore instrumentations here, as including the package references causes
179
- // trimming warnings. We can still add them via reflection.
180
-
181
170
#if NET
182
171
if ( RuntimeFeature . IsDynamicCodeSupported )
183
172
#endif
184
173
{
185
- // This instrumentation is not currently compatible for AoT scenarios.
186
- AddWithLogging ( builder , logger , "SqlClient" , b => b . AddSqlClientInstrumentation ( ) , builderState . InstanceIdentifier ) ;
187
174
SignalBuilder . AddInstrumentationViaReflection ( builder , components , ContribTraceInstrumentation . GetReflectionInstrumentationAssemblies ( ) , builderState . InstanceIdentifier ) ;
175
+
176
+ // This is special-cased because we need to register additional options to ensure we capture exceptions by default
177
+ // This improves the UI experience as requests which cause an exception are highlighted in the UI and users can view the
178
+ // log generated from the span event.
179
+ AddAspNetCoreInstrumentation ( builder , builderState ) ;
188
180
}
189
181
190
182
TracerProvderBuilderExtensions . AddElasticProcessorsCore ( builder , builderState , null , services ) ;
@@ -199,12 +191,79 @@ private static void ConfigureBuilder(TracerProviderBuilder builder, BuilderState
199
191
}
200
192
201
193
logger . LogConfiguredSignalProvider ( nameof ( Signals . Traces ) , nameof ( TracerProviderBuilder ) , builderState . InstanceIdentifier ) ;
194
+ }
195
+
196
+ [ UnconditionalSuppressMessage ( "DynamicCode" , "IL2026" , Justification = "The call to this method is guarded by a RuntimeFeature.IsDynamicCodeSupported` " +
197
+ "check and therefore this method is safe to call in AoT scenarios." ) ]
198
+ [ UnconditionalSuppressMessage ( "DynamicCode" , "IL3050" , Justification = "The call to this method is guarded by a RuntimeFeature.IsDynamicCodeSupported` " +
199
+ "check and therefore this method is safe to call in AoT scenarios." ) ]
200
+ [ UnconditionalSuppressMessage ( "DynamicallyAccessMembers" , "IL2075" , Justification = "The call to this method is guarded by a RuntimeFeature.IsDynamicCodeSupported` " +
201
+ "check and therefore this method is safe to call in AoT scenarios." ) ]
202
+ private static void AddAspNetCoreInstrumentation ( TracerProviderBuilder builder , BuilderState builderState )
203
+ {
204
+ if ( builderState . Components . Options . SkipInstrumentationAssemblyScanning )
205
+ return ;
206
+
207
+ var logger = builderState . Components . Logger ;
202
208
203
- [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
204
- static void AddWithLogging ( TracerProviderBuilder builder , ILogger logger , string name , Action < TracerProviderBuilder > add , string builderIdentifier )
209
+ const string tracerProviderBuilderExtensionsTypeName = "OpenTelemetry.Trace.AspNetCoreInstrumentationTracerProviderBuilderExtensions" ;
210
+ const string aspNetCoreTraceInstrumentationOptionsTypeName = "OpenTelemetry.Instrumentation.AspNetCore.AspNetCoreTraceInstrumentationOptions" ;
211
+ const string extensionMethodName = "AddAspNetCoreInstrumentation" ;
212
+ const string assemblyName = "OpenTelemetry.Instrumentation.AspNetCore" ;
213
+
214
+ var builderTypeName = builder . GetType ( ) . Name ;
215
+
216
+ try
217
+ {
218
+ var tracerProviderBuilderExtensionsType = Type . GetType ( $ "{ tracerProviderBuilderExtensionsTypeName } , { assemblyName } ") ;
219
+ var optionsType = Type . GetType ( $ "{ aspNetCoreTraceInstrumentationOptionsTypeName } , { assemblyName } ") ;
220
+
221
+ if ( tracerProviderBuilderExtensionsType is null )
222
+ {
223
+ logger . LogUnableToFindTypeWarning ( tracerProviderBuilderExtensionsTypeName , assemblyName ) ;
224
+ return ;
225
+ }
226
+
227
+ if ( optionsType is null )
228
+ {
229
+ logger . LogUnableToFindTypeWarning ( aspNetCoreTraceInstrumentationOptionsTypeName , assemblyName ) ;
230
+ return ;
231
+ }
232
+
233
+ Action < object > configureOptions = options =>
234
+ {
235
+ var enrichWithExceptionProperty = options . GetType ( ) . GetProperty ( "EnrichWithException" ) ;
236
+ if ( enrichWithExceptionProperty is not null )
237
+ {
238
+ var enrichWithExceptionDelegate = ( Action < Activity , Exception > ) ( ( activity , ex ) =>
239
+ {
240
+ activity . AddException ( ex ) ;
241
+
242
+ if ( ex . Source is not null )
243
+ {
244
+ activity . SetTag ( "exception.source" , ex . Source ) ;
245
+ }
246
+ } ) ;
247
+
248
+ enrichWithExceptionProperty . SetValue ( options , enrichWithExceptionDelegate ) ;
249
+ }
250
+ } ;
251
+
252
+ var methodInfo = tracerProviderBuilderExtensionsType . GetMethod ( extensionMethodName , BindingFlags . Static | BindingFlags . Public ,
253
+ Type . DefaultBinder , [ typeof ( TracerProviderBuilder ) , typeof ( Action < > ) . MakeGenericType ( optionsType ) ] , null ) ;
254
+
255
+ if ( methodInfo is null )
256
+ {
257
+ logger . LogUnableToFindMethodWarning ( tracerProviderBuilderExtensionsTypeName , extensionMethodName , assemblyName ) ;
258
+ return ;
259
+ }
260
+
261
+ methodInfo . Invoke ( null , [ builder , configureOptions ] ) ;
262
+ }
263
+ catch ( Exception ex )
205
264
{
206
- add . Invoke ( builder ) ;
207
- logger . LogAddedInstrumentation ( name , nameof ( TracerProviderBuilder ) , builderIdentifier ) ;
265
+ logger . LogError ( new EventId ( 503 , "DynamicInstrumentaionFailed" ) , ex , "Failed to dynamically enable " +
266
+ "{InstrumentationName} on {Provider}." , assemblyName , builderTypeName ) ;
208
267
}
209
268
}
210
269
}
0 commit comments