Skip to content

Commit 5d610ac

Browse files
authored
[Instrumentation.WCF] AspNetParentSpanCorrector fixes (#3248)
1 parent e7b5215 commit 5d610ac

File tree

3 files changed

+21
-7
lines changed

3 files changed

+21
-7
lines changed

src/OpenTelemetry.Instrumentation.Wcf/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
## Unreleased
44

5+
* Fixes possible infinite recursion when WCF is hosted in ASP.NET.
6+
([#3248](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/3248))
7+
58
## 1.13.0-beta.1
69

710
Released 2025-Oct-15

src/OpenTelemetry.Instrumentation.Wcf/Implementation/AspNetParentSpanCorrector.cs

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -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(

test/OpenTelemetry.Instrumentation.Wcf.Tests/OpenTelemetry.Instrumentation.Wcf.Tests.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
<Reference Include="System.ServiceModel" />
1717
<Reference Include="System.ServiceModel.Web" />
1818
<PackageReference Include="System.Reflection.DispatchProxy" />
19-
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule\OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule.csproj" />
19+
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry.Instrumentation.AspNet\OpenTelemetry.Instrumentation.AspNet.csproj" />
2020
</ItemGroup>
2121

2222
<ItemGroup Condition="'$([MSBuild]::GetTargetFrameworkIdentifier(`$(TargetFramework)`))' == '.NETCoreApp'">

0 commit comments

Comments
 (0)