Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -873,7 +873,7 @@ HRESULT CallTargetTokens::ModifyLocalSigAndInitialize(void* rewriterWrapperPtr,

HRESULT CallTargetTokens::WriteBeginMethod(void* rewriterWrapperPtr, mdTypeRef integrationTypeRef,
const TypeInfo* currentType,
std::vector<FunctionMethodArgument>& methodArguments, ILInstr** instruction)
const std::vector<FunctionMethodArgument>& methodArguments, ILInstr** instruction)
{
auto hr = EnsureBaseCalltargetTokens();
if (FAILED(hr))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ class CallTargetTokens
mdToken* callTargetReturnToken, ILInstr** firstInstruction);

HRESULT WriteBeginMethod(void* rewriterWrapperPtr, mdTypeRef integrationTypeRef, const TypeInfo* currentType,
std::vector<FunctionMethodArgument>& methodArguments, ILInstr** instruction);
const std::vector<FunctionMethodArgument>& methodArguments, ILInstr** instruction);

HRESULT WriteEndVoidReturnMemberRef(void* rewriterWrapperPtr, mdTypeRef integrationTypeRef,
const TypeInfo* currentType, ILInstr** instruction);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// <copyright file="AsyncMethodInvoker_InvokeBegin_Integration.cs" company="Datadog">
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License.
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc.
// </copyright>

#if NETFRAMEWORK
using System;
using System.ComponentModel;
using System.Reflection;
using Datadog.Trace.ClrProfiler.CallTarget;
using Datadog.Trace.ClrProfiler.Emit;
using Datadog.Trace.Configuration;

namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Wcf
{
/// <summary>
/// System.ServiceModel.Dispatcher.AsyncMethodInvoker calltarget instrumentation
/// </summary>
[InstrumentMethod(
AssemblyName = "System.ServiceModel",
TypeName = "System.ServiceModel.Dispatcher.AsyncMethodInvoker",
MethodName = "InvokeBegin",
ReturnTypeName = ClrNames.IAsyncResult,
ParameterTypeNames = new[] { ClrNames.Object, "System.Object[]", ClrNames.AsyncCallback, ClrNames.Object },
MinimumVersion = "4.0.0",
MaximumVersion = "4.*.*",
IntegrationName = WcfCommon.IntegrationName)]
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public class AsyncMethodInvoker_InvokeBegin_Integration
{
/// <summary>
/// OnMethodBegin callback
/// </summary>
/// <typeparam name="TTarget">Type of the target</typeparam>
/// <param name="instance">Instance value, aka `this` of the instrumented method.</param>
/// <param name="instanceArg">RequestContext instance</param>
/// <param name="inputs">Input arguments</param>
/// <param name="callback">Callback argument</param>
/// <param name="state">State argument</param>
/// <returns>Calltarget state value</returns>
public static CallTargetState OnMethodBegin<TTarget>(TTarget instance, object instanceArg, object[] inputs, AsyncCallback callback, object state)
{
// TODO Just use the OperationContext.Current object to get the span information
// context.IncomingMessageHeaders contains:
// - Action
// - To
//
// context.IncomingMessageProperties contains:
// - ["httpRequest"] key to find distributed tracing headers
if (!Tracer.Instance.Settings.IsIntegrationEnabled(WcfCommon.IntegrationId) || !Tracer.Instance.Settings.DelayWcfInstrumentationEnabled || WcfCommon.GetCurrentOperationContext is null)
{
return CallTargetState.GetDefault();
}

var requestContext = WcfCommon.GetCurrentOperationContext()?.GetProperty<object>("RequestContext").GetValueOrDefault();
return new CallTargetState(WcfCommon.CreateScope(requestContext));
}
}
}
#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// <copyright file="AsyncMethodInvoker_InvokeEnd_Integration.cs" company="Datadog">
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License.
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc.
// </copyright>

#if NETFRAMEWORK
using System;
using System.ComponentModel;
using System.Reflection;
using Datadog.Trace.ClrProfiler.CallTarget;
using Datadog.Trace.Configuration;

namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Wcf
{
/// <summary>
/// System.ServiceModel.Dispatcher.AsyncMethodInvoker calltarget instrumentation
/// </summary>
[InstrumentMethod(
AssemblyName = "System.ServiceModel",
TypeName = "System.ServiceModel.Dispatcher.AsyncMethodInvoker",
MethodName = "InvokeEnd",
ReturnTypeName = ClrNames.Object,
ParameterTypeNames = new[] { ClrNames.Object, "System.Object[]&", ClrNames.IAsyncResult },
MinimumVersion = "4.0.0",
MaximumVersion = "4.*.*",
IntegrationName = WcfCommon.IntegrationName)]
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public class AsyncMethodInvoker_InvokeEnd_Integration
{
/// <summary>
/// OnMethodEnd callback
/// </summary>
/// <typeparam name="TTarget">Type of the target</typeparam>
/// <typeparam name="TReturn">Type of the response</typeparam>
/// <param name="instance">Instance value, aka `this` of the instrumented method.</param>
/// <param name="returnValue">Return value</param>
/// <param name="exception">Exception instance in case the original code threw an exception.</param>
/// <param name="state">Calltarget state value</param>
/// <returns>A response value, in an async scenario will be T of Task of T</returns>
public static CallTargetReturn<TReturn> OnMethodEnd<TTarget, TReturn>(TTarget instance, TReturn returnValue, Exception exception, CallTargetState state)
{
var scope = Tracer.Instance.ActiveScope;
scope.DisposeWithException(exception);
return new CallTargetReturn<TReturn>(returnValue);
}
}
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,6 @@ namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Wcf
public class ChannelHandlerIntegration
{
private const string IntegrationName = nameof(Configuration.IntegrationId.Wcf);
private const string ChannelHandlerTypeName = "System.ServiceModel.Dispatcher.ChannelHandler";
private const string HttpRequestMessagePropertyTypeName = "System.ServiceModel.Channels.HttpRequestMessageProperty";

private const IntegrationId IntegrationId = Configuration.IntegrationId.Wcf;
private static readonly IDatadogLogger Log = DatadogLogging.GetLoggerFor(typeof(ChannelHandlerIntegration));

/// <summary>
/// OnMethodBegin callback
Expand All @@ -53,7 +48,12 @@ public class ChannelHandlerIntegration
/// <returns>Calltarget state value</returns>
public static CallTargetState OnMethodBegin<TTarget, TRequestContext, TOperationContext>(TTarget instance, TRequestContext request, TOperationContext currentOperationContext)
{
return new CallTargetState(CreateScope(request));
if (Tracer.Instance.Settings.DelayWcfInstrumentationEnabled)
{
return CallTargetState.GetDefault();
}

return new CallTargetState(WcfCommon.CreateScope(request));
}

/// <summary>
Expand All @@ -71,85 +71,6 @@ public static CallTargetReturn<TReturn> OnMethodEnd<TTarget, TReturn>(TTarget in
state.Scope.DisposeWithException(exception);
return new CallTargetReturn<TReturn>(returnValue);
}

private static Scope CreateScope(object requestContext)
{
var requestMessage = requestContext.GetProperty<object>("RequestMessage").GetValueOrDefault();

if (requestMessage == null)
{
return null;
}

var tracer = Tracer.Instance;

if (!tracer.Settings.IsIntegrationEnabled(IntegrationId))
{
// integration disabled, don't create a scope, skip this trace
return null;
}

Scope scope = null;

try
{
SpanContext propagatedContext = null;
var tagsFromHeaders = Enumerable.Empty<KeyValuePair<string, string>>();
string host = null;
string httpMethod = null;

IDictionary<string, object> requestProperties = requestMessage.GetProperty<IDictionary<string, object>>("Properties").GetValueOrDefault();
if (requestProperties.TryGetValue("httpRequest", out object httpRequestProperty) &&
httpRequestProperty.GetType().FullName.Equals(HttpRequestMessagePropertyTypeName, StringComparison.OrdinalIgnoreCase))
{
var webHeaderCollection = httpRequestProperty.GetProperty<WebHeaderCollection>("Headers").GetValueOrDefault();

// we're using an http transport
host = webHeaderCollection[HttpRequestHeader.Host];
httpMethod = httpRequestProperty.GetProperty<string>("Method").GetValueOrDefault()?.ToUpperInvariant();

// try to extract propagated context values from http headers
if (tracer.ActiveScope == null)
{
try
{
var headers = webHeaderCollection.Wrap();
propagatedContext = SpanContextPropagator.Instance.Extract(headers);
tagsFromHeaders = SpanContextPropagator.Instance.ExtractHeaderTags(headers, tracer.Settings.HeaderTags, SpanContextPropagator.HttpRequestHeadersTagPrefix);
}
catch (Exception ex)
{
Log.Error(ex, "Error extracting propagated HTTP headers.");
}
}
}

var tags = new WebTags();
scope = tracer.StartActiveWithTags("wcf.request", propagatedContext, tags: tags);
var span = scope.Span;

object requestHeaders = requestMessage.GetProperty<object>("Headers").GetValueOrDefault();
string action = requestHeaders.GetProperty<string>("Action").GetValueOrDefault();
Uri requestHeadersTo = requestHeaders.GetProperty<Uri>("To").GetValueOrDefault();

span.DecorateWebServerSpan(
resourceName: action ?? requestHeadersTo?.LocalPath,
httpMethod,
host,
httpUrl: requestHeadersTo?.AbsoluteUri,
tags,
tagsFromHeaders);

tags.SetAnalyticsSampleRate(IntegrationId, tracer.Settings, enabledWithGlobalSetting: true);
}
catch (Exception ex)
{
Log.Error(ex, "Error creating or populating scope.");
}

// always returns the scope, even if it's null
return scope;
}
}
}
#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// <copyright file="SyncMethodInvokerIntegration.cs" company="Datadog">
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License.
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc.
// </copyright>

