Example #1
0
        private void OnMvcBeforeAction(object arg)
        {
            var httpContext = (HttpContext)BeforeActionHttpContextFetcher.Fetch(arg);

            if (ShouldIgnore(httpContext))
            {
                if (_isLogLevelDebugEnabled)
                {
                    Log.Debug("Ignoring request");
                }
            }
            else
            {
                Span span = _tracer.ScopeManager.Active?.Span;

                if (span != null)
                {
                    // NOTE: This event is the start of the action pipeline. The action has been selected, the route
                    //       has been selected but no filters have run and model binding hasn't occured.
                    var actionDescriptor = (ActionDescriptor)BeforeActionActionDescriptorFetcher.Fetch(arg);

                    // Try to use the best tag values available.

                    if (!span.Tags.ContainsKey(Tags.HttpMethod))
                    {
                        HttpRequest request    = httpContext.Request;
                        string      httpMethod = request.Method?.ToUpperInvariant();

                        if (!string.IsNullOrEmpty(httpMethod))
                        {
                            span.Tags.Add(Tags.HttpMethod, httpMethod);
                        }
                    }

                    if (actionDescriptor.RouteValues.TryGetValue("controller", out string controllerName))
                    {
                        span.Tags[Tags.AspNetController] = controllerName;
                    }

                    if (actionDescriptor.RouteValues.TryGetValue("action", out string actionName))
                    {
                        span.Tags[Tags.AspNetAction] = actionName;
                    }

                    string routeTemplate = actionDescriptor.AttributeRouteInfo?.Template;
                    if (!string.IsNullOrEmpty(routeTemplate))
                    {
                        span.OperationName = routeTemplate;
                    }
                    else if (!string.IsNullOrEmpty(controllerName) && !string.IsNullOrEmpty(actionName))
                    {
                        span.OperationName = $"{controllerName}.{actionName}".ToLowerInvariant();
                    }

                    ServerTimingHeader.SetHeaders(span.Context, httpContext.Response.Headers, (headers, name, value) => headers.Add(name, value));
                }
            }
        }
Example #2
0
        public void SetHeaders_InjectsTheHeadersCorrectly(IHeadersCollection headers)
        {
            var traceId     = TraceId.CreateRandom();
            var spanContext = new SpanContext(traceId, 123, SamplingPriority.AutoKeep);

            ServerTimingHeader.SetHeaders(spanContext, headers, (h, name, value) => h.Add(name, value));

            using (new AssertionScope())
            {
                headers.GetValues("Server-Timing").Should().HaveCount(1);
                headers.GetValues("Server-Timing").Should().Equal($"traceparent;desc=\"00-{traceId.ToString()}-000000000000007b-01\"");
                headers.GetValues("Access-Control-Expose-Headers").Should().HaveCount(1);
                headers.GetValues("Access-Control-Expose-Headers").Should().Equal("Server-Timing");
            }
        }
