migrate more integrations to newer MethodBuilder (#494)
* use MethodBuilder in AspNetMvcIntegration * use MethodBuilder in AspNetWebApi2Integration * use MethodBuilder in WcfIntegration * add object.GetInstrumentedInterface() extension method * replace uses of WithConcreteTypeName() to WithConcreteType() * replace more uses of WithConcreteTypeName() to WithConcreteType() * remove unused MethodBuilder.WithConcreteTypeName() * clean up code * rename fields for clarity * add more type name constants * stop using "dynamic" keyword * use object.GetInstrumentedType() * remove unused static Type fields * use object.GetInstrumentedType() * add more type name constants, remove unused static Type fields * stop using "dynamic" keyword * use GetInstrumentedInterface() instead of GetInstrumentedType()
This commit is contained in:
parent
2b458b0315
commit
b735a4bd38
|
|
@ -81,12 +81,6 @@ namespace Datadog.Trace.ClrProfiler.Emit
|
|||
return this;
|
||||
}
|
||||
|
||||
public MethodBuilder<TDelegate> WithConcreteTypeName(string typeName)
|
||||
{
|
||||
var concreteType = _resolutionModule?.GetType(typeName);
|
||||
return this.WithConcreteType(concreteType);
|
||||
}
|
||||
|
||||
public MethodBuilder<TDelegate> WithNamespaceAndNameFilters(params string[] namespaceNameFilters)
|
||||
{
|
||||
_namespaceAndNameFilter = namespaceNameFilters;
|
||||
|
|
|
|||
|
|
@ -22,13 +22,15 @@ namespace Datadog.Trace.ClrProfiler.Integrations
|
|||
/// <summary>
|
||||
/// Type for unobtrusive hooking into Microsoft.AspNetCore.Mvc.Core pipeline.
|
||||
/// </summary>
|
||||
private const string DiagnosticSource = "Microsoft.AspNetCore.Mvc.Internal.MvcCoreDiagnosticSourceExtensions";
|
||||
private const string DiagnosticSourceTypeName = "Microsoft.AspNetCore.Mvc.Internal.MvcCoreDiagnosticSourceExtensions";
|
||||
|
||||
/// <summary>
|
||||
/// Base type used for traversing the pipeline in Microsoft.AspNetCore.Mvc.Core.
|
||||
/// </summary>
|
||||
private const string ResourceInvoker = "Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker";
|
||||
private const string ResourceInvokerTypeName = "Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker";
|
||||
|
||||
private static readonly Type DiagnosticSourceType = Type.GetType($"{DiagnosticSourceTypeName}, {AspnetMvcCore}");
|
||||
private static readonly Type ResourceInvokerType = Type.GetType($"{ResourceInvokerTypeName}, {AspnetMvcCore}");
|
||||
private static readonly ILog Log = LogProvider.GetLogger(typeof(AspNetCoreMvc2Integration));
|
||||
|
||||
private readonly object _httpContext;
|
||||
|
|
@ -47,14 +49,14 @@ namespace Datadog.Trace.ClrProfiler.Integrations
|
|||
var request = _httpContext.GetProperty("Request").GetValueOrDefault();
|
||||
|
||||
GetTagValues(
|
||||
actionDescriptor,
|
||||
request,
|
||||
out string httpMethod,
|
||||
out string host,
|
||||
out string resourceName,
|
||||
out string url,
|
||||
out string controllerName,
|
||||
out string actionName);
|
||||
actionDescriptor,
|
||||
request,
|
||||
out string httpMethod,
|
||||
out string host,
|
||||
out string resourceName,
|
||||
out string url,
|
||||
out string controllerName,
|
||||
out string actionName);
|
||||
|
||||
SpanContext propagatedContext = null;
|
||||
var tracer = Tracer.Instance;
|
||||
|
|
@ -94,10 +96,10 @@ namespace Datadog.Trace.ClrProfiler.Integrations
|
|||
var span = _scope.Span;
|
||||
|
||||
span.DecorateWebServerSpan(
|
||||
resourceName: resourceName,
|
||||
method: httpMethod,
|
||||
host: host,
|
||||
httpUrl: url);
|
||||
resourceName: resourceName,
|
||||
method: httpMethod,
|
||||
host: host,
|
||||
httpUrl: url);
|
||||
|
||||
span.SetTag(Tags.AspNetController, controllerName);
|
||||
span.SetTag(Tags.AspNetAction, actionName);
|
||||
|
|
@ -126,7 +128,7 @@ namespace Datadog.Trace.ClrProfiler.Integrations
|
|||
[InterceptMethod(
|
||||
CallerAssembly = AspnetMvcCore,
|
||||
TargetAssembly = AspnetMvcCore,
|
||||
TargetType = DiagnosticSource,
|
||||
TargetType = DiagnosticSourceTypeName,
|
||||
TargetSignatureTypes = new[] { ClrNames.Void, ClrNames.Ignore, "Microsoft.AspNetCore.Mvc.Abstractions.ActionDescriptor", "Microsoft.AspNetCore.Http.HttpContext", "Microsoft.AspNetCore.Routing.RouteData" },
|
||||
TargetMinimumVersion = Major2,
|
||||
TargetMaximumVersion = Major2)]
|
||||
|
|
@ -168,7 +170,7 @@ namespace Datadog.Trace.ClrProfiler.Integrations
|
|||
instrumentedMethod =
|
||||
MethodBuilder<Action<object, object, object, object>>
|
||||
.Start(moduleVersionPtr, mdToken, opCode, nameof(BeforeAction))
|
||||
.WithConcreteTypeName(DiagnosticSource)
|
||||
.WithConcreteType(DiagnosticSourceType)
|
||||
.WithParameters(diagnosticSource, actionDescriptor, httpContext, routeData)
|
||||
.WithNamespaceAndNameFilters(
|
||||
ClrNames.Void,
|
||||
|
|
@ -181,7 +183,7 @@ namespace Datadog.Trace.ClrProfiler.Integrations
|
|||
catch (Exception ex)
|
||||
{
|
||||
// profiled app will continue working as expected without this method
|
||||
Log.ErrorException($"Error resolving {DiagnosticSource}.{nameof(BeforeAction)}(...)", ex);
|
||||
Log.ErrorException($"Error resolving {DiagnosticSourceTypeName}.{nameof(BeforeAction)}(...)", ex);
|
||||
}
|
||||
|
||||
try
|
||||
|
|
@ -209,7 +211,7 @@ namespace Datadog.Trace.ClrProfiler.Integrations
|
|||
[InterceptMethod(
|
||||
CallerAssembly = AspnetMvcCore,
|
||||
TargetAssembly = AspnetMvcCore,
|
||||
TargetType = DiagnosticSource,
|
||||
TargetType = DiagnosticSourceTypeName,
|
||||
TargetSignatureTypes = new[] { ClrNames.Void, ClrNames.Ignore, "Microsoft.AspNetCore.Mvc.Abstractions.ActionDescriptor", "Microsoft.AspNetCore.Http.HttpContext", "Microsoft.AspNetCore.Routing.RouteData" },
|
||||
TargetMinimumVersion = Major2,
|
||||
TargetMaximumVersion = Major2)]
|
||||
|
|
@ -244,14 +246,14 @@ namespace Datadog.Trace.ClrProfiler.Integrations
|
|||
|
||||
Action<object, object, object, object> instrumentedMethod = null;
|
||||
|
||||
string methodDef = $"{DiagnosticSource}.{nameof(AfterAction)}(...)";
|
||||
string methodDef = $"{DiagnosticSourceTypeName}.{nameof(AfterAction)}(...)";
|
||||
|
||||
try
|
||||
{
|
||||
instrumentedMethod =
|
||||
MethodBuilder<Action<object, object, object, object>>
|
||||
.Start(moduleVersionPtr, mdToken, opCode, nameof(AfterAction))
|
||||
.WithConcreteTypeName(DiagnosticSource)
|
||||
.WithConcreteType(DiagnosticSourceType)
|
||||
.WithParameters(diagnosticSource, actionDescriptor, httpContext, routeData)
|
||||
.WithNamespaceAndNameFilters(
|
||||
ClrNames.Void,
|
||||
|
|
@ -294,7 +296,7 @@ namespace Datadog.Trace.ClrProfiler.Integrations
|
|||
[InterceptMethod(
|
||||
CallerAssembly = AspnetMvcCore,
|
||||
TargetAssembly = AspnetMvcCore,
|
||||
TargetType = ResourceInvoker,
|
||||
TargetType = ResourceInvokerTypeName,
|
||||
TargetSignatureTypes = new[] { ClrNames.Void, ClrNames.Ignore },
|
||||
TargetMinimumVersion = Major2,
|
||||
TargetMaximumVersion = Major2)]
|
||||
|
|
@ -310,14 +312,14 @@ namespace Datadog.Trace.ClrProfiler.Integrations
|
|||
|
||||
var shouldTrace = Tracer.Instance.Settings.IsIntegrationEnabled(IntegrationName);
|
||||
|
||||
Action<object> instrumentedMethod = null;
|
||||
Action<object> instrumentedMethod;
|
||||
|
||||
try
|
||||
{
|
||||
instrumentedMethod =
|
||||
MethodBuilder<Action<object>>
|
||||
.Start(moduleVersionPtr, mdToken, opCode, nameof(Rethrow))
|
||||
.WithConcreteTypeName(ResourceInvoker)
|
||||
.WithConcreteType(ResourceInvokerType)
|
||||
.WithParameters(context)
|
||||
.WithNamespaceAndNameFilters(ClrNames.Void, ClrNames.Ignore)
|
||||
.Build();
|
||||
|
|
@ -325,8 +327,8 @@ namespace Datadog.Trace.ClrProfiler.Integrations
|
|||
catch (Exception ex)
|
||||
{
|
||||
// profiled app will not continue working as expected without this method
|
||||
var contextTypeName = (context == null) ? string.Empty : (context.GetType().FullName + " ");
|
||||
var methodDef = $"{ResourceInvoker}.{nameof(Rethrow)}({contextTypeName}context)";
|
||||
var contextTypeName = context.GetType().FullName + " ";
|
||||
var methodDef = $"{ResourceInvokerTypeName}.{nameof(Rethrow)}({contextTypeName} context)";
|
||||
Log.ErrorException($"Error retrieving {methodDef}", ex);
|
||||
throw;
|
||||
}
|
||||
|
|
@ -394,14 +396,14 @@ namespace Datadog.Trace.ClrProfiler.Integrations
|
|||
}
|
||||
|
||||
private static void GetTagValues(
|
||||
object actionDescriptor,
|
||||
object request,
|
||||
out string httpMethod,
|
||||
out string host,
|
||||
out string resourceName,
|
||||
out string url,
|
||||
out string controllerName,
|
||||
out string actionName)
|
||||
object actionDescriptor,
|
||||
object request,
|
||||
out string httpMethod,
|
||||
out string host,
|
||||
out string resourceName,
|
||||
out string url,
|
||||
out string controllerName,
|
||||
out string actionName)
|
||||
{
|
||||
controllerName = actionDescriptor.GetProperty<string>("ControllerName").GetValueOrDefault()?.ToLowerInvariant();
|
||||
|
||||
|
|
@ -420,7 +422,7 @@ namespace Datadog.Trace.ClrProfiler.Integrations
|
|||
url = $"{pathBase}{path}{queryString}";
|
||||
|
||||
string resourceUrl = actionDescriptor.GetProperty("AttributeRouteInfo").GetProperty<string>("Template").GetValueOrDefault() ??
|
||||
UriHelpers.GetRelativeUrl(new Uri($"https://{host}{url}"), tryRemoveIds: true).ToLowerInvariant();
|
||||
UriHelpers.GetRelativeUrl(new Uri($"https://{host}{url}"), tryRemoveIds: true).ToLowerInvariant();
|
||||
|
||||
resourceName = $"{httpMethod} {resourceUrl}";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,11 +23,11 @@ namespace Datadog.Trace.ClrProfiler.Integrations
|
|||
private const string Major5Minor1 = "5.1";
|
||||
private const string Major5 = "5";
|
||||
private const string AssemblyName = "System.Web.Mvc";
|
||||
private const string AsyncActionInvokerTypeName = "System.Web.Mvc.Async.IAsyncActionInvoker";
|
||||
|
||||
private static readonly Type ControllerContextType = Type.GetType($"System.Web.Mvc.ControllerContext, {AssemblyName}", throwOnError: false);
|
||||
private static readonly Type RouteCollectionRouteType = Type.GetType($"System.Web.Mvc.Routing.RouteCollectionRoute, {AssemblyName}", throwOnError: false);
|
||||
private static readonly Type AsyncActionInvokerType = Type.GetType($"{AsyncActionInvokerTypeName}, {AssemblyName}", throwOnError: false);
|
||||
private const string AsyncActionInvokerTypeName = "System.Web.Mvc.Async.IAsyncActionInvoker";
|
||||
private const string ControllerContextTypeName = "System.Web.Mvc.ControllerContext";
|
||||
private const string RouteCollectionRouteTypeName = "System.Web.Mvc.Routing.RouteCollectionRoute";
|
||||
|
||||
private static readonly ILog Log = LogProvider.GetLogger(typeof(AspNetMvcIntegration));
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -35,16 +35,8 @@ namespace Datadog.Trace.ClrProfiler.Integrations
|
|||
/// </summary>
|
||||
/// <param name="controllerContext">The System.Web.Mvc.ControllerContext that was passed as an argument to the instrumented method.</param>
|
||||
/// <returns>A new scope used to instrument an MVC action.</returns>
|
||||
public static Scope CreateScope(dynamic controllerContext)
|
||||
public static Scope CreateScope(object controllerContext)
|
||||
{
|
||||
if (ControllerContextType == null ||
|
||||
controllerContext == null ||
|
||||
((object)controllerContext)?.GetType() != ControllerContextType)
|
||||
{
|
||||
// bail out early
|
||||
return null;
|
||||
}
|
||||
|
||||
Scope scope = null;
|
||||
|
||||
try
|
||||
|
|
@ -55,7 +47,12 @@ namespace Datadog.Trace.ClrProfiler.Integrations
|
|||
return null;
|
||||
}
|
||||
|
||||
var httpContext = controllerContext?.HttpContext as HttpContextBase;
|
||||
if (controllerContext == null || controllerContext.GetType().FullName != ControllerContextTypeName)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var httpContext = controllerContext.GetProperty<HttpContextBase>("HttpContext").GetValueOrDefault();
|
||||
|
||||
if (httpContext == null)
|
||||
{
|
||||
|
|
@ -67,11 +64,11 @@ namespace Datadog.Trace.ClrProfiler.Integrations
|
|||
string url = httpContext.Request.RawUrl.ToLowerInvariant();
|
||||
string resourceName = null;
|
||||
|
||||
RouteData routeData = controllerContext.RouteData as RouteData;
|
||||
RouteData routeData = controllerContext.GetProperty<RouteData>("RouteData").GetValueOrDefault();
|
||||
Route route = routeData?.Route as Route;
|
||||
RouteValueDictionary routeValues = routeData?.Values;
|
||||
|
||||
if (route == null && routeData?.Route.GetType() == RouteCollectionRouteType)
|
||||
if (route == null && routeData?.Route.GetType().FullName == RouteCollectionRouteTypeName)
|
||||
{
|
||||
var routeMatches = routeValues?.GetValueOrDefault("MS_DirectRouteMatches") as List<RouteData>;
|
||||
|
||||
|
|
@ -173,7 +170,7 @@ namespace Datadog.Trace.ClrProfiler.Integrations
|
|||
CallerAssembly = AssemblyName,
|
||||
TargetAssembly = AssemblyName,
|
||||
TargetType = AsyncActionInvokerTypeName,
|
||||
TargetSignatureTypes = new[] { ClrNames.IAsyncResult, "System.Web.Mvc.ControllerContext", ClrNames.String, ClrNames.AsyncCallback, ClrNames.Object },
|
||||
TargetSignatureTypes = new[] { ClrNames.IAsyncResult, ControllerContextTypeName, ClrNames.String, ClrNames.AsyncCallback, ClrNames.Object },
|
||||
TargetMinimumVersion = Major5Minor1,
|
||||
TargetMaximumVersion = Major5)]
|
||||
public static object BeginInvokeAction(
|
||||
|
|
@ -201,20 +198,38 @@ namespace Datadog.Trace.ClrProfiler.Integrations
|
|||
Log.ErrorException("Error instrumenting method {0}", ex, "System.Web.Mvc.Async.IAsyncActionInvoker.BeginInvokeAction()");
|
||||
}
|
||||
|
||||
var beginInvokeAction = Emit.DynamicMethodBuilder<Func<object, object, object, object, object, object>>
|
||||
.GetOrCreateMethodCallDelegate(
|
||||
AsyncActionInvokerType,
|
||||
"BeginInvokeAction",
|
||||
(OpCodeValue)opCode);
|
||||
Func<object, object, object, object, object, object> instrumentedMethod;
|
||||
|
||||
try
|
||||
{
|
||||
var asyncActionInvokerType = asyncControllerActionInvoker.GetInstrumentedInterface(AsyncActionInvokerTypeName);
|
||||
|
||||
instrumentedMethod = MethodBuilder<Func<object, object, object, object, object, object>>
|
||||
.Start(moduleVersionPtr, mdToken, opCode, nameof(BeginInvokeAction))
|
||||
.WithConcreteType(asyncActionInvokerType)
|
||||
.WithParameters(controllerContext, actionName, callback, state)
|
||||
.WithNamespaceAndNameFilters(
|
||||
ClrNames.IAsyncResult,
|
||||
"System.Web.Mvc.ControllerContext",
|
||||
ClrNames.String,
|
||||
ClrNames.AsyncCallback,
|
||||
ClrNames.Object)
|
||||
.Build();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.ErrorException($"Error resolving {AsyncActionInvokerTypeName}.{nameof(BeginInvokeAction)}(...)", ex);
|
||||
throw;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// call the original method, inspecting (but not catching) any unhandled exceptions
|
||||
return beginInvokeAction(asyncControllerActionInvoker, controllerContext, actionName, callback, state);
|
||||
return instrumentedMethod(asyncControllerActionInvoker, controllerContext, actionName, callback, state);
|
||||
}
|
||||
catch (Exception ex) when (scope?.Span.SetExceptionForFilter(ex) ?? false)
|
||||
catch (Exception ex)
|
||||
{
|
||||
// unreachable code
|
||||
scope?.Span.SetException(ex);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
|
@ -254,20 +269,33 @@ namespace Datadog.Trace.ClrProfiler.Integrations
|
|||
Log.ErrorException("Error instrumenting method {0}", ex, $"{AsyncActionInvokerTypeName}.EndInvokeAction()");
|
||||
}
|
||||
|
||||
var endInvokeAction = Emit.DynamicMethodBuilder<Func<object, object, bool>>
|
||||
.GetOrCreateMethodCallDelegate(
|
||||
AsyncActionInvokerType,
|
||||
"EndInvokeAction",
|
||||
(OpCodeValue)opCode);
|
||||
Func<object, object, bool> instrumentedMethod;
|
||||
|
||||
try
|
||||
{
|
||||
var asyncActionInvokerType = asyncControllerActionInvoker.GetInstrumentedInterface(AsyncActionInvokerTypeName);
|
||||
|
||||
instrumentedMethod = MethodBuilder<Func<object, object, bool>>
|
||||
.Start(moduleVersionPtr, mdToken, opCode, nameof(EndInvokeAction))
|
||||
.WithConcreteType(asyncActionInvokerType)
|
||||
.WithParameters(asyncResult)
|
||||
.WithNamespaceAndNameFilters(ClrNames.Bool, ClrNames.IAsyncResult)
|
||||
.Build();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.ErrorException($"Error resolving {AsyncActionInvokerTypeName}.{nameof(EndInvokeAction)}(...)", ex);
|
||||
throw;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// call the original method, inspecting (but not catching) any unhandled exceptions
|
||||
return endInvokeAction(asyncControllerActionInvoker, asyncResult);
|
||||
return instrumentedMethod(asyncControllerActionInvoker, asyncResult);
|
||||
}
|
||||
catch (Exception ex) when (scope?.Span.SetExceptionForFilter(ex) ?? false)
|
||||
catch (Exception ex)
|
||||
{
|
||||
// unreachable code
|
||||
scope?.Span.SetException(ex);
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ using Datadog.Trace.Logging;
|
|||
namespace Datadog.Trace.ClrProfiler.Integrations
|
||||
{
|
||||
/// <summary>
|
||||
/// AspNetWeb5Integration wraps the Web API.
|
||||
/// Contains instrumentation wrappers for ASP.NET Web API 5.
|
||||
/// </summary>
|
||||
public static class AspNetWebApi2Integration
|
||||
{
|
||||
|
|
@ -22,6 +22,10 @@ namespace Datadog.Trace.ClrProfiler.Integrations
|
|||
private const string Major5Minor2 = "5.2";
|
||||
private const string Major5 = "5";
|
||||
|
||||
private const string SystemWebHttpAssemblyName = "System.Web.Http";
|
||||
private const string HttpControllerTypeName = "System.Web.Http.Controllers.IHttpController";
|
||||
private const string HttpControllerContextTypeName = "System.Web.Http.Controllers.HttpControllerContext";
|
||||
|
||||
private static readonly ILog Log = LogProvider.GetLogger(typeof(AspNetWebApi2Integration));
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -35,9 +39,9 @@ namespace Datadog.Trace.ClrProfiler.Integrations
|
|||
/// <param name="moduleVersionPtr">A pointer to the module version GUID.</param>
|
||||
/// <returns>A task with the result</returns>
|
||||
[InterceptMethod(
|
||||
TargetAssembly = "System.Web.Http",
|
||||
TargetType = "System.Web.Http.Controllers.IHttpController",
|
||||
TargetSignatureTypes = new[] { ClrNames.HttpResponseMessageTask, "System.Web.Http.Controllers.HttpControllerContext", ClrNames.CancellationToken },
|
||||
TargetAssembly = SystemWebHttpAssemblyName,
|
||||
TargetType = HttpControllerTypeName,
|
||||
TargetSignatureTypes = new[] { ClrNames.HttpResponseMessageTask, HttpControllerContextTypeName, ClrNames.CancellationToken },
|
||||
TargetMinimumVersion = Major5Minor2,
|
||||
TargetMaximumVersion = Major5)]
|
||||
public static object ExecuteAsync(
|
||||
|
|
@ -52,8 +56,7 @@ namespace Datadog.Trace.ClrProfiler.Integrations
|
|||
|
||||
var tokenSource = cancellationTokenSource as CancellationTokenSource;
|
||||
var cancellationToken = tokenSource?.Token ?? CancellationToken.None;
|
||||
var callOpCode = (OpCodeValue)opCode;
|
||||
return ExecuteAsyncInternal(apiController, controllerContext, cancellationToken, callOpCode);
|
||||
return ExecuteAsyncInternal(apiController, controllerContext, cancellationToken, opCode, mdToken, moduleVersionPtr);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -62,26 +65,46 @@ namespace Datadog.Trace.ClrProfiler.Integrations
|
|||
/// <param name="apiController">The Api Controller</param>
|
||||
/// <param name="controllerContext">The controller context for the call</param>
|
||||
/// <param name="cancellationToken">The cancellation token</param>
|
||||
/// <param name="callOpCode">The <see cref="OpCodeValue"/> used in the original method call.</param>
|
||||
/// <param name="opCode">The OpCode used in the original method call.</param>
|
||||
/// <param name="mdToken">The mdToken of the original method call.</param>
|
||||
/// <param name="moduleVersionPtr">A pointer to the module version GUID.</param>
|
||||
/// <returns>A task with the result</returns>
|
||||
private static async Task<HttpResponseMessage> ExecuteAsyncInternal(object apiController, object controllerContext, CancellationToken cancellationToken, OpCodeValue callOpCode)
|
||||
private static async Task<HttpResponseMessage> ExecuteAsyncInternal(
|
||||
object apiController,
|
||||
object controllerContext,
|
||||
CancellationToken cancellationToken,
|
||||
int opCode,
|
||||
int mdToken,
|
||||
long moduleVersionPtr)
|
||||
{
|
||||
Type controllerType = apiController.GetType();
|
||||
Func<object, object, CancellationToken, Task<HttpResponseMessage>> instrumentedMethod;
|
||||
|
||||
// in some cases, ExecuteAsync() is an explicit interface implementation,
|
||||
// which is not public and has a different name, so try both
|
||||
var executeAsyncFunc =
|
||||
Emit.DynamicMethodBuilder<Func<object, object, CancellationToken, Task<HttpResponseMessage>>>
|
||||
.GetOrCreateMethodCallDelegate(controllerType, "ExecuteAsync", callOpCode) ??
|
||||
Emit.DynamicMethodBuilder<Func<object, object, CancellationToken, Task<HttpResponseMessage>>>
|
||||
.GetOrCreateMethodCallDelegate(controllerType, "System.Web.Http.Controllers.IHttpController.ExecuteAsync", callOpCode);
|
||||
try
|
||||
{
|
||||
var httpControllerType = apiController.GetInstrumentedInterface(HttpControllerTypeName);
|
||||
|
||||
instrumentedMethod = MethodBuilder<Func<object, object, CancellationToken, Task<HttpResponseMessage>>>
|
||||
.Start(moduleVersionPtr, mdToken, opCode, nameof(ExecuteAsync))
|
||||
.WithConcreteType(httpControllerType)
|
||||
.WithParameters(controllerContext, cancellationToken)
|
||||
.WithNamespaceAndNameFilters(
|
||||
ClrNames.HttpResponseMessageTask,
|
||||
HttpControllerContextTypeName,
|
||||
ClrNames.CancellationToken)
|
||||
.Build();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.ErrorException($"Error resolving {HttpControllerTypeName}.{nameof(ExecuteAsync)}(...)", ex);
|
||||
throw;
|
||||
}
|
||||
|
||||
using (Scope scope = CreateScope(controllerContext))
|
||||
{
|
||||
try
|
||||
{
|
||||
// call the original method, inspecting (but not catching) any unhandled exceptions
|
||||
var responseMessage = await executeAsyncFunc(apiController, controllerContext, cancellationToken).ConfigureAwait(false);
|
||||
var responseMessage = await instrumentedMethod(apiController, controllerContext, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (scope != null)
|
||||
{
|
||||
|
|
@ -91,15 +114,15 @@ namespace Datadog.Trace.ClrProfiler.Integrations
|
|||
|
||||
return responseMessage;
|
||||
}
|
||||
catch (Exception ex) when (scope?.Span.SetExceptionForFilter(ex) ?? false)
|
||||
catch (Exception ex)
|
||||
{
|
||||
// unreachable code
|
||||
scope?.Span.SetException(ex);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Scope CreateScope(dynamic controllerContext)
|
||||
private static Scope CreateScope(object controllerContext)
|
||||
{
|
||||
Scope scope = null;
|
||||
|
||||
|
|
@ -112,7 +135,7 @@ namespace Datadog.Trace.ClrProfiler.Integrations
|
|||
}
|
||||
|
||||
var tracer = Tracer.Instance;
|
||||
var request = controllerContext?.Request as HttpRequestMessage;
|
||||
var request = controllerContext.GetProperty<HttpRequestMessage>("Request").GetValueOrDefault();
|
||||
SpanContext propagatedContext = null;
|
||||
|
||||
if (request != null && tracer.ActiveScope == null)
|
||||
|
|
|
|||
|
|
@ -25,5 +25,28 @@ namespace Datadog.Trace.ClrProfiler
|
|||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static System.Type GetInstrumentedInterface(
|
||||
this object runtimeObject,
|
||||
string instrumentedInterfaceName)
|
||||
{
|
||||
if (runtimeObject == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var currentType = runtimeObject.GetType();
|
||||
var interfaces = currentType.GetInterfaces();
|
||||
|
||||
foreach (var interfaceType in interfaces)
|
||||
{
|
||||
if ($"{interfaceType.Namespace}.{interfaceType.Name}" == instrumentedInterfaceName)
|
||||
{
|
||||
return interfaceType;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
#if !NETSTANDARD2_0
|
||||
|
||||
using System;
|
||||
using System.ServiceModel.Channels;
|
||||
using Datadog.Trace.ClrProfiler.Emit;
|
||||
using Datadog.Trace.ClrProfiler.ExtensionMethods;
|
||||
using Datadog.Trace.ClrProfiler.Models;
|
||||
using Datadog.Trace.Logging;
|
||||
|
||||
namespace Datadog.Trace.ClrProfiler.Integrations
|
||||
{
|
||||
|
|
@ -16,10 +16,14 @@ namespace Datadog.Trace.ClrProfiler.Integrations
|
|||
private const string IntegrationName = "Wcf";
|
||||
private const string Major4 = "4";
|
||||
|
||||
private const string ChannelHandlerTypeName = "System.ServiceModel.Dispatcher.ChannelHandler";
|
||||
|
||||
private static readonly ILog Log = LogProvider.GetLogger(typeof(WcfIntegration));
|
||||
|
||||
/// <summary>
|
||||
/// Instrumentation wrapper for System.ServiceModel.Dispatcher.ChannelHandler
|
||||
/// </summary>
|
||||
/// <param name="thisObj">The ChannelHandler instance.</param>
|
||||
/// <param name="channelHandler">The ChannelHandler instance.</param>
|
||||
/// <param name="requestContext">A System.ServiceModel.Channels.RequestContext implementation instance.</param>
|
||||
/// <param name="currentOperationContext">A System.ServiceModel.OperationContext instance.</param>
|
||||
/// <param name="opCode">The OpCode used in the original method call.</param>
|
||||
|
|
@ -28,39 +32,54 @@ namespace Datadog.Trace.ClrProfiler.Integrations
|
|||
/// <returns>The value returned by the instrumented method.</returns>
|
||||
[InterceptMethod(
|
||||
TargetAssembly = "System.ServiceModel",
|
||||
TargetType = "System.ServiceModel.Dispatcher.ChannelHandler",
|
||||
TargetType = ChannelHandlerTypeName,
|
||||
TargetSignatureTypes = new[] { ClrNames.Bool, "System.ServiceModel.Channels.RequestContext", "System.ServiceModel.OperationContext" },
|
||||
TargetMinimumVersion = Major4,
|
||||
TargetMaximumVersion = Major4)]
|
||||
public static bool HandleRequest(
|
||||
object thisObj,
|
||||
object channelHandler,
|
||||
object requestContext,
|
||||
object currentOperationContext,
|
||||
int opCode,
|
||||
int mdToken,
|
||||
long moduleVersionPtr)
|
||||
{
|
||||
var handleRequestDelegate = Emit.DynamicMethodBuilder<Func<object, object, object, bool>>
|
||||
.GetOrCreateMethodCallDelegate(
|
||||
thisObj.GetType(),
|
||||
"HandleRequest",
|
||||
(OpCodeValue)opCode);
|
||||
Func<object, object, object, bool> instrumentedMethod;
|
||||
var declaringType = channelHandler.GetInstrumentedType(ChannelHandlerTypeName);
|
||||
|
||||
try
|
||||
{
|
||||
instrumentedMethod = MethodBuilder<Func<object, object, object, bool>>
|
||||
.Start(moduleVersionPtr, mdToken, opCode, nameof(HandleRequest))
|
||||
.WithConcreteType(declaringType)
|
||||
.WithParameters(requestContext, currentOperationContext)
|
||||
.WithNamespaceAndNameFilters(
|
||||
ClrNames.Bool,
|
||||
"System.ServiceModel.Channels.RequestContext",
|
||||
"System.ServiceModel.OperationContext")
|
||||
.Build();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.ErrorException($"Error resolving {ChannelHandlerTypeName}.{nameof(HandleRequest)}(...)", ex);
|
||||
throw;
|
||||
}
|
||||
|
||||
if (!Tracer.Instance.Settings.IsIntegrationEnabled(IntegrationName) ||
|
||||
!(requestContext is RequestContext castRequestContext))
|
||||
{
|
||||
return handleRequestDelegate(thisObj, requestContext, currentOperationContext);
|
||||
return instrumentedMethod(channelHandler, requestContext, currentOperationContext);
|
||||
}
|
||||
|
||||
using (var wcfDelegate = WcfRequestMessageSpanIntegrationDelegate.CreateAndBegin(castRequestContext))
|
||||
{
|
||||
try
|
||||
{
|
||||
return handleRequestDelegate(thisObj, requestContext, currentOperationContext);
|
||||
return instrumentedMethod(channelHandler, requestContext, currentOperationContext);
|
||||
}
|
||||
catch (Exception ex) when (wcfDelegate?.SetExceptionForFilter(ex) ?? false)
|
||||
catch (Exception ex)
|
||||
{
|
||||
// unreachable code
|
||||
wcfDelegate?.SetExceptionForFilter(ex);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue