Esempio n. 1
0
        private static Scope CreateScope(object controllerContext, out AspNetTags tags)
        {
            Scope scope = null;

            tags = null;

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

                var         tracer            = Tracer.Instance;
                var         request           = controllerContext.GetProperty <object>("Request").GetValueOrDefault();
                SpanContext propagatedContext = null;
                var         tagsFromHeaders   = Enumerable.Empty <KeyValuePair <string, string> >();

                if (request != null && tracer.ActiveScope == null)
                {
                    try
                    {
                        // extract propagated http headers
                        var headers           = request.GetProperty <object>("Headers").GetValueOrDefault();
                        var headersCollection = new ReflectionHttpHeadersCollection(headers);

                        propagatedContext = SpanContextPropagator.Instance.Extract(headersCollection);
                        tagsFromHeaders   = SpanContextPropagator.Instance.ExtractHeaderTags(headersCollection, tracer.Settings.HeaderTags);
                    }
                    catch (Exception ex)
                    {
                        Log.Error(ex, "Error extracting propagated HTTP headers.");
                    }
                }

                tags  = new AspNetTags();
                scope = tracer.StartActiveWithTags(OperationName, propagatedContext, tags: tags);
                UpdateSpan(controllerContext, scope.Span, tags, tagsFromHeaders);

                // set analytics sample rate if enabled
                var analyticsSampleRate = tracer.Settings.GetIntegrationAnalyticsSampleRate(IntegrationName, enabledWithGlobalSetting: true);

                if (analyticsSampleRate != null)
                {
                    tags.AnalyticsSampleRate = analyticsSampleRate;
                }
            }
            catch (Exception ex)
            {
                Log.Error(ex, "Error creating scope.");
            }

            return(scope);
        }
Esempio n. 2
0
        internal static Scope CreateScope(IHttpControllerContext controllerContext, out AspNetTags tags)
        {
            Scope scope = null;

            tags = null;

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

                var         tracer            = Tracer.Instance;
                var         request           = controllerContext.Request;
                SpanContext propagatedContext = null;
                var         tagsFromHeaders   = Enumerable.Empty <KeyValuePair <string, string> >();

                if (request != null && tracer.InternalActiveScope == null)
                {
                    try
                    {
                        // extract propagated http headers
                        var headers           = request.Headers;
                        var headersCollection = new HttpHeadersCollection(headers);

                        propagatedContext = SpanContextPropagator.Instance.Extract(headersCollection);
                        tagsFromHeaders   = SpanContextPropagator.Instance.ExtractHeaderTags(headersCollection, tracer.Settings.HeaderTags, SpanContextPropagator.HttpRequestHeadersTagPrefix, request.Headers.UserAgent.ToString());
                    }
                    catch (Exception ex)
                    {
                        Log.Error(ex, "Error extracting propagated HTTP headers.");
                    }
                }

                tags  = new AspNetTags();
                scope = tracer.StartActiveInternal(OperationName, propagatedContext, tags: tags);
                UpdateSpan(controllerContext, scope.Span, tags, tagsFromHeaders);

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

            return(scope);
        }
