Skip to content

Commit e529a6b

Browse files
Add new instrumentation for System.Servicemodel.Dispatcher.AsyncMethodInvoker and update snapshots. IMPORTANT: Does not work with TcpBinding; OnMethodEnd for the InvokeEnd operation does not detect ambient Scope
1 parent 3928831 commit e529a6b

5 files changed

+286
-0
lines changed
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// <copyright file="AsyncMethodInvoker_InvokeBegin_Integration.cs" company="Datadog">
2+
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License.
3+
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc.
4+
// </copyright>
5+
6+
#if NETFRAMEWORK
7+
using System;
8+
using System.ComponentModel;
9+
using System.Reflection;
10+
using Datadog.Trace.ClrProfiler.CallTarget;
11+
using Datadog.Trace.ClrProfiler.Emit;
12+
using Datadog.Trace.ClrProfiler.Integrations;
13+
using Datadog.Trace.Configuration;
14+
15+
namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Wcf
16+
{
17+
/// <summary>
18+
/// System.ServiceModel.Dispatcher.AsyncMethodInvoker calltarget instrumentation
19+
/// </summary>
20+
[InstrumentMethod(
21+
AssemblyName = "System.ServiceModel",
22+
TypeName = "System.ServiceModel.Dispatcher.AsyncMethodInvoker",
23+
MethodName = "InvokeBegin",
24+
ReturnTypeName = ClrNames.IAsyncResult,
25+
ParameterTypeNames = new[] { ClrNames.Object, "System.Object[]", ClrNames.AsyncCallback, ClrNames.Object },
26+
MinimumVersion = "4.0.0",
27+
MaximumVersion = "4.*.*",
28+
IntegrationName = IntegrationName)]
29+
[Browsable(false)]
30+
[EditorBrowsable(EditorBrowsableState.Never)]
31+
public class AsyncMethodInvoker_InvokeBegin_Integration
32+
{
33+
private const string IntegrationName = nameof(IntegrationIds.Wcf);
34+
private static readonly Func<object> _getCurrentOperationContext;
35+
36+
static AsyncMethodInvoker_InvokeBegin_Integration()
37+
{
38+
var operationContextType = Type.GetType("System.ServiceModel.OperationContext, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", throwOnError: false);
39+
if (operationContextType is not null)
40+
{
41+
var property = operationContextType.GetProperty("Current", BindingFlags.Public | BindingFlags.Static);
42+
var method = property.GetGetMethod();
43+
_getCurrentOperationContext = (Func<object>)method.CreateDelegate(typeof(Func<object>));
44+
}
45+
}
46+
47+
/// <summary>
48+
/// OnMethodBegin callback
49+
/// </summary>
50+
/// <typeparam name="TTarget">Type of the target</typeparam>
51+
/// <param name="instance">Instance value, aka `this` of the instrumented method.</param>
52+
/// <param name="instanceArg">RequestContext instance</param>
53+
/// <param name="inputs">Input arguments</param>
54+
/// <param name="callback">Callback argument</param>
55+
/// <param name="state">State argument</param>
56+
/// <returns>Calltarget state value</returns>
57+
public static CallTargetState OnMethodBegin<TTarget>(TTarget instance, object instanceArg, object[] inputs, AsyncCallback callback, object state)
58+
{
59+
// TODO Just use the OperationContext.Current object to get the span information
60+
// context.IncomingMessageHeaders contains:
61+
// - Action
62+
// - To
63+
//
64+
// context.IncomingMessageProperties contains:
65+
// - ["httpRequest"] key to find distributed tracing headers
66+
if (_getCurrentOperationContext is null || !Tracer.Instance.Settings.WcfEnableNewInstrumentation)
67+
{
68+
return CallTargetState.GetDefault();
69+
}
70+
71+
var requestContext = _getCurrentOperationContext()?.GetProperty<object>("RequestContext").GetValueOrDefault();
72+
return new CallTargetState(WcfIntegration.CreateScope(requestContext));
73+
}
74+
}
75+
}
76+
#endif
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// <copyright file="AsyncMethodInvoker_InvokeEnd_Integration.cs" company="Datadog">
2+
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License.
3+
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc.
4+
// </copyright>
5+
6+
#if NETFRAMEWORK
7+
using System;
8+
using System.ComponentModel;
9+
using System.Reflection;
10+
using Datadog.Trace.ClrProfiler.CallTarget;
11+
using Datadog.Trace.Configuration;
12+
13+
namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Wcf
14+
{
15+
/// <summary>
16+
/// System.ServiceModel.Dispatcher.AsyncMethodInvoker calltarget instrumentation
17+
/// </summary>
18+
[InstrumentMethod(
19+
AssemblyName = "System.ServiceModel",
20+
TypeName = "System.ServiceModel.Dispatcher.AsyncMethodInvoker",
21+
MethodName = "InvokeEnd",
22+
ReturnTypeName = ClrNames.Object,
23+
ParameterTypeNames = new[] { ClrNames.Object, "System.Object[]&", ClrNames.IAsyncResult },
24+
TargetMethodArgumentsToLoad = new ushort[] { 0, 2 }, // DO NOT pass the "out object[]" parameter into the instrumentation method
25+
MinimumVersion = "4.0.0",
26+
MaximumVersion = "4.*.*",
27+
IntegrationName = IntegrationName)]
28+
[Browsable(false)]
29+
[EditorBrowsable(EditorBrowsableState.Never)]
30+
public class AsyncMethodInvoker_InvokeEnd_Integration
31+
{
32+
private const string IntegrationName = nameof(IntegrationIds.Wcf);
33+
private static readonly Func<object> _getCurrentOperationContext;
34+
35+
static AsyncMethodInvoker_InvokeEnd_Integration()
36+
{
37+
var operationContextType = Type.GetType("System.ServiceModel.OperationContext, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", throwOnError: false);
38+
if (operationContextType is not null)
39+
{
40+
var property = operationContextType.GetProperty("Current", BindingFlags.Public | BindingFlags.Static);
41+
var method = property.GetGetMethod();
42+
_getCurrentOperationContext = (Func<object>)method.CreateDelegate(typeof(Func<object>));
43+
}
44+
}
45+
46+
/// <summary>
47+
/// OnMethodEnd callback
48+
/// </summary>
49+
/// <typeparam name="TTarget">Type of the target</typeparam>
50+
/// <typeparam name="TReturn">Type of the response</typeparam>
51+
/// <param name="instance">Instance value, aka `this` of the instrumented method.</param>
52+
/// <param name="returnValue">Return value</param>
53+
/// <param name="exception">Exception instance in case the original code threw an exception.</param>
54+
/// <param name="state">Calltarget state value</param>
55+
/// <returns>A response value, in an async scenario will be T of Task of T</returns>
56+
public static CallTargetReturn<TReturn> OnMethodEnd<TTarget, TReturn>(TTarget instance, TReturn returnValue, Exception exception, CallTargetState state)
57+
{
58+
var scope = Tracer.Instance.ActiveScope;
59+
scope.DisposeWithException(exception);
60+
return new CallTargetReturn<TReturn>(returnValue);
61+
}
62+
}
63+
}
64+
#endif

tracer/src/Datadog.Trace/ClrProfiler/InstrumentationDefinitions.Generated.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,8 @@ private static NativeCallTargetDefinition[] GetDefinitionsArray()
266266
new("StackExchange.Redis.StrongName", "StackExchange.Redis.RedisTransaction", "ExecuteAsync", new[] { "System.Threading.Tasks.Task`1<T>", "StackExchange.Redis.Message", "StackExchange.Redis.ResultProcessor`1[!!0]", "StackExchange.Redis.ServerEndPoint" }, new ushort[] { }, 1, 0, 0, 2, 65535, 65535, assemblyFullName, "Datadog.Trace.ClrProfiler.AutoInstrumentation.Redis.StackExchange.RedisExecuteAsyncIntegration"),
267267

268268
// Wcf
269+
new("System.ServiceModel", "System.ServiceModel.Dispatcher.AsyncMethodInvoker", "InvokeBegin", new[] { "System.IAsyncResult", "System.Object", "System.Object[]", "System.AsyncCallback", "System.Object" }, new ushort[] { }, 4, 0, 0, 4, 65535, 65535, assemblyFullName, "Datadog.Trace.ClrProfiler.AutoInstrumentation.Wcf.AsyncMethodInvoker_InvokeBegin_Integration"),
270+
new("System.ServiceModel", "System.ServiceModel.Dispatcher.AsyncMethodInvoker", "InvokeEnd", new[] { "System.Object", "System.Object", "System.Object[]&", "System.IAsyncResult" }, new ushort[] { 0, 2 }, 4, 0, 0, 4, 65535, 65535, assemblyFullName, "Datadog.Trace.ClrProfiler.AutoInstrumentation.Wcf.AsyncMethodInvoker_InvokeEnd_Integration"),
269271
new("System.ServiceModel", "System.ServiceModel.Dispatcher.ChannelHandler", "HandleRequest", new[] { "System.Boolean", "System.ServiceModel.Channels.RequestContext", "System.ServiceModel.OperationContext" }, new ushort[] { }, 4, 0, 0, 4, 65535, 65535, assemblyFullName, "Datadog.Trace.ClrProfiler.AutoInstrumentation.Wcf.ChannelHandlerIntegration"),
270272
new("System.ServiceModel", "System.ServiceModel.Dispatcher.SyncMethodInvoker", "Invoke", new[] { "System.Object", "System.Object", "System.Object[]", "System.Object[]&" }, new ushort[] { 0, 1 }, 4, 0, 0, 4, 65535, 65535, assemblyFullName, "Datadog.Trace.ClrProfiler.AutoInstrumentation.Wcf.SyncMethodInvokerIntegration"),
271273
new("System.ServiceModel", "System.ServiceModel.Dispatcher.TaskMethodInvoker", "InvokeAsync", new[] { "System.Threading.Tasks.Task`1<System.Tuple`2<System.Object, System.Object[]>>", "System.Object", "System.Object[]" }, new ushort[] { }, 4, 0, 0, 4, 65535, 65535, assemblyFullName, "Datadog.Trace.ClrProfiler.AutoInstrumentation.Wcf.TaskMethodInvokerIntegration"),