#if NETFRAMEWORK
using System;
using System.ComponentModel;
using System.Reflection;
using Datadog.Trace.ClrProfiler.CallTarget;
using Datadog.Trace.ClrProfiler.Emit;
using Datadog.Trace.Configuration;

namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Wcf
{
/// <summary>
/// System.ServiceModel.Dispatcher.SyncMethodInvoker calltarget instrumentation
/// </summary>
[InstrumentMethod(
AssemblyName = "System.ServiceModel",
TypeName = "System.ServiceModel.Dispatcher.SyncMethodInvoker",
MethodName = "Invoke",
ReturnTypeName = ClrNames.Object,
ParameterTypeNames = new[] { ClrNames.Object, "System.Object[]", "System.Object[]&" },
MinimumVersion = "4.0.0",
MaximumVersion = "4.*.*",
IntegrationName = WcfCommon.IntegrationName)]
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public class SyncMethodInvokerIntegration
{
/// <summary>
/// OnMethodBegin callback
/// </summary>
/// <typeparam name="TTarget">Type of the target</typeparam>
/// <param name="instance">Instance value, aka `this` of the instrumented method.</param>
/// <param name="instanceArg">RequestContext instance</param>
/// <param name="inputs">Input arguments</param>
/// <param name="outputs">Output arguments</param>
/// <returns>Calltarget state value</returns>
public static CallTargetState OnMethodBegin<TTarget>(TTarget instance, object instanceArg, object[] inputs, ref object[] outputs)
{
// TODO Just use the OperationContext.Current object to get the span information
// context.IncomingMessageHeaders contains:
// - Action
// - To
//
// context.IncomingMessageProperties contains:
// - ["httpRequest"] key to find distributed tracing headers
if (!Tracer.Instance.Settings.IsIntegrationEnabled(WcfCommon.IntegrationId) || !Tracer.Instance.Settings.DelayWcfInstrumentationEnabled || WcfCommon.GetCurrentOperationContext is null)
{
return CallTargetState.GetDefault();
}

var requestContext = WcfCommon.GetCurrentOperationContext()?.GetProperty<object>("RequestContext").GetValueOrDefault();
return new CallTargetState(WcfCommon.CreateScope(requestContext));
}

/// <summary>
/// OnMethodEnd callback
/// </summary>
/// <typeparam name="TTarget">Type of the target</typeparam>
/// <typeparam name="TReturn">Type of the response</typeparam>
/// <param name="instance">Instance value, aka `this` of the instrumented method.</param>
/// <param name="returnValue">Return value</param>
/// <param name="exception">Exception instance in case the original code threw an exception.</param>
/// <param name="state">Calltarget state value</param>
/// <returns>A response value, in an async scenario will be T of Task of T</returns>
public static CallTargetReturn<TReturn> OnMethodEnd<TTarget, TReturn>(TTarget instance, TReturn returnValue, Exception exception, CallTargetState state)
{
state.Scope.DisposeWithException(exception);
return new CallTargetReturn<TReturn>(returnValue);
}
}
}
#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// <copyright file="TaskMethodInvokerIntegration.cs" company="Datadog">
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License.
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc.
// </copyright>