Esempio n. 3
0
        private static void UpdateSpan(object controllerContext, Span span, AspNetTags tags, IEnumerable <KeyValuePair <string, string> > headerTags)
        {
            try
            {
                object request    = controllerContext.GetProperty <object>("Request").GetValueOrDefault();
                Uri    requestUri = request.GetProperty <Uri>("RequestUri").GetValueOrDefault();

                string host        = request.GetProperty <object>("Headers").GetProperty <string>("Host").GetValueOrDefault() ?? string.Empty;
                string rawUrl      = requestUri?.ToString().ToLowerInvariant() ?? string.Empty;
                string absoluteUri = requestUri?.AbsoluteUri?.ToLowerInvariant() ?? string.Empty;
                string method      = request.GetProperty <object>("Method").GetProperty <string>("Method").GetValueOrDefault()?.ToUpperInvariant() ?? "GET";
                string route       = null;
                try
                {
                    route = controllerContext.GetProperty <object>("RouteData").GetProperty <object>("Route").GetProperty <string>("RouteTemplate").GetValueOrDefault();
                }
                catch
                {
                }

                string resourceName = $"{method} {absoluteUri.ToLowerInvariant()}";

                if (route != null)
                {
                    resourceName = $"{method} {route.ToLowerInvariant()}";
                }
                else if (requestUri != null)
                {
                    var cleanUri = UriHelpers.GetRelativeUrl(requestUri, tryRemoveIds: true);
                    resourceName = $"{method} {cleanUri.ToLowerInvariant()}";
                }

                string controller = string.Empty;
                string action     = string.Empty;
                try
                {
                    var routeValues = controllerContext.GetProperty <object>("RouteData").GetProperty <IDictionary <string, object> >("Values").GetValueOrDefault();
                    if (routeValues != null)
                    {
                        controller = (routeValues.GetValueOrDefault("controller") as string)?.ToLowerInvariant();
                        action     = (routeValues.GetValueOrDefault("action") as string)?.ToLowerInvariant();
                    }
                }
                catch
                {
                }

                // Fail safe to catch templates in routing values
                resourceName =
                    resourceName
                    .Replace("{controller}", controller)
                    .Replace("{action}", action);

                span.DecorateWebServerSpan(
                    resourceName: resourceName,
                    method: method,
                    host: host,
                    httpUrl: rawUrl,
                    tags,
                    headerTags);

                tags.AspNetAction     = action;
                tags.AspNetController = controller;
                tags.AspNetRoute      = route;
            }
            catch (Exception ex)
            {
                Log.Error(ex, "Error populating scope data.");
            }
        }
        internal static void UpdateSpan(IHttpControllerContext controllerContext, Span span, AspNetTags tags, IEnumerable <KeyValuePair <string, string> > headerTags)
        {
            try
            {
                var newResourceNamesEnabled = Tracer.Instance.Settings.RouteTemplateResourceNamesEnabled;
                var request    = controllerContext.Request;
                Uri requestUri = request.RequestUri;

                string host   = request.Headers.Host ?? string.Empty;
                string rawUrl = requestUri?.ToString().ToLowerInvariant() ?? string.Empty;
                string method = request.Method.Method?.ToUpperInvariant() ?? "GET";
                string route  = null;
                try
                {
                    route = controllerContext.RouteData.Route.RouteTemplate;
                }
                catch
                {
                }

                string resourceName;

                if (route != null)
                {
                    resourceName = $"{method} {(newResourceNamesEnabled ? "/" : string.Empty)}{route.ToLowerInvariant()}";
                }
                else if (requestUri != null)
                {
                    var cleanUri = UriHelpers.GetCleanUriPath(requestUri);
                    resourceName = $"{method} {cleanUri.ToLowerInvariant()}";
                }
                else
                {
                    resourceName = $"{method}";
                }

                string controller = string.Empty;
                string action     = string.Empty;
                string area       = string.Empty;
                try
                {
                    var routeValues = controllerContext.RouteData.Values;
                    if (routeValues != null)
                    {
                        controller = (routeValues.GetValueOrDefault("controller") as string)?.ToLowerInvariant();
                        action     = (routeValues.GetValueOrDefault("action") as string)?.ToLowerInvariant();
                        area       = (routeValues.GetValueOrDefault("area") as string)?.ToLowerInvariant();
                    }
                }
                catch
                {
                }

                // Replace well-known routing tokens
                resourceName =
                    resourceName
                    .Replace("{area}", area)
                    .Replace("{controller}", controller)
                    .Replace("{action}", action);

                span.DecorateWebServerSpan(
                    resourceName: resourceName,
                    method: method,
                    host: host,
                    httpUrl: rawUrl,
                    tags,
                    headerTags);

                tags.AspNetAction     = action;
                tags.AspNetController = controller;
                tags.AspNetArea       = area;
                tags.AspNetRoute      = route;

                if (newResourceNamesEnabled)
                {
                    // set the resource name in the HttpContext so TracingHttpModule can update root span
                    var httpContext = System.Web.HttpContext.Current;
                    if (httpContext is not null)
                    {
                        httpContext.Items[SharedConstants.HttpContextPropagatedResourceNameKey] = resourceName;
                    }
                }
            }
            catch (Exception ex)
            {
                Log.Error(ex, "Error populating scope data.");
            }
        }
Esempio n. 5
0
        internal static void UpdateSpan(IHttpControllerContext controllerContext, Span span, AspNetTags tags, IEnumerable <KeyValuePair <string, string> > headerTags)
        {
            try
            {
                var newResourceNamesEnabled = Tracer.Instance.Settings.RouteTemplateResourceNamesEnabled;
                var request    = controllerContext.Request;
                Uri requestUri = request.RequestUri;

                string host   = request.Headers.Host ?? string.Empty;
                string rawUrl = requestUri?.ToString().ToLowerInvariant() ?? string.Empty;
                string method = request.Method.Method?.ToUpperInvariant() ?? "GET";
                string route  = null;
                try
                {
                    route = controllerContext.RouteData.Route.RouteTemplate;
                }
                catch
                {
                }

                IDictionary <string, object> routeValues = null;
                try
                {
                    routeValues = controllerContext.RouteData.Values;
                }
                catch
                {
                }

                string resourceName;

                string controller = string.Empty;
                string action     = string.Empty;
                string area       = string.Empty;

                if (route is not null && routeValues is not null)
                {
                    resourceName = AspNetResourceNameHelper.CalculateResourceName(
                        httpMethod: method,
                        routeTemplate: route,
                        routeValues,
                        defaults: null,
                        out area,
                        out controller,
                        out action,
                        addSlashPrefix: newResourceNamesEnabled,
                        expandRouteTemplates: newResourceNamesEnabled && Tracer.Instance.Settings.ExpandRouteTemplatesEnabled);
                }
                else if (requestUri != null)
                {
                    var cleanUri = UriHelpers.GetCleanUriPath(requestUri);
                    resourceName = $"{method} {cleanUri}";
                }
                else
                {
                    resourceName = $"{method}";
                }

                if (route is null && routeValues is not null)
                {
                    // we weren't able to get the route template (somehow) but _were_ able to
                    // get the route values. Not sure how this is possible, but is preexisting behaviour
                    try
                    {
                        controller = (routeValues.GetValueOrDefault("controller") as string)?.ToLowerInvariant();
                        action     = (routeValues.GetValueOrDefault("action") as string)?.ToLowerInvariant();
                        area       = (routeValues.GetValueOrDefault("area") as string)?.ToLowerInvariant();
                    }
                    catch
                    {
                    }
                }

                span.DecorateWebServerSpan(
                    resourceName: resourceName,
                    method: method,
                    host: host,
                    httpUrl: rawUrl,
                    tags,
                    headerTags);

                if (tags is not null)
                {
                    tags.AspNetAction     = action;
                    tags.AspNetController = controller;
                    tags.AspNetArea       = area;
                    tags.AspNetRoute      = route;
                }

                if (newResourceNamesEnabled)
                {
                    // set the resource name in the HttpContext so TracingHttpModule can update root span
                    var httpContext = System.Web.HttpContext.Current;
                    if (httpContext is not null)
                    {
                        httpContext.Items[SharedItems.HttpContextPropagatedResourceNameKey] = resourceName;
                    }
                }
            }
        /// <summary>
        /// Creates a scope used to instrument an MVC action and populates some common details.
        /// </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(ControllerContextStruct controllerContext)
        {
            Scope scope = null;

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

                var httpContext = controllerContext.HttpContext;

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

                var    newResourceNamesEnabled = Tracer.Instance.Settings.RouteTemplateResourceNamesEnabled;
                string host         = httpContext.Request.Headers.Get("Host");
                string httpMethod   = httpContext.Request.HttpMethod.ToUpperInvariant();
                string url          = httpContext.Request.RawUrl.ToLowerInvariant();
                string resourceName = null;

                RouteData            routeData   = controllerContext.RouteData;
                Route                route       = routeData?.Route as Route;
                RouteValueDictionary routeValues = routeData?.Values;
                bool wasAttributeRouted          = false;

                if (route == null && routeData?.Route.GetType().FullName == RouteCollectionRouteTypeName)
                {
                    var routeMatches = routeValues?.GetValueOrDefault("MS_DirectRouteMatches") as List <RouteData>;

                    if (routeMatches?.Count > 0)
                    {
                        // route was defined using attribute routing i.e. [Route("/path/{id}")]
                        // get route and routeValues from the RouteData in routeMatches
                        wasAttributeRouted = true;
                        route       = routeMatches[0].Route as Route;
                        routeValues = routeMatches[0].Values;

                        if (route != null)
                        {
                            var resourceUrl = route.Url?.ToLowerInvariant() ?? string.Empty;
                            if (resourceUrl.FirstOrDefault() != '/')
                            {
                                resourceUrl = string.Concat("/", resourceUrl);
                            }

                            resourceName = $"{httpMethod} {resourceUrl}";
                        }
                    }
                }

                string routeUrl       = route?.Url;
                string areaName       = (routeValues?.GetValueOrDefault("area") as string)?.ToLowerInvariant();
                string controllerName = (routeValues?.GetValueOrDefault("controller") as string)?.ToLowerInvariant();
                string actionName     = (routeValues?.GetValueOrDefault("action") as string)?.ToLowerInvariant();

                if (newResourceNamesEnabled && string.IsNullOrEmpty(resourceName) && !string.IsNullOrEmpty(routeUrl))
                {
                    resourceName = $"{httpMethod} /{routeUrl.ToLowerInvariant()}";
                }

                if (string.IsNullOrEmpty(resourceName) && httpContext.Request.Url != null)
                {
                    var cleanUri = UriHelpers.GetCleanUriPath(httpContext.Request.Url);
                    resourceName = $"{httpMethod} {cleanUri.ToLowerInvariant()}";
                }

                if (string.IsNullOrEmpty(resourceName))
                {
                    // Keep the legacy resource name, just to have something
                    resourceName = $"{httpMethod} {controllerName}.{actionName}";
                }

                // Replace well-known routing tokens
                resourceName =
                    resourceName
                    .Replace("{area}", areaName)
                    .Replace("{controller}", controllerName)
                    .Replace("{action}", actionName);

                if (newResourceNamesEnabled && !wasAttributeRouted && routeValues is not null && route is not null)
                {
                    // Remove unused parameters from conventional route templates
                    // Don't bother with routes defined using attribute routing
                    foreach (var parameter in route.Defaults)
                    {
                        var parameterName = parameter.Key;
                        if (parameterName != "area" &&
                            parameterName != "controller" &&
                            parameterName != "action" &&
                            !routeValues.ContainsKey(parameterName))
                        {
                            resourceName = resourceName.Replace($"/{{{parameterName}}}", string.Empty);
                        }
                    }
                }

                SpanContext propagatedContext = null;
                var         tracer            = Tracer.Instance;
                var         tagsFromHeaders   = Enumerable.Empty <KeyValuePair <string, string> >();

                if (tracer.ActiveScope == null)
                {
                    try
                    {
                        // extract propagated http headers
                        var headers = httpContext.Request.Headers.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 AspNetTags();
                scope = Tracer.Instance.StartActiveWithTags(OperationName, propagatedContext, tags: tags);
                Span span = scope.Span;

                span.DecorateWebServerSpan(
                    resourceName: resourceName,
                    method: httpMethod,
                    host: host,
                    httpUrl: url,
                    tags,
                    tagsFromHeaders);

                tags.AspNetRoute      = routeUrl;
                tags.AspNetArea       = areaName;
                tags.AspNetController = controllerName;
                tags.AspNetAction     = actionName;

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

                if (newResourceNamesEnabled)
                {
                    // set the resource name in the HttpContext so TracingHttpModule can update root span
                    httpContext.Items[SharedConstants.HttpContextPropagatedResourceNameKey] = resourceName;
                }
            }
            catch (Exception ex)
            {
                Log.Error(ex, "Error creating or populating scope.");
            }

            return(scope);
        }
        /// <summary>
        /// Creates a scope used to instrument an MVC action and populates some common details.
        /// </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>
        internal static Scope CreateScope(ControllerContextStruct controllerContext)
        {
            Scope scope = null;

            try
            {
                var httpContext = controllerContext.HttpContext;
                if (httpContext == null)
                {
                    return(null);
                }

                Span span = null;
                // integration enabled, go create a scope!
                var tracer = Tracer.Instance;
                if (tracer.Settings.IsIntegrationEnabled(IntegrationId))
                {
                    var    newResourceNamesEnabled = tracer.Settings.RouteTemplateResourceNamesEnabled;
                    string host         = httpContext.Request.Headers.Get("Host");
                    string httpMethod   = httpContext.Request.HttpMethod.ToUpperInvariant();
                    string url          = httpContext.Request.Url?.ToString(); // Upstream uses RawUrl, ie. the part of the URL following the domain information.
                    string resourceName = null;

                    RouteData            routeData   = controllerContext.RouteData;
                    Route                route       = routeData?.Route as Route;
                    RouteValueDictionary routeValues = routeData?.Values;
                    bool wasAttributeRouted          = false;
                    bool isChildAction = controllerContext.ParentActionViewContext.RouteData?.Values["controller"] is not null;

                    if (isChildAction && newResourceNamesEnabled)
                    {
                        // For child actions, we want to stick to what was requested in the http request.
                        // And the child action being a child, then we have already computed the resourcename.
                        resourceName = httpContext.Items[SharedItems.HttpContextPropagatedResourceNameKey] as string;
                    }

                    if (route == null && routeData?.Route.GetType().FullName == RouteCollectionRouteTypeName)
                    {
                        var routeMatches = routeValues?.GetValueOrDefault("MS_DirectRouteMatches") as List <RouteData>;

                        if (routeMatches?.Count > 0)
                        {
                            // route was defined using attribute routing i.e. [Route("/path/{id}")]
                            // get route and routeValues from the RouteData in routeMatches
                            wasAttributeRouted = true;
                            route       = routeMatches[0].Route as Route;
                            routeValues = routeMatches[0].Values;
                        }
                    }

                    string routeUrl = route?.Url;
                    string areaName;
                    string controllerName;
                    string actionName;

                    if ((wasAttributeRouted || newResourceNamesEnabled) && string.IsNullOrEmpty(resourceName) && !string.IsNullOrEmpty(routeUrl))
                    {
                        resourceName = AspNetResourceNameHelper.CalculateResourceName(
                            httpMethod: httpMethod,
                            routeTemplate: routeUrl,
                            routeValues,
                            defaults: wasAttributeRouted ? null : route.Defaults,
                            out areaName,
                            out controllerName,
                            out actionName,
                            expandRouteTemplates: newResourceNamesEnabled && tracer.Settings.ExpandRouteTemplatesEnabled);
                    }
                    else
                    {
                        // just grab area/controller/action directly
                        areaName       = (routeValues?.GetValueOrDefault("area") as string)?.ToLowerInvariant();
                        controllerName = (routeValues?.GetValueOrDefault("controller") as string)?.ToLowerInvariant();
                        actionName     = (routeValues?.GetValueOrDefault("action") as string)?.ToLowerInvariant();
                    }

                    if (string.IsNullOrEmpty(resourceName))
                    {
                        // Keep the legacy resource name, just to have something
                        resourceName = $"{httpMethod} {controllerName}.{actionName}";
                    }

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

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

                    var tags = new AspNetTags();
                    scope = tracer.StartActiveInternal(resourceName, propagatedContext, tags: tags);
                    span  = scope.Span;

                    span.DecorateWebServerSpan(
                        resourceName: resourceName,
                        method: httpMethod,
                        host: host,
                        httpUrl: url,
                        tags,
                        tagsFromHeaders,
                        httpContext.Request.UserHostAddress);

                    span.LogicScope = isChildAction ? ChildActionOperationName : OperationName;

                    tags.AspNetRoute      = routeUrl;
                    tags.AspNetArea       = areaName;
                    tags.AspNetController = controllerName;
                    tags.AspNetAction     = actionName;

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

                    if (string.IsNullOrEmpty(httpContext.Items[SharedItems.HttpContextPropagatedResourceNameKey] as string))
                    {
                        // set the resource name in the HttpContext so TracingHttpModule can update root span
                        httpContext.Items[SharedItems.HttpContextPropagatedResourceNameKey] = resourceName;
                    }

                    tracer.TracerManager.Telemetry.IntegrationGeneratedSpan(IntegrationId);
                }
            }
            catch (Exception ex)
            {
                Log.Error(ex, "Error creating or populating scope.");
            }

            return(scope);
        }
        /// <summary>
        /// Creates a scope used to instrument an MVC action and populates some common details.
        /// </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(object controllerContext)
        {
            Scope scope = null;

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

                if (controllerContext == null || controllerContext.GetType().FullName != ControllerContextTypeName)
                {
                    return(null);
                }

                var httpContext = controllerContext.GetProperty <HttpContextBase>("HttpContext").GetValueOrDefault();

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

                string host         = httpContext.Request.Headers.Get("Host");
                string httpMethod   = httpContext.Request.HttpMethod.ToUpperInvariant();
                string url          = httpContext.Request.RawUrl.ToLowerInvariant();
                string resourceName = null;

                RouteData            routeData   = controllerContext.GetProperty <RouteData>("RouteData").GetValueOrDefault();
                Route                route       = routeData?.Route as Route;
                RouteValueDictionary routeValues = routeData?.Values;

                if (route == null && routeData?.Route.GetType().FullName == RouteCollectionRouteTypeName)
                {
                    var routeMatches = routeValues?.GetValueOrDefault("MS_DirectRouteMatches") as List <RouteData>;

                    if (routeMatches?.Count > 0)
                    {
                        // route was defined using attribute routing i.e. [Route("/path/{id}")]
                        // get route and routeValues from the RouteData in routeMatches
                        route       = routeMatches[0].Route as Route;
                        routeValues = routeMatches[0].Values;

                        if (route != null)
                        {
                            var resourceUrl = route.Url?.ToLowerInvariant() ?? string.Empty;
                            if (resourceUrl.FirstOrDefault() != '/')
                            {
                                resourceUrl = string.Concat("/", resourceUrl);
                            }

                            resourceName = $"{httpMethod} {resourceUrl}";
                        }
                    }
                }

                if (string.IsNullOrEmpty(resourceName) && httpContext.Request.Url != null)
                {
                    var cleanUri = UriHelpers.GetRelativeUrl(httpContext.Request.Url, tryRemoveIds: true);
                    resourceName = $"{httpMethod} {cleanUri.ToLowerInvariant()}";
                }

                string areaName       = (routeValues?.GetValueOrDefault("area") as string)?.ToLowerInvariant();
                string controllerName = (routeValues?.GetValueOrDefault("controller") as string)?.ToLowerInvariant();
                string actionName     = (routeValues?.GetValueOrDefault("action") as string)?.ToLowerInvariant();

                if (string.IsNullOrEmpty(resourceName))
                {
                    // Keep the legacy resource name, just to have something
                    resourceName = $"{httpMethod} {controllerName}.{actionName}";
                }

                SpanContext propagatedContext = null;
                var         tracer            = Tracer.Instance;
                var         tagsFromHeaders   = Enumerable.Empty <KeyValuePair <string, string> >();

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

                var tags = new AspNetTags();
                scope = Tracer.Instance.StartActiveWithTags(OperationName, propagatedContext, tags: tags);
                Span span = scope.Span;

                // Fail safe to catch templates in routing values
                resourceName =
                    resourceName
                    .Replace("{area}", areaName)
                    .Replace("{controller}", controllerName)
                    .Replace("{action}", actionName);

                span.DecorateWebServerSpan(
                    resourceName: resourceName,
                    method: httpMethod,
                    host: host,
                    httpUrl: url,
                    tags,
                    tagsFromHeaders);

                tags.AspNetRoute      = route?.Url;
                tags.AspNetArea       = areaName;
                tags.AspNetController = controllerName;
                tags.AspNetAction     = actionName;

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

            return(scope);
        }