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