tracer/test/snapshots/WcfTests.__binding=BasicHttpBinding_enableCallTarget=True_enableNewWcfInstrumentation=True.verified.txt

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,5 +142,77 @@
142142
_dd.tracer_kr: 1.0,
143143
_dd.top_level: 1.0
144144
}
145+
},
146+
{
147+
TraceId: Id_19,
148+
SpanId: Id_20,
149+
Name: wcf.request,
150+
Resource: WcfSample/ICalculator/ServerAsyncAdd,
151+
Service: Samples.Wcf,
152+
Type: web,
153+
ParentId: Id_21,
154+
Tags: {
155+
env: integration_tests,
156+
http.method: POST,
157+
http.request.headers.host: localhost:00000,
158+
http.url: http://localhost:00000/WcfSample/CalculatorService,
159+
language: dotnet,
160+
runtime-id: Guid_1,
161+
span.kind: server,
162+
version: 1.0.0
163+
},
164+
Metrics: {
165+
_sampling_priority_v1: 1.0,
166+
_dd.tracer_kr: 1.0,
167+
_dd.top_level: 1.0
168+
}
169+
},
170+
{
171+
TraceId: Id_22,
172+
SpanId: Id_23,
173+
Name: wcf.request,
174+
Resource: WcfSample/ICalculator/ServerAsyncAdd,
175+
Service: Samples.Wcf,
176+
Type: web,
177+
ParentId: Id_24,
178+
Tags: {
179+
env: integration_tests,
180+
http.method: POST,
181+
http.request.headers.host: localhost:00000,
182+
http.url: http://localhost:00000/WcfSample/CalculatorService,
183+
language: dotnet,
184+
runtime-id: Guid_1,
185+
span.kind: server,
186+
version: 1.0.0
187+
},
188+
Metrics: {
189+
_sampling_priority_v1: 1.0,
190+
_dd.tracer_kr: 1.0,
191+
_dd.top_level: 1.0
192+
}
193+
},
194+
{
195+
TraceId: Id_25,
196+
SpanId: Id_26,
197+
Name: wcf.request,
198+
Resource: WcfSample/ICalculator/ServerAsyncAdd,
199+
Service: Samples.Wcf,
200+
Type: web,
201+
ParentId: Id_27,
202+
Tags: {
203+
env: integration_tests,
204+
http.method: POST,
205+
http.request.headers.host: localhost:00000,
206+
http.url: http://localhost:00000/WcfSample/CalculatorService,
207+
language: dotnet,
208+
runtime-id: Guid_1,
209+
span.kind: server,
210+
version: 1.0.0
211+
},
212+
Metrics: {
213+
_sampling_priority_v1: 1.0,
214+
_dd.tracer_kr: 1.0,
215+
_dd.top_level: 1.0
216+
}
145217
}
146218
]

tracer/test/snapshots/WcfTests.__binding=WSHttpBinding_enableCallTarget=True_enableNewWcfInstrumentation=True.verified.txt

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,5 +142,77 @@
142142
_dd.tracer_kr: 1.0,
143143
_dd.top_level: 1.0
144144
}
145+
},
146+
{
147+
TraceId: Id_19,
148+
SpanId: Id_20,
149+
Name: wcf.request,
150+
Resource: WcfSample/ICalculator/ServerAsyncAdd,
151+
Service: Samples.Wcf,
152+
Type: web,
153+
ParentId: Id_21,
154+
Tags: {
155+
env: integration_tests,
156+
http.method: POST,
157+
http.request.headers.host: localhost:00000,
158+
http.url: http://localhost:00000/WcfSample/CalculatorService,
159+
language: dotnet,
160+
runtime-id: Guid_1,
161+
span.kind: server,
162+
version: 1.0.0
163+
},
164+
Metrics: {
165+
_sampling_priority_v1: 1.0,
166+
_dd.tracer_kr: 1.0,
167+
_dd.top_level: 1.0
168+
}
169+
},
170+
{
171+
TraceId: Id_22,
172+
SpanId: Id_23,
173+
Name: wcf.request,
174+
Resource: WcfSample/ICalculator/ServerAsyncAdd,
175+
Service: Samples.Wcf,
176+
Type: web,
177+
ParentId: Id_24,
178+
Tags: {
179+
env: integration_tests,
180+
http.method: POST,
181+
http.request.headers.host: localhost:00000,
182+
http.url: http://localhost:00000/WcfSample/CalculatorService,
183+
language: dotnet,
184+
runtime-id: Guid_1,
185+
span.kind: server,
186+
version: 1.0.0
187+
},
188+
Metrics: {
189+
_sampling_priority_v1: 1.0,
190+
_dd.tracer_kr: 1.0,
191+
_dd.top_level: 1.0
192+
}
193+
},
194+
{
195+
TraceId: Id_25,
196+
SpanId: Id_26,
197+
Name: wcf.request,
198+
Resource: WcfSample/ICalculator/ServerAsyncAdd,
199+
Service: Samples.Wcf,
200+
Type: web,
201+
ParentId: Id_27,
202+
Tags: {
203+
env: integration_tests,
204+
http.method: POST,
205+
http.request.headers.host: localhost:00000,
206+
http.url: http://localhost:00000/WcfSample/CalculatorService,
207+
language: dotnet,
208+
runtime-id: Guid_1,
209+
span.kind: server,
210+
version: 1.0.0
211+
},
212+
Metrics: {
213+
_sampling_priority_v1: 1.0,
214+
_dd.tracer_kr: 1.0,
215+
_dd.top_level: 1.0
216+
}
145217
}
146218
]

0 commit comments

Comments
 (0)