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)); } } }
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"); } }
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; } }
/// <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; } } }