#if NETFRAMEWORK
using System;
using System.ComponentModel;
using System.Reflection;
using Datadog.Trace.ClrProfiler.CallTarget;
using Datadog.Trace.ClrProfiler.Emit;
using Datadog.Trace.Configuration;

namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Wcf
{
/// <summary>
/// System.ServiceModel.Dispatcher.TaskMethodInvoker calltarget instrumentation
/// </summary>
[InstrumentMethod(
AssemblyName = "System.ServiceModel",
TypeName = "System.ServiceModel.Dispatcher.TaskMethodInvoker",
MethodName = "InvokeAsync",
ReturnTypeName = "System.Threading.Tasks.Task`1<System.Tuple`2<System.Object, System.Object[]>>",
ParameterTypeNames = new[] { ClrNames.Object, "System.Object[]" },
MinimumVersion = "4.0.0",
MaximumVersion = "4.*.*",
IntegrationName = WcfCommon.IntegrationName)]
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public class TaskMethodInvokerIntegration
{
/// <summary>
/// OnMethodBegin callback
/// </summary>
/// <typeparam name="TTarget">Type of the target</typeparam>
/// <param name="instance">Instance value, aka `this` of the instrumented method.</param>
/// <param name="instanceArg">RequestContext instance</param>
/// <param name="inputs">Input arguments</param>
/// <returns>Calltarget state value</returns>
public static CallTargetState OnMethodBegin<TTarget>(TTarget instance, object instanceArg, object[] inputs)
{
// TODO Just use the OperationContext.Current object to get the span information
// context.IncomingMessageHeaders contains:
// - Action
// - To
//
// context.IncomingMessageProperties contains:
// - ["httpRequest"] key to find distributed tracing headers
if (!Tracer.Instance.Settings.IsIntegrationEnabled(WcfCommon.IntegrationId) || !Tracer.Instance.Settings.DelayWcfInstrumentationEnabled || WcfCommon.GetCurrentOperationContext is null)
{
return CallTargetState.GetDefault();
}

var requestContext = WcfCommon.GetCurrentOperationContext()?.GetProperty<object>("RequestContext").GetValueOrDefault();
return new CallTargetState(WcfCommon.CreateScope(requestContext));
}

/// <summary>
/// OnAsyncMethodEnd callback
/// </summary>
/// <typeparam name="TTarget">Type of the target</typeparam>
/// <typeparam name="TResponse">Type of the response, in an async scenario will be T of Task of T</typeparam>
/// <param name="instance">Instance value, aka `this` of the instrumented method.</param>
/// <param name="returnValue">Return value</param>
/// <param name="exception">Exception instance in case the original code threw an exception.</param>
/// <param name="state">Calltarget state value</param>
/// <returns>A response value, in an async scenario will be T of Task of T</returns>
public static TResponse OnAsyncMethodEnd<TTarget, TResponse>(TTarget instance, TResponse returnValue, Exception exception, CallTargetState state)
{
state.Scope.DisposeWithException(exception);
return returnValue;
}
}
}
#endif
Loading