/// <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="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); }
public Scope CreateScope(OutboundHttpArgs args, out HttpTags tags) { var otelTags = new OtelHttpTags(); tags = otelTags; string operationName = "HTTP " + args.HttpMethod; string serviceName = _tracer.Settings.GetServiceName(_tracer, "http-client"); var scope = _tracer.StartActiveWithTags(operationName, tags: tags, serviceName: serviceName, spanId: args.SpanId); scope.Span.Type = SpanTypes.Http; var uri = args.RequestUri; otelTags.HttpMethod = args.HttpMethod; otelTags.HttpUrl = string.Concat(uri.Scheme, Uri.SchemeDelimiter, uri.Authority, uri.PathAndQuery, uri.Fragment); otelTags.InstrumentationName = IntegrationRegistry.GetName(args.IntegrationInfo); return(scope); }
public void CanRoundTripIntegrationIds() { using var scope = new AssertionScope(); var values = IntegrationRegistry.Ids.Values; values.Should().HaveCountGreaterThan(0); foreach (var i in IntegrationRegistry.Ids.Values) { var integrationId = (IntegrationId)i; var name = IntegrationRegistry.GetName(integrationId); IntegrationRegistry.TryGetIntegrationId(name, out var parsedId1).Should().BeTrue(); IntegrationRegistry.TryGetIntegrationId(name.ToUpperInvariant(), out var parsedId2).Should().BeTrue(); IntegrationRegistry.TryGetIntegrationId(name.ToLowerInvariant(), out var parsedId3).Should().BeTrue(); parsedId1.Should().Be(integrationId); parsedId2.Should().Be(integrationId); parsedId3.Should().Be(integrationId); } }
private static Scope CreateDbCommandScope(Tracer tracer, IDbCommand command, IntegrationId integrationId, string dbType, string operationName, string serviceName, ref DbCommandCache.TagsCacheItem tagsFromConnectionString) { if (!tracer.Settings.IsIntegrationEnabled(integrationId) || !tracer.Settings.IsIntegrationEnabled(IntegrationId.AdoNet)) { // integration disabled, don't create a scope, skip this span return(null); } Scope scope = null; try { Span parent = tracer.InternalActiveScope?.Span; if (IsDbAlreadyInstrumented(parent, dbType, command.CommandText)) { // we are already instrumenting this, // don't instrument nested methods that belong to the same stacktrace // e.g. ExecuteReader() -> ExecuteReader(commandBehavior) return(null); } var tags = new SqlTags { DbType = dbType, InstrumentationName = IntegrationRegistry.GetName(integrationId) }; tags.SetAnalyticsSampleRate(integrationId, tracer.Settings, enabledWithGlobalSetting: false); scope = tracer.StartActiveInternal(operationName, tags: tags, serviceName: serviceName); scope.Span.AddTagsFromDbCommand(command); tracer.TracerManager.Telemetry.IntegrationGeneratedSpan(integrationId); } catch (Exception ex) { Log.Error(ex, "Error creating or populating scope."); } return(scope); }
private static Scope CreateDbCommandScope(Tracer tracer, IDbCommand command, IntegrationId integrationId, string dbType, string operationName, string serviceName, ref DbCommandCache.TagsCacheItem tagsFromConnectionString) { if (!tracer.Settings.IsIntegrationEnabled(integrationId) || !tracer.Settings.IsIntegrationEnabled(IntegrationId.AdoNet)) { // integration disabled, don't create a scope, skip this span return(null); } Scope scope = null; try { Span parent = tracer.InternalActiveScope?.Span; if (parent is { Type : SpanTypes.Sql } && parent.GetTag(Tags.DbType) == dbType && parent.ResourceName == command.CommandText) { // we are already instrumenting this, // don't instrument nested methods that belong to the same stacktrace // e.g. ExecuteReader() -> ExecuteReader(commandBehavior) return(null); } var tags = new SqlTags { DbType = dbType, InstrumentationName = IntegrationRegistry.GetName(integrationId), DbName = tagsFromConnectionString.DbName, DbUser = tagsFromConnectionString.DbUser, OutHost = tagsFromConnectionString.OutHost, }; tags.SetAnalyticsSampleRate(integrationId, tracer.Settings, enabledWithGlobalSetting: false); scope = tracer.StartActiveInternal(operationName, tags: tags, serviceName: serviceName); scope.Span.ResourceName = command.CommandText; scope.Span.Type = SpanTypes.Sql; tracer.TracerManager.Telemetry.IntegrationGeneratedSpan(integrationId); }
public Scope CreateScope(OutboundHttpArgs args, out HttpTags tags) { tags = new DatadogHttpTags(); var requestUri = args.RequestUri; var httpMethod = args.HttpMethod; string serviceName = _tracer.Settings.GetServiceName(_tracer, "http-client"); var scope = _tracer.StartActiveWithTags("http.request", tags: tags, serviceName: serviceName, spanId: args.SpanId); scope.Span.Type = SpanTypes.Http; tags.HttpMethod = httpMethod; tags.HttpUrl = UriHelpers.CleanUri(requestUri, removeScheme: false, tryRemoveIds: false); string resourceUrl = UriHelpers.CleanUri(requestUri, removeScheme: true, tryRemoveIds: true); scope.Span.ResourceName = $"{httpMethod} {resourceUrl}"; var integrationId = args.IntegrationInfo; tags.InstrumentationName = IntegrationRegistry.GetName(integrationId); tags.SetAnalyticsSampleRate(integrationId, _tracer.Settings, enabledWithGlobalSetting: false); return(scope); }
public static ImmutableDirectLogSubmissionSettings Create( string?host, string?source, string?intakeUrl, string?apiKey, DirectSubmissionLogLevel minimumLevel, IDictionary <string, string> globalTags, ICollection <string> enabledLogShippingIntegrations, BatchingSinkOptions batchingOptions) { if (enabledLogShippingIntegrations.Count == 0) { // not trying to enable log submission, so don't log any errors and create a _null_ implementation return(CreateNullSettings()); } var isEnabled = true; var validationErrors = new List <string>(); if (string.IsNullOrWhiteSpace(host)) { isEnabled = false; validationErrors.Add($"Missing required setting '{ConfigurationKeys.DirectLogSubmission.Host}'."); } if (string.IsNullOrWhiteSpace(source)) { isEnabled = false; validationErrors.Add($"Missing required setting '{ConfigurationKeys.DirectLogSubmission.Source}'."); } if (!Uri.TryCreate(intakeUrl, UriKind.Absolute, out var intakeUri)) { isEnabled = false; validationErrors.Add($"The intake url '{intakeUrl}' was not a valid URL."); } if (string.IsNullOrWhiteSpace(apiKey)) { isEnabled = false; validationErrors.Add($"Missing required settings '{ConfigurationKeys.ApiKey}'."); } var stringifiedTags = StringifyGlobalTags(globalTags); var enabledIntegrations = new bool[IntegrationRegistry.Ids.Count]; var enabledIntegrationNames = new List <string>(SupportedIntegrations.Length); foreach (var integrationName in enabledLogShippingIntegrations) { if (!IntegrationRegistry.TryGetIntegrationId(integrationName, out var integrationId)) { validationErrors.Add( "Unknown integration: " + integrationName + ". Use a valid logs integration name: " + string.Join(", ", SupportedIntegrations.Select(x => IntegrationRegistry.GetName(x)))); continue; } if (!SupportedIntegrations.Contains(integrationId)) { validationErrors.Add( "Integration: " + integrationName + " is not a supported direct log submission integration. " + "Use one of " + string.Join(", ", SupportedIntegrations.Select(x => IntegrationRegistry.GetName(x)))); continue; } if (!enabledIntegrations[(int)integrationId]) { enabledIntegrationNames.Add(IntegrationRegistry.GetName(integrationId)); enabledIntegrations[(int)integrationId] = true; } } return(new ImmutableDirectLogSubmissionSettings( host: host ?? string.Empty, source: source ?? string.Empty, globalTags: stringifiedTags, intakeUrl: intakeUri !, apiKey: apiKey ?? string.Empty, isEnabled: isEnabled, minimumLevel: minimumLevel, enabledIntegrations: enabledIntegrations, validationErrors, enabledIntegrationNames, batchingOptions)); }
public void TryGetIntegrationId_ReturnsFalseForUnknownIntegration() { IntegrationRegistry.TryGetIntegrationId("blobby", out _).Should().BeFalse(); }