@@ -25,18 +25,18 @@ internal static class AspNetParentSpanCorrector
2525{
2626 private const string TelemetryHttpModuleTypeName = "OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule, OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule" ;
2727 private const string TelemetryHttpModuleOptionsTypeName = "OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModuleOptions, OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule" ;
28+ private const string AspNetInstrumentationTypeName = "OpenTelemetry.Instrumentation.AspNet.AspNetInstrumentation, OpenTelemetry.Instrumentation.AspNet" ;
2829
2930 private static readonly ReflectedInfo ? ReflectedValues = Initialize ( ) ;
3031 private static readonly PropertyFetcher < object > RequestFetcher = new ( "Request" ) ;
3132 private static readonly PropertyFetcher < NameValueCollection > HeadersFetcher = new ( "Headers" ) ;
32- private static bool isRegistered ;
33+ private static int isRegistered ;
3334
3435 public static void Register ( )
3536 {
36- if ( ! isRegistered && ReflectedValues != null )
37+ if ( Interlocked . CompareExchange ( ref isRegistered , 1 , 0 ) == 0 )
3738 {
38- ReflectedValues . SubscribeToOnRequestStartedCallback ( ) ;
39- isRegistered = true ;
39+ ReflectedValues ? . SubscribeToOnRequestStartedCallback ( ) ;
4040 }
4141 }
4242
@@ -108,6 +108,11 @@ private static Func<object> GenerateSubscribeLambda()
108108 var telemetryHttpModuleOptionsType = Type . GetType ( TelemetryHttpModuleOptionsTypeName , true ) ;
109109 var onRequestStartedProp = telemetryHttpModuleOptionsType ? . GetProperty ( "OnRequestStartedCallback" ) ?? throw new NotSupportedException ( "TelemetryHttpModuleOptions.OnRequestStartedCallback property not found" ) ;
110110
111+ // ensure that HttpModuleTelemetry callbacks are initialized by the AspNet instrumentation
112+ var aspNetInstrumentationType = Type . GetType ( AspNetInstrumentationTypeName , true ) ;
113+ var aspNetInstrumentationInstance = aspNetInstrumentationType ? . GetField ( "Instance" , BindingFlags . Static | BindingFlags . Public ) ;
114+ _ = aspNetInstrumentationInstance ? . GetValue ( null ) ;
115+
111116 // Get the parameter types from the callback property type itself to avoid hardcoded type loading
112117 var callbackType = onRequestStartedProp . PropertyType ;
113118 var invokeMethod = callbackType . GetMethod ( "Invoke" ) ;
@@ -120,10 +125,16 @@ private static Func<object> GenerateSubscribeLambda()
120125
121126 // Get the existing callback value
122127 var options = Expression . Property ( null , telemetryHttpModuleType , "Options" ) ;
123- var existingCallback = Expression . Property ( options , onRequestStartedProp ) ;
128+
129+ // Capture the existing callback as a constant value at lambda creation time
130+ // This prevents infinite recursion by storing the original callback value before assignment
131+ var captureCallback = Expression . Lambda < Func < object > > (
132+ Expression . Convert ( Expression . Property ( options , onRequestStartedProp ) , typeof ( object ) ) ) . Compile ( ) ;
133+ var existingCallbackValue = captureCallback ( ) ;
134+ var existingCallback = Expression . Constant ( existingCallbackValue , callbackType ) ;
124135
125136 // Create conditional logic: if existingCallback != null, call it, otherwise return null
126- var nullCheck = Expression . NotEqual ( existingCallback , Expression . Constant ( null , existingCallback . Type ) ) ;
137+ var nullCheck = Expression . NotEqual ( existingCallback , Expression . Constant ( null , callbackType ) ) ;
127138
128139 // Call existing callback if it exists
129140 var callExistingCallback = Expression . Call (
0 commit comments