public ZipkinApi(TracerSettings settings) { Log.Debug("Creating new Zipkin Api"); _settings = settings ?? throw new ArgumentNullException(nameof(settings)); _tracesEndpoint = _settings.AgentUri; // User needs to include the proper path. }
public SamplingPriority GetSamplingPriority(Span span) { var traceId = span.TraceId; if (_rules.Count > 0) { foreach (var rule in _rules) { if (rule.IsMatch(span)) { var sampleRate = rule.GetSamplingRate(span); Log.Debug( "Matched on rule {0}. Applying rate of {1} to trace id {2}", rule.RuleName, sampleRate, traceId); return(GetSamplingPriority(span, sampleRate, agentSampling: rule is DefaultSamplingRule)); } } } Log.Debug("No rules matched for trace {0}", traceId); return(SamplingPriority.AutoKeep); }
public bool Allowed(ulong traceId) { if (_maxTracesPerInterval == 0) { // Rate limit of 0 blocks everything return(false); } if (_maxTracesPerInterval < 0) { // Negative rate limit disables rate limiting return(true); } WaitForRefresh(); // This must happen after the wait, because we check for window statistics, modifying this number Interlocked.Increment(ref _windowChecks); var count = _intervalQueue.Count; if (count >= _maxTracesPerInterval) { Log.Debug("Dropping trace id {0} with count of {1} for last {2}ms.", traceId, count, _intervalMilliseconds); return(false); } _intervalQueue.Enqueue(DateTime.Now); Interlocked.Increment(ref _windowAllowed); return(true); }
private void ExtractCounters(ReadOnlyCollection <object> payload) { for (int i = 0; i < payload.Count; ++i) { if (!(payload[i] is IDictionary <string, object> eventPayload)) { continue; } if (!eventPayload.TryGetValue("Name", out object name) || !MetricsMapping.TryGetValue(name.ToString(), out var statName)) { continue; } if (eventPayload.TryGetValue("Mean", out object rawValue) || eventPayload.TryGetValue("Increment", out rawValue)) { var value = (double)rawValue; _statsd.Gauge(statName, value); } else { Log.Debug <object>("EventCounter {0} has no Mean or Increment field", name); } } }
private void OnHostingHttpRequestInStart(object arg) { var httpContext = (HttpContext)HttpRequestInStartHttpContextFetcher.Fetch(arg); if (ShouldIgnore(httpContext)) { if (_isLogLevelDebugEnabled) { Log.Debug("Ignoring request"); } } else { HttpRequest request = httpContext.Request; string host = request.Host.Value; string httpMethod = request.Method?.ToUpperInvariant() ?? "UNKNOWN"; string url = GetUrl(request); string resourceUrl = UriHelpers.GetRelativeUrl(new Uri(url), tryRemoveIds: true) .ToLowerInvariant(); SpanContext propagatedContext = ExtractPropagatedContext(request); Span span = _tracer.StartSpan(HttpRequestInOperationName, propagatedContext) .SetTag(Tags.InstrumentationName, ComponentName); IPAddress remoteIp = null; if (Trace.Tracer.Instance.Settings.AddClientIpToServerSpans) { remoteIp = httpContext?.Connection?.RemoteIpAddress; } span.DecorateWebServerSpan(null, httpMethod, host, url, remoteIp); span.SetTag(Tags.InstrumentationName, IntegrationName); // set analytics sample rate if enabled var analyticsSampleRate = _tracer.Settings.GetIntegrationAnalyticsSampleRate(IntegrationName, enabledWithGlobalSetting: true); span.SetMetric(Tags.Analytics, analyticsSampleRate); Scope scope = _tracer.ActivateSpan(span); _options.OnRequest?.Invoke(scope.Span, httpContext); } }
public float GetSamplingRate(Span span) { Log.Debug("Using the default sampling logic"); var env = span.GetTag(Tags.Env); var service = span.ServiceName; var key = $"service:{service},env:{env}"; if (_sampleRates.TryGetValue(key, out var sampleRate)) { span.SetMetric(Metrics.SamplingAgentDecision, sampleRate); return(sampleRate); } Log.Debug("Could not establish sample rate for trace {0}", span.TraceId); return(1); }
internal Span(SpanContext context, DateTimeOffset?start) { Context = context; ServiceName = context.ServiceName; StartTime = start ?? Context.TraceContext.UtcNow; Log.Debug( "Span started: [s_id: {0}, p_id: {1}, t_id: {2}]", SpanId, Context.ParentId, TraceId); }
private void EventSource_BuildStarted(object sender, BuildStartedEventArgs e) { try { Log.Debug("Build Started"); _buildSpan = _tracer.StartSpan(BuildTags.BuildOperationName); _buildSpan.SetMetric(Tags.Analytics, 1.0d); _buildSpan.SetTraceSamplingPriority(SamplingPriority.AutoKeep); _buildSpan.Type = SpanTypes.Build; _buildSpan.SetTag(BuildTags.BuildName, e.SenderName); foreach (KeyValuePair <string, string> envValue in e.BuildEnvironment) { _buildSpan.SetTag($"{BuildTags.BuildEnvironment}.{envValue.Key}", envValue.Value); } _buildSpan.SetTag(BuildTags.BuildCommand, Environment.CommandLine); _buildSpan.SetTag(BuildTags.BuildWorkingFolder, Environment.CurrentDirectory); _buildSpan.SetTag(BuildTags.BuildStartMessage, e.Message); _buildSpan.SetTag(CommonTags.RuntimeOSArchitecture, Environment.Is64BitOperatingSystem ? "x64" : "x86"); _buildSpan.SetTag(CommonTags.RuntimeProcessArchitecture, Environment.Is64BitProcess ? "x64" : "x86"); CIEnvironmentValues.DecorateSpan(_buildSpan); } catch (Exception ex) { Log.SafeLogError(ex, "Error in BuildStarted event"); } }
public virtual IDisposable SubscribeIfMatch(DiagnosticListener diagnosticListener) { if (ListenerNames.Any(diagnosticListener.Name.Contains)) { return(diagnosticListener.Subscribe(this, IsEventEnabled)); } else { Log.Debug($"{diagnosticListener.Name} not subscribing to {this.GetType().Name}."); } return(null); }
public ISpanBuilder AddReference(string referenceType, global::OpenTracing.ISpanContext referencedContext) { lock (_lock) { if (referenceType == References.ChildOf) { _parent = referencedContext; return(this); } } Log.Debug("ISpanBuilder.AddReference is not implemented for other references than ChildOf by Datadog.Trace"); return(this); }
public SamplingPriority GetSamplingPriority(Span span) { float sampleRate; var traceId = span.TraceId; if (_rules.Count > 0) { foreach (var rule in _rules) { if (rule.IsMatch(span)) { sampleRate = rule.GetSamplingRate(); Log.Debug( "Matched on rule {0}. Applying rate of {1} to trace id {2}", rule.Name, sampleRate, traceId); span.SetMetric(Metrics.SamplingRuleDecision, sampleRate); return(GetSamplingPriority(span, sampleRate, withRateLimiter: true)); } } } var env = span.GetTag(Tags.Env); var service = span.ServiceName; var key = $"service:{service},env:{env}"; if (_sampleRates.TryGetValue(key, out sampleRate)) { Log.Debug("Using the default sampling logic for trace {0}", traceId); return(GetSamplingPriority(span, sampleRate, withRateLimiter: false)); } Log.Debug("Could not establish sample rate for trace {0}", traceId); return(SamplingPriority.AutoKeep); }
public bool Allowed(Span span) { try { if (_maxTracesPerInterval == 0) { // Rate limit of 0 blocks everything return(false); } if (_maxTracesPerInterval < 0) { // Negative rate limit disables rate limiting return(true); } WaitForRefresh(); // This must happen after the wait, because we check for window statistics, modifying this number Interlocked.Increment(ref _windowChecks); var count = _intervalQueue.Count; if (count >= _maxTracesPerInterval) { Log.Debug("Dropping trace id {0} with count of {1} for last {2}ms.", span.TraceId, count, _intervalMilliseconds); return(false); } _intervalQueue.Enqueue(DateTime.UtcNow); Interlocked.Increment(ref _windowAllowed); return(true); } finally { // Always set the sample rate metric whether it was allowed or not // DEV: Setting this allows us to properly compute metrics and debug the // various sample rates that are getting applied to this span span.SetMetric(Metrics.SamplingLimitDecision, GetEffectiveRate()); } }
/// <inheritdoc /> public void Init(HttpApplication httpApplication) { // Intent: The first HttpModule to run Init for this HttpApplication will register for events // Actual: Each HttpApplication that comes through here is potentially a new .NET object, even // if it refers to the same web application. Based on my reading, it appears that initialization // is done for several types of resources. Read more in this SO article -- look at Sunday Ironfoot's // (yes, reliable sounding name of course) response toward the end of this article: // https://stackoverflow.com/a/2416546/24231. // I've discovered that not letting each of these unique application objects be added, and thus // the event handlers be registered within each HttpApplication object, leads to the runtime // weirdness: at one point it crashed consistently for me, and later, I saw no spans at all. Log.Debug("[sts] TracingHttpModule - Init"); if (registeredEventHandlers.TryAdd(httpApplication, 1)) { _httpApplication = httpApplication; httpApplication.BeginRequest += OnBeginRequest; httpApplication.EndRequest += OnEndRequest; httpApplication.Error += OnError; } }
private async Task SendRequestAsync(HttpRequest request, Stream requestStream) { // Headers are always ASCII per the HTTP spec using (var writer = new StreamWriter(requestStream, Encoding.ASCII, bufferSize: MaxRequestHeadersBufferSize, leaveOpen: true)) { await DatadogHttpHeaderHelper.WriteLeadingHeaders(request, writer).ConfigureAwait(false); foreach (var header in request.Headers) { await DatadogHttpHeaderHelper.WriteHeader(writer, header).ConfigureAwait(false); } await DatadogHttpHeaderHelper.WriteEndOfHeaders(writer).ConfigureAwait(false); } await request.Content.CopyToAsync(requestStream, null).ConfigureAwait(false); Logger.Debug("Datadog HTTP: Flushing stream."); await requestStream.FlushAsync().ConfigureAwait(false); }
public void WriteTrace(Span[] trace) { var success = _tracesBuffer.Push(trace); if (!success) { Log.Debug("Trace buffer is full. Dropping a trace from the buffer."); } if (_statsd != null) { _statsd.Increment(TracerMetricNames.Queue.EnqueuedTraces); _statsd.Increment(TracerMetricNames.Queue.EnqueuedSpans, trace.Length); if (!success) { _statsd.Increment(TracerMetricNames.Queue.DroppedTraces); _statsd.Increment(TracerMetricNames.Queue.DroppedSpans, trace.Length); } } }
public void WriteTrace(List <Span> trace) { var success = _tracesBuffer.Push(trace); if (!success) { Log.Debug("Trace buffer is full, dropping it."); } if (_statsd != null) { _statsd.AppendIncrementCount(TracerMetricNames.Queue.EnqueuedTraces); _statsd.AppendIncrementCount(TracerMetricNames.Queue.EnqueuedSpans, trace.Count); if (!success) { _statsd.AppendIncrementCount(TracerMetricNames.Queue.DroppedTraces); } _statsd.Send(); } }
public static object BeginInvokeAction( object asyncControllerActionInvoker, object controllerContext, object actionName, object callback, object state, int opCode, int mdToken, long moduleVersionPtr) { Log.Debug("[sts] AspNetMvcIntegration - BeginInvokeAction"); if (asyncControllerActionInvoker == null) { throw new ArgumentNullException(nameof(asyncControllerActionInvoker)); } Scope scope = null; try { if (HttpContext.Current != null) { scope = CreateScope(controllerContext); HttpContext.Current.Items[HttpContextKey] = scope; } } catch (Exception ex) { Log.Error(ex, "Error instrumenting method {0}", "System.Web.Mvc.Async.IAsyncActionInvoker.BeginInvokeAction()"); } 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.ErrorRetrievingMethod( exception: ex, moduleVersionPointer: moduleVersionPtr, mdToken: mdToken, opCode: opCode, instrumentedType: AsyncActionInvokerTypeName, methodName: nameof(BeginInvokeAction), instanceType: asyncControllerActionInvoker.GetType().AssemblyQualifiedName); throw; } try { // call the original method, inspecting (but not catching) any unhandled exceptions return(instrumentedMethod(asyncControllerActionInvoker, controllerContext, actionName, callback, state)); } catch (Exception ex) { scope?.Span.SetException(ex); throw; } }
public static object GetResponse(object webRequest, int opCode, int mdToken, long moduleVersionPtr) { Log.Debug("[sts] WebRequestIntegration - GetResponse"); if (webRequest == null) { throw new ArgumentNullException(nameof(webRequest)); } const string methodName = nameof(GetResponse); Func <object, WebResponse> callGetResponse; try { var instrumentedType = webRequest.GetInstrumentedType("System.Net.WebRequest"); callGetResponse = MethodBuilder <Func <object, WebResponse> > .Start(moduleVersionPtr, mdToken, opCode, methodName) .WithConcreteType(instrumentedType) .WithNamespaceAndNameFilters("System.Net.WebResponse") .Build(); } catch (Exception ex) { Log.ErrorRetrievingMethod( exception: ex, moduleVersionPointer: moduleVersionPtr, mdToken: mdToken, opCode: opCode, instrumentedType: WebRequestTypeName, methodName: methodName, instanceType: webRequest.GetType().AssemblyQualifiedName); throw; } var request = (WebRequest)webRequest; if (!(request is HttpWebRequest) || !IsTracingEnabled(request)) { return(callGetResponse(webRequest)); } using (var scope = ScopeFactory.CreateOutboundHttpScope(Tracer.Instance, request.Method, request.RequestUri, IntegrationName)) { try { if (scope != null) { // add distributed tracing headers to the HTTP request SpanContextPropagator.Instance.Inject(scope.Span.Context, request.Headers.Wrap()); } WebResponse response = callGetResponse(webRequest); if (scope != null && response is HttpWebResponse webResponse) { scope.Span.SetTag(Tags.HttpStatusCode, ((int)webResponse.StatusCode).ToString()); } return(response); } catch (Exception ex) { scope?.Span.SetException(ex); throw; } } }
public float GetSamplingRate(Span span) { Log.Debug("Using the global sampling rate: {0}", _globalRate); span.SetMetric(Metrics.SamplingRuleDecision, _globalRate); return(_globalRate); }
internal Tracer(TracerSettings settings, IAgentWriter agentWriter, ISampler sampler, IScopeManager scopeManager, IDogStatsd statsd) { // update the count of Tracer instances Interlocked.Increment(ref _liveTracerCount); Settings = settings ?? TracerSettings.FromDefaultSources(); // if not configured, try to determine an appropriate service name DefaultServiceName = Settings.ServiceName ?? GetApplicationName() ?? UnknownServiceName; // only set DogStatsdClient if tracer metrics are enabled if (Settings.TracerMetricsEnabled) { // Run this first in case the port override is ready TracingProcessManager.SubscribeToDogStatsDPortOverride( port => { Log.Debug("Attempting to override dogstatsd port with {0}", port); Statsd = CreateDogStatsdClient(Settings, DefaultServiceName, port); }); Statsd = statsd ?? CreateDogStatsdClient(Settings, DefaultServiceName, Settings.DogStatsdPort); } // Run this first in case the port override is ready TracingProcessManager.SubscribeToTraceAgentPortOverride( port => { Log.Debug("Attempting to override trace agent port with {0}", port); var builder = new UriBuilder(Settings.AgentUri) { Port = port }; var baseEndpoint = builder.Uri; if (_agentWriter == null) { IApi overridingApiClient = new Api(baseEndpoint, apiRequestFactory: null, Statsd); _agentWriter = _agentWriter ?? new AgentWriter(overridingApiClient, Statsd, queueSize: Settings.TraceQueueSize); } else { _agentWriter.SetApiBaseEndpoint(baseEndpoint); } }); // fall back to default implementations of each dependency if not provided _agentWriter = agentWriter ?? new AgentWriter(new Api(Settings.AgentUri, apiRequestFactory: null, Statsd), Statsd, queueSize: Settings.TraceQueueSize); _scopeManager = scopeManager ?? new AsyncLocalScopeManager(); Sampler = sampler ?? new RuleBasedSampler(new RateLimiter(Settings.MaxTracesSubmittedPerSecond)); if (!string.IsNullOrWhiteSpace(Settings.CustomSamplingRules)) { foreach (var rule in CustomSamplingRule.BuildFromConfigurationString(Settings.CustomSamplingRules)) { Sampler.RegisterRule(rule); } } if (Settings.GlobalSamplingRate != null) { var globalRate = (float)Settings.GlobalSamplingRate; if (globalRate < 0f || globalRate > 1f) { Log.Warning("{0} configuration of {1} is out of range", ConfigurationKeys.GlobalSamplingRate, Settings.GlobalSamplingRate); } else { Sampler.RegisterRule(new GlobalSamplingRule(globalRate)); } } // Register callbacks to make sure we flush the traces before exiting AppDomain.CurrentDomain.ProcessExit += CurrentDomain_ProcessExit; AppDomain.CurrentDomain.DomainUnload += CurrentDomain_DomainUnload; try { // Registering for the AppDomain.UnhandledException event cannot be called by a security transparent method // This will only happen if the Tracer is not run full-trust AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; } catch (Exception ex) { Log.Warning(ex, "Unable to register a callback to the AppDomain.UnhandledException event."); } try { // Registering for the cancel key press event requires the System.Security.Permissions.UIPermission Console.CancelKeyPress += Console_CancelKeyPress; } catch (Exception ex) { Log.Warning(ex, "Unable to register a callback to the Console.CancelKeyPress event."); } // start the heartbeat loop _heartbeatTimer = new Timer(HeartbeatCallback, state: null, dueTime: TimeSpan.Zero, period: TimeSpan.FromMinutes(1)); // If configured, add/remove the correlation identifiers into the // LibLog logging context when a scope is activated/closed if (Settings.LogsInjectionEnabled) { InitializeLibLogScopeEventSubscriber(_scopeManager, DefaultServiceName, Settings.ServiceVersion, Settings.Environment); } if (Interlocked.Exchange(ref _firstInitialization, 0) == 1) { if (Settings.StartupDiagnosticLogEnabled) { _ = WriteDiagnosticLog(); } if (Settings.RuntimeMetricsEnabled) { _runtimeMetricsWriter = new RuntimeMetricsWriter(Statsd ?? CreateDogStatsdClient(Settings, DefaultServiceName, Settings.DogStatsdPort), 10000); } } }
internal Tracer(TracerSettings settings, IAgentWriter agentWriter, ISampler sampler, IScopeManager scopeManager, IStatsd statsd) { // update the count of Tracer instances Interlocked.Increment(ref _liveTracerCount); Settings = settings ?? TracerSettings.FromDefaultSources(); // if not configured, try to determine an appropriate service name DefaultServiceName = Settings.ServiceName ?? GetApplicationName() ?? UnknownServiceName; // only set DogStatsdClient if tracer metrics are enabled if (Settings.TracerMetricsEnabled) { // Run this first in case the port override is ready TracingProcessManager.SubscribeToDogStatsDPortOverride( port => { Log.Debug("Attempting to override dogstatsd port with {0}", port); Statsd = CreateDogStatsdClient(Settings, DefaultServiceName, port); }); Statsd = statsd ?? CreateDogStatsdClient(Settings, DefaultServiceName, Settings.DogStatsdPort); } // Run this first in case the port override is ready TracingProcessManager.SubscribeToTraceAgentPortOverride( port => { Log.Debug("Attempting to override trace agent port with {0}", port); var builder = new UriBuilder(Settings.AgentUri) { Port = port }; var baseEndpoint = builder.Uri; IApi overridingApiClient = new Api(baseEndpoint, delegatingHandler: null, Statsd); if (_agentWriter == null) { _agentWriter = _agentWriter ?? new AgentWriter(overridingApiClient, Statsd); } else { _agentWriter.OverrideApi(overridingApiClient); } }); // fall back to default implementations of each dependency if not provided _agentWriter = agentWriter ?? new AgentWriter(new Api(Settings.AgentUri, delegatingHandler: null, Statsd), Statsd); _scopeManager = scopeManager ?? new AsyncLocalScopeManager(); Sampler = sampler ?? new RuleBasedSampler(new RateLimiter(Settings.MaxTracesSubmittedPerSecond)); if (!string.IsNullOrWhiteSpace(Settings.CustomSamplingRules)) { // User has opted in, ensure rate limiter is used RuleBasedSampler.OptInTracingWithoutLimits(); foreach (var rule in CustomSamplingRule.BuildFromConfigurationString(Settings.CustomSamplingRules)) { Sampler.RegisterRule(rule); } } if (Settings.GlobalSamplingRate != null) { var globalRate = (float)Settings.GlobalSamplingRate; if (globalRate < 0f || globalRate > 1f) { Log.Warning("{0} configuration of {1} is out of range", ConfigurationKeys.GlobalSamplingRate, Settings.GlobalSamplingRate); } else { Sampler.RegisterRule(new GlobalSamplingRule(globalRate)); } } // Register callbacks to make sure we flush the traces before exiting AppDomain.CurrentDomain.ProcessExit += CurrentDomain_ProcessExit; AppDomain.CurrentDomain.DomainUnload += CurrentDomain_DomainUnload; AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; Console.CancelKeyPress += Console_CancelKeyPress; // start the heartbeat loop _heartbeatTimer = new Timer(HeartbeatCallback, state: null, dueTime: TimeSpan.Zero, period: TimeSpan.FromMinutes(1)); // If configured, add/remove the correlation identifiers into the // LibLog logging context when a scope is activated/closed if (Settings.LogsInjectionEnabled) { InitializeLibLogScopeEventSubscriber(_scopeManager, DefaultServiceName, Settings.ServiceVersion, Settings.Environment); } }
internal static DynamicMethod CreateBeginMethodDelegate(Type integrationType, Type targetType, Type[] argumentsTypes) { /* * OnMethodBegin signatures with 1 or more parameters with 1 or more generics: * - CallTargetState OnMethodBegin<TTarget>(TTarget instance); * - CallTargetState OnMethodBegin<TTarget, TArg1>(TTarget instance, TArg1 arg1); * - CallTargetState OnMethodBegin<TTarget, TArg1, TArg2>(TTarget instance, TArg1 arg1, TArg2); * - CallTargetState OnMethodBegin<TTarget, TArg1, TArg2, ...>(TTarget instance, TArg1 arg1, TArg2, ...); * - CallTargetState OnMethodBegin<TTarget>(); * - CallTargetState OnMethodBegin<TTarget, TArg1>(TArg1 arg1); * - CallTargetState OnMethodBegin<TTarget, TArg1, TArg2>(TArg1 arg1, TArg2); * - CallTargetState OnMethodBegin<TTarget, TArg1, TArg2, ...>(TArg1 arg1, TArg2, ...); * */ Log.Debug($"Creating BeginMethod Dynamic Method for '{integrationType.FullName}' integration. [Target={targetType.FullName}]"); MethodInfo onMethodBeginMethodInfo = integrationType.GetMethod(BeginMethodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); if (onMethodBeginMethodInfo is null) { throw new NullReferenceException($"Couldn't find the method: {BeginMethodName} in type: {integrationType.FullName}"); } if (onMethodBeginMethodInfo.ReturnType != typeof(CallTargetState)) { throw new ArgumentException($"The return type of the method: {BeginMethodName} in type: {integrationType.FullName} is not {nameof(CallTargetState)}"); } Type[] genericArgumentsTypes = onMethodBeginMethodInfo.GetGenericArguments(); if (genericArgumentsTypes.Length < 1) { throw new ArgumentException($"The method: {BeginMethodName} in type: {integrationType.FullName} doesn't have the generic type for the instance type."); } ParameterInfo[] onMethodBeginParameters = onMethodBeginMethodInfo.GetParameters(); if (onMethodBeginParameters.Length < argumentsTypes.Length) { throw new ArgumentException($"The method: {BeginMethodName} with {onMethodBeginParameters.Length} parameters in type: {integrationType.FullName} has less parameters than required."); } else if (onMethodBeginParameters.Length > argumentsTypes.Length + 1) { throw new ArgumentException($"The method: {BeginMethodName} with {onMethodBeginParameters.Length} parameters in type: {integrationType.FullName} has more parameters than required."); } else if (onMethodBeginParameters.Length != argumentsTypes.Length && onMethodBeginParameters[0].ParameterType != genericArgumentsTypes[0]) { throw new ArgumentException($"The first generic argument for method: {BeginMethodName} in type: {integrationType.FullName} must be the same as the first parameter for the instance value."); } List <Type> callGenericTypes = new List <Type>(); bool mustLoadInstance = onMethodBeginParameters.Length != argumentsTypes.Length; Type instanceGenericType = genericArgumentsTypes[0]; Type instanceGenericConstraint = instanceGenericType.GetGenericParameterConstraints().FirstOrDefault(); Type instanceProxyType = null; if (instanceGenericConstraint != null) { var result = DuckType.GetOrCreateProxyType(instanceGenericConstraint, targetType); instanceProxyType = result.ProxyType; callGenericTypes.Add(instanceProxyType); } else { callGenericTypes.Add(targetType); } DynamicMethod callMethod = new DynamicMethod( $"{onMethodBeginMethodInfo.DeclaringType.Name}.{onMethodBeginMethodInfo.Name}", typeof(CallTargetState), new Type[] { targetType }.Concat(argumentsTypes), onMethodBeginMethodInfo.Module, true); ILGenerator ilWriter = callMethod.GetILGenerator(); // Load the instance if is needed if (mustLoadInstance) { ilWriter.Emit(OpCodes.Ldarg_0); if (instanceGenericConstraint != null) { WriteCreateNewProxyInstance(ilWriter, instanceProxyType, targetType); } } // Load arguments for (var i = mustLoadInstance ? 1 : 0; i < onMethodBeginParameters.Length; i++) { Type sourceParameterType = argumentsTypes[mustLoadInstance ? i - 1 : i]; Type targetParameterType = onMethodBeginParameters[i].ParameterType; Type targetParameterTypeConstraint = null; Type parameterProxyType = null; if (targetParameterType.IsGenericParameter) { targetParameterType = genericArgumentsTypes[targetParameterType.GenericParameterPosition]; targetParameterTypeConstraint = targetParameterType.GetGenericParameterConstraints().FirstOrDefault(pType => pType != typeof(IDuckType)); if (targetParameterTypeConstraint is null) { callGenericTypes.Add(sourceParameterType); } else { var result = DuckType.GetOrCreateProxyType(targetParameterTypeConstraint, sourceParameterType); parameterProxyType = result.ProxyType; callGenericTypes.Add(parameterProxyType); } } else if (!targetParameterType.IsAssignableFrom(sourceParameterType) && (!(sourceParameterType.IsEnum && targetParameterType.IsEnum))) { throw new InvalidCastException($"The target parameter {targetParameterType} can't be assigned from {sourceParameterType}"); } WriteLoadArgument(ilWriter, i, mustLoadInstance); if (parameterProxyType != null) { WriteCreateNewProxyInstance(ilWriter, parameterProxyType, sourceParameterType); } } // Call method onMethodBeginMethodInfo = onMethodBeginMethodInfo.MakeGenericMethod(callGenericTypes.ToArray()); ilWriter.EmitCall(OpCodes.Call, onMethodBeginMethodInfo, null); ilWriter.Emit(OpCodes.Ret); Log.Debug($"Created BeginMethod Dynamic Method for '{integrationType.FullName}' integration. [Target={targetType.FullName}]"); return(callMethod); }