Example #3
0
        private void OnBeginRequest(object sender, EventArgs eventArgs)
        {
            Scope scope = null;

            try
            {
                var tracer = Tracer.Instance;

                if (!tracer.Settings.IsIntegrationEnabled(IntegrationId))
                {
                    // integration disabled
                    return;
                }

                var httpContext = (sender as HttpApplication)?.Context;

                if (httpContext == null)
                {
                    return;
                }

                // Make sure the request wasn't already handled by another TracingHttpModule,
                // in case they're registered multiple times
                if (httpContext.Items.Contains(_httpContextScopeKey))
                {
                    return;
                }

                HttpRequest httpRequest       = httpContext.Request;
                SpanContext propagatedContext = null;
                var         tagsFromHeaders   = Enumerable.Empty <KeyValuePair <string, string> >();
                var         propagator        = SpanContextPropagator.Instance;

                if (tracer.InternalActiveScope == null)
                {
                    try
                    {
                        // extract propagated http headers
                        var headers = httpRequest.Headers.Wrap();
                        propagatedContext = propagator.Extract(headers);
                        tagsFromHeaders   = headers.ExtractHeaderTags(tracer.Settings.HeaderTags, PropagationExtensions.HttpRequestHeadersTagPrefix);
                    }
                    catch (Exception ex)
                    {
                        Log.Error(ex, "Error extracting propagated HTTP headers.");
                    }
                }

                string host       = httpRequest.Headers.Get("Host");
                string httpMethod = httpRequest.HttpMethod.ToUpperInvariant();
                string url        = httpRequest.Url.ToString(); // Upstream uses RawUrl, ie. the part of the URL following the domain information.

                var tags = new WebTags();
                scope = tracer.StartActiveInternal(httpMethod, propagatedContext, tags: tags);
                // Leave resourceName blank for now - we'll update it in OnEndRequest
                scope.Span.DecorateWebServerSpan(resourceName: null, httpMethod, host, url, tags, tagsFromHeaders, httpRequest.UserHostAddress);
                scope.Span.LogicScope = _requestOperationName;

                tags.SetAnalyticsSampleRate(IntegrationId, tracer.Settings, enabledWithGlobalSetting: true);

                // Decorate the incoming HTTP Request with distributed tracing headers
                // in case the next processor cannot access the stored Scope
                // (e.g. WCF being hosted in IIS)
                if (HttpRuntime.UsingIntegratedPipeline)
                {
                    ServerTimingHeader.SetHeaders(scope.Span.Context, httpContext.Response.Headers, (headers, name, value) => headers.Add(name, value));

                    propagator.Inject(scope.Span.Context, httpRequest.Headers.Wrap());
                }

                httpContext.Items[_httpContextScopeKey] = scope;

                tracer.TracerManager.Telemetry.IntegrationGeneratedSpan(IntegrationId);
            }
            catch (Exception ex)
            {
                // Dispose here, as the scope won't be in context items and won't get disposed on request end in that case...
                scope?.Dispose();
                Log.Error(ex, "SignalFx ASP.NET HttpModule instrumentation error");
            }
        }
        public static bool EndInvokeAction(
            object asyncControllerActionInvoker,
            object asyncResult,
            int opCode,
            int mdToken,
            long moduleVersionPtr)
        {
            if (asyncControllerActionInvoker == null)
            {
                throw new ArgumentNullException(nameof(asyncControllerActionInvoker));
            }

            Scope scope       = null;
            var   httpContext = HttpContext.Current;

            try
            {
                scope = httpContext?.Items[HttpContextKey] as Scope;
            }
            catch (Exception ex)
            {
                Log.Error(ex, "Error instrumenting method {0}", $"{AsyncActionInvokerTypeName}.EndInvokeAction()");
            }

            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.ErrorRetrievingMethod(
                    exception: ex,
                    moduleVersionPointer: moduleVersionPtr,
                    mdToken: mdToken,
                    opCode: opCode,
                    instrumentedType: AsyncActionInvokerTypeName,
                    methodName: nameof(EndInvokeAction),
                    instanceType: asyncControllerActionInvoker.GetType().AssemblyQualifiedName);
                throw;
            }

            try
            {
                // call the original method, inspecting (but not catching) any unhandled exceptions
                var res = instrumentedMethod(asyncControllerActionInvoker, asyncResult);

                if (scope == null)
                {
                    return(res);
                }

                var response = httpContext?.Response;
                if (response?.StatusCode == 200)
                {
                    scope.Span.SetTag(Tags.HttpStatusCode, "200");
                }
                else if (response?.StatusCode != null)
                {
                    scope.Span.SetTag(Tags.HttpStatusCode, response.StatusCode.ToString(CultureInfo.InvariantCulture));
                    if (!string.IsNullOrWhiteSpace(response?.StatusDescription))
                    {
                        scope.Span.SetTag(Tags.HttpStatusText, response.StatusDescription);
                    }
                }

                ServerTimingHeader.SetHeaders(scope.Span.Context, response, (resp, name, value) => resp.Headers.Add(name, value));
                return(res);
            }
            catch (Exception ex)
            {
                scope?.Span.SetException(ex);
                throw;
            }
            finally
            {
                scope?.Dispose();
            }
        }
        public static object BeginInvokeAction(
            object asyncControllerActionInvoker,
            object controllerContext,
            object actionName,
            object callback,
            object state,
            int opCode,
            int mdToken,
            long moduleVersionPtr)
        {
            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
                var response = instrumentedMethod(asyncControllerActionInvoker, controllerContext, actionName, callback, state);

                if (HttpContext.Current != null && scope != null)
                {
                    ServerTimingHeader.SetHeaders(scope.Span.Context, HttpContext.Current.Response, (resp, name, value) => resp.Headers.Add(name, value));
                }

                return(response);
            }
            catch (Exception ex)
            {
                scope?.Span.SetException(ex);
                throw;
            }
        }
Example #6
0
        /// <summary>
        /// Calls the underlying ExecuteAsync and traces the request.
        /// </summary>
        /// <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="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,
            int opCode,
            int mdToken,
            long moduleVersionPtr)
        {
            Func <object, object, CancellationToken, Task <HttpResponseMessage> > instrumentedMethod;

            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.ErrorRetrievingMethod(
                    exception: ex,
                    moduleVersionPointer: moduleVersionPtr,
                    mdToken: mdToken,
                    opCode: opCode,
                    instrumentedType: HttpControllerTypeName,
                    methodName: nameof(ExecuteAsync),
                    instanceType: apiController.GetType().AssemblyQualifiedName);
                throw;
            }

            using (Scope scope = CreateScope(controllerContext))
            {
                try
                {
                    // call the original method, inspecting (but not catching) any unhandled exceptions
                    var responseMessage = await instrumentedMethod(apiController, controllerContext, cancellationToken).ConfigureAwait(false);

                    if (scope != null)
                    {
                        // some fields aren't set till after execution, so populate anything missing
                        UpdateSpan(controllerContext, scope.Span);
                        if (responseMessage.StatusCode == HttpStatusCode.OK)
                        {
                            scope.Span.SetTag(Tags.HttpStatusCode, "200");
                        }
                        else
                        {
                            scope.Span.SetTag(Tags.HttpStatusCode, ((int)responseMessage.StatusCode).ToString(CultureInfo.InvariantCulture));
                            if (!string.IsNullOrWhiteSpace(responseMessage.ReasonPhrase))
                            {
                                scope.Span.SetTag(Tags.HttpStatusText, responseMessage.ReasonPhrase);
                            }
                        }

                        ServerTimingHeader.SetHeaders(scope.Span.Context, responseMessage, (response, name, value) => response.Headers.Add(name, value));
                    }

                    return(responseMessage);
                }
                catch (Exception ex)
                {
                    if (scope != null)
                    {
                        // some fields aren't set till after execution, so populate anything missing
                        UpdateSpan(controllerContext, scope.Span);
                    }

                    scope?.Span.SetException(ex);

                    throw;
                }
            }
        }