/// <summary>
        /// Creates a scope for outbound http requests and populates some common details.
        /// </summary>
        /// <param name="tracer">The tracer instance to use to create the new scope.</param>
        /// <param name="httpMethod">The HTTP method used by the request.</param>
        /// <param name="requestUri">The URI requested by the request.</param>
        /// <param name="integrationName">The name of the integration creating this scope.</param>
        /// <param name="propagatedSpanId">The propagated span ID.</param>
        /// <returns>A new pre-populated scope.</returns>
        public static Scope CreateOutboundHttpScope(Tracer tracer, string httpMethod, Uri requestUri, string integrationName, ulong?propagatedSpanId = null)
        {
            if (!tracer.Settings.IsIntegrationEnabled(integrationName))
            {
                // integration disabled, don't create a scope, skip this trace
                return(null);
            }

            Scope scope = null;

            try
            {
                if (GetActiveHttpScope(tracer, httpMethod, requestUri) != null)
                {
                    // we are already instrumenting this,
                    // don't instrument nested methods that belong to the same stacktrace
                    // e.g. HttpClientHandler.SendAsync() -> SocketsHttpHandler.SendAsync()
                    return(null);
                }

                var spanName = httpMethod ?? OperationName;
                if (tracer.Settings.AppendUrlPathToName)
                {
                    spanName += ":" + requestUri.AbsolutePath;
                }

                scope = tracer.StartActive(spanName, spanId: propagatedSpanId);
                var span = scope.Span;

                span.ServiceName = tracer.DefaultServiceName;

                span.SetTag(Tags.SpanKind, SpanKinds.Client);
                // Only the span responsible for propagated context should have client span.kind
                if (span.Context.Parent != null)
                {
                    var parentSpan = ((SpanContext)span.Context.Parent).Span;
                    var spanKind   = parentSpan.GetTag(Tags.SpanKind);
                    if (SpanKinds.Client.Equals(spanKind))
                    {
                        parentSpan.SetTag(Tags.SpanKind, null);
                    }
                }

                span.SetTag(Tags.HttpMethod, httpMethod?.ToUpperInvariant());
                span.SetTag(Tags.HttpUrl, UriHelpers.CleanUri(requestUri, removeScheme: false, tryRemoveIds: false));
                span.SetTag(Tags.InstrumentationName, integrationName);

                // set analytics sample rate if enabled
                var analyticsSampleRate = tracer.Settings.GetIntegrationAnalyticsSampleRate(integrationName, enabledWithGlobalSetting: false);
                span.SetMetric(Tags.Analytics, analyticsSampleRate);
            }
            catch (Exception ex)
            {
                Log.Error(ex, "Error creating or populating scope.");
            }

            // always returns the scope, even if it's null because we couldn't create it,
            // or we couldn't populate it completely (some tags is better than no tags)
            return(scope);
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Creates a scope for outbound http requests and populates some common details.
        /// </summary>
        /// <param name="tracer">The tracer instance to use to create the new scope.</param>
        /// <param name="httpMethod">The HTTP method used by the request.</param>
        /// <param name="requestUri">The URI requested by the request.</param>
        /// <param name="integrationId">The id of the integration creating this scope.</param>
        /// <param name="tags">The tags associated to the scope</param>
        /// <param name="traceId">The trace id - this id will be ignored if there's already an active trace</param>
        /// <param name="spanId">The span id</param>
        /// <param name="startTime">The start time that should be applied to the span</param>
        /// <param name="addToTraceContext">Set to false if the span is meant to be discarded. In that case, the span won't be added to the TraceContext.</param>
        /// <returns>A new pre-populated scope.</returns>
        internal static Span CreateInactiveOutboundHttpSpan(Tracer tracer, string httpMethod, Uri requestUri, IntegrationId integrationId, out HttpTags tags, TraceId?traceId, ulong?spanId, DateTimeOffset?startTime, bool addToTraceContext)
        {
            tags = null;

            if (!tracer.Settings.IsIntegrationEnabled(integrationId) || PlatformHelpers.PlatformStrategy.ShouldSkipClientSpan(tracer.InternalActiveScope) || HttpBypassHelper.UriContainsAnyOf(requestUri, tracer.Settings.HttpClientExcludedUrlSubstrings))
            {
                // integration disabled, don't create a scope, skip this trace
                return(null);
            }

            Span span = null;

            try
            {
                if (GetActiveHttpScope(tracer) != null)
                {
                    // we are already instrumenting this,
                    // don't instrument nested methods that belong to the same stacktrace
                    // e.g. HttpClientHandler.SendAsync() -> SocketsHttpHandler.SendAsync()
                    return(null);
                }

                string resourceUrl = requestUri != null?UriHelpers.CleanUri(requestUri, removeScheme : true, tryRemoveIds : true) : null;

                string httpUrl = requestUri != null?UriHelpers.CleanUri(requestUri, removeScheme : false, tryRemoveIds : false) : null;

                tags = new HttpTags();

                // Upstream uses "http.request" for the span name but following legacy version and the
                // OpenTelemetry specification we use the capitalized method as the span name.
                string uppercaseHttpMethod = httpMethod?.ToUpperInvariant();

                string serviceName = tracer.Settings.GetServiceName(tracer, ServiceName);
                span = tracer.StartSpan(uppercaseHttpMethod, tags, serviceName: serviceName, traceId: traceId, spanId: spanId, startTime: startTime, addToTraceContext: addToTraceContext);

                span.Type         = SpanTypes.Http;
                span.ResourceName = $"{httpMethod} {resourceUrl}";
                span.LogicScope   = OperationName;

                tags.HttpMethod          = uppercaseHttpMethod;
                tags.HttpUrl             = httpUrl;
                tags.InstrumentationName = IntegrationRegistry.GetName(integrationId);

                tags.SetAnalyticsSampleRate(integrationId, tracer.Settings, enabledWithGlobalSetting: false);

                if (!addToTraceContext && span.Context.TraceContext.SamplingPriority == null)
                {
                    // If we don't add the span to the trace context, then we need to manually call the sampler
                    span.Context.TraceContext.SetSamplingPriority(tracer.TracerManager.Sampler?.GetSamplingPriority(span));
                }
            }
            catch (Exception ex)
            {
                Log.Error(ex, "Error creating or populating span.");
            }

            // always returns the span, even if it's null because we couldn't create it,
            // or we couldn't populate it completely (some tags is better than no tags)
            return(span);
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Creates a scope for outbound http requests and populates some common details.
        /// </summary>
        /// <param name="tracer">The tracer instance to use to create the new scope.</param>
        /// <param name="httpMethod">The HTTP method used by the request.</param>
        /// <param name="requestUri">The URI requested by the request.</param>
        /// <param name="integrationName">The name of the integration creating this scope.</param>
        /// <returns>A new pre-populated scope.</returns>
        public static Scope CreateOutboundHttpScope(Tracer tracer, string httpMethod, Uri requestUri, string integrationName)
        {
            if (!tracer.Settings.IsIntegrationEnabled(integrationName))
            {
                // integration disabled, don't create a scope, skip this trace
                return(null);
            }

            Scope scope = null;

            try
            {
                Span parent = tracer.ActiveScope?.Span;

                if (parent != null &&
                    parent.Type == SpanTypes.Http &&
                    parent.GetTag(Tags.InstrumentationName) != null)
                {
                    // we are already instrumenting this,
                    // don't instrument nested methods that belong to the same stacktrace
                    // e.g. HttpClientHandler.SendAsync() -> SocketsHttpHandler.SendAsync()
                    return(null);
                }

                string resourceUrl = requestUri != null?UriHelpers.CleanUri(requestUri, removeScheme : true, tryRemoveIds : true) : null;

                string httpUrl = requestUri != null?UriHelpers.CleanUri(requestUri, removeScheme : false, tryRemoveIds : false) : null;

                scope = tracer.StartActive(OperationName, serviceName: $"{tracer.DefaultServiceName}-{ServiceName}");
                var span = scope.Span;

                span.Type         = SpanTypes.Http;
                span.ResourceName = $"{httpMethod} {resourceUrl}";

                span.SetTag(Tags.SpanKind, SpanKinds.Client);
                span.SetTag(Tags.HttpMethod, httpMethod?.ToUpperInvariant());
                span.SetTag(Tags.HttpUrl, httpUrl);
                span.SetTag(Tags.InstrumentationName, integrationName);

                // set analytics sample rate if enabled
                if (integrationName != null)
                {
                    var analyticsSampleRate = tracer.Settings.GetIntegrationAnalyticsSampleRate(integrationName, enabledWithGlobalSetting: false);
                    span.SetMetric(Tags.Analytics, analyticsSampleRate);
                }
            }
            catch (Exception ex)
            {
                Log.Error(ex, "Error creating or populating scope.");
            }

            // always returns the scope, even if it's null because we couldn't create it,
            // or we couldn't populate it completely (some tags is better than no tags)
            return(scope);
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Creates a scope for outbound http requests and populates some common details.
        /// </summary>
        /// <param name="tracer">The tracer instance to use to create the new scope.</param>
        /// <param name="httpMethod">The HTTP method used by the request.</param>
        /// <param name="requestUri">The URI requested by the request.</param>
        /// <param name="integrationId">The id of the integration creating this scope.</param>
        /// <param name="tags">The tags associated to the scope</param>
        /// <returns>A new pre-populated scope.</returns>
        public static Scope CreateOutboundHttpScope(Tracer tracer, string httpMethod, Uri requestUri, IntegrationInfo integrationId, out HttpTags tags)
        {
            tags = null;

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

            Scope scope = null;

            try
            {
                Span parent = tracer.ActiveScope?.Span;

                if (parent != null &&
                    parent.Type == SpanTypes.Http &&
                    parent.GetTag(Tags.InstrumentationName) != null)
                {
                    // we are already instrumenting this,
                    // don't instrument nested methods that belong to the same stacktrace
                    // e.g. HttpClientHandler.SendAsync() -> SocketsHttpHandler.SendAsync()
                    return(null);
                }

                string resourceUrl = requestUri != null?UriHelpers.CleanUri(requestUri, removeScheme : true, tryRemoveIds : true) : null;

                string httpUrl = requestUri != null?UriHelpers.CleanUri(requestUri, removeScheme : false, tryRemoveIds : false) : null;

                tags = new HttpTags();

                string serviceName = tracer.Settings.GetServiceName(tracer, ServiceName);
                scope = tracer.StartActiveWithTags(OperationName, tags: tags, serviceName: serviceName);
                var span = scope.Span;

                span.Type         = SpanTypes.Http;
                span.ResourceName = $"{httpMethod} {resourceUrl}";

                tags.HttpMethod          = httpMethod?.ToUpperInvariant();
                tags.HttpUrl             = httpUrl;
                tags.InstrumentationName = IntegrationRegistry.GetName(integrationId);

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

            // always returns the scope, even if it's null because we couldn't create it,
            // or we couldn't populate it completely (some tags is better than no tags)
            return(scope);
        }
        /// <summary>
        /// Gets a scope for outbound HTTP requests if one is the current active scope.
        /// </summary>
        /// <param name="tracer">The tracer instance to use to create the new scope.</param>
        /// <param name="httpMethod">The HTTP method used by the request.</param>
        /// <param name="requestUri">The URI requested by the request.</param>
        /// <returns>
        /// A scope for the outbound HTTP requests if one is current active, null if the
        /// HTTP call was already instrumented or the current scope is not for an HTTP
        /// request.
        /// </returns>
        public static Scope GetActiveHttpScope(Tracer tracer, string httpMethod, Uri requestUri)
        {
            var scope = tracer.ActiveScope;

            var parent = scope?.Span;

            if (parent != null &&
                StringComparer.OrdinalIgnoreCase.Equals(parent.GetTag(Tags.SpanKind), SpanKinds.Client) &&
                StringComparer.OrdinalIgnoreCase.Equals(parent.GetTag(Tags.HttpMethod), httpMethod) &&
                StringComparer.OrdinalIgnoreCase.Equals(parent.GetTag(Tags.HttpUrl), UriHelpers.CleanUri(requestUri, removeScheme: false, tryRemoveIds: false)))
            {
                return(scope);
            }

            return(null);
        }
Ejemplo n.º 6
0
        /// <summary>
        /// Creates a scope for outbound http requests and populates some common details.
        /// </summary>
        /// <param name="tracer">The tracer instance to use to create the new scope.</param>
        /// <param name="httpMethod">The HTTP method used by the request.</param>
        /// <param name="requestUri">The URI requested by the request.</param>
        /// <param name="integrationName">The name of the integration creating this scope.</param>
        /// <returns>A new pre-populated scope.</returns>
        public static Scope CreateOutboundHttpScope(Tracer tracer, string httpMethod, Uri requestUri, string integrationName)
        {
            if (!tracer.Settings.IsIntegrationEnabled(integrationName))
            {
                // integration disabled, don't create a scope, skip this trace
                return(null);
            }

            Scope scope = null;

            try
            {
                scope = tracer.StartActive(OperationName);
                var span = scope.Span;

                span.Type        = SpanTypes.Http;
                span.ServiceName = $"{tracer.DefaultServiceName}-{ServiceName}";

                span.ResourceName = string.Join(
                    " ",
                    httpMethod,
                    UriHelpers.CleanUri(requestUri, removeScheme: true, tryRemoveIds: true));

                span.SetTag(Tags.SpanKind, SpanKinds.Client);
                span.SetTag(Tags.HttpMethod, httpMethod?.ToUpperInvariant());
                span.SetTag(Tags.HttpUrl, UriHelpers.CleanUri(requestUri, removeScheme: false, tryRemoveIds: false));
                span.SetTag(Tags.InstrumentationName, integrationName);

                // set analytics sample rate if enabled
                var analyticsSampleRate = tracer.Settings.GetIntegrationAnalyticsSampleRate(integrationName, enabledWithGlobalSetting: false);
                span.SetMetric(Tags.Analytics, analyticsSampleRate);
            }
            catch (Exception ex)
            {
                Log.Error(ex, "Error creating or populating scope.");
            }

            // always returns the scope, even if it's null because we couldn't create it,
            // or we couldn't populate it completely (some tags is better than no tags)
            return(scope);
        }