Esempio n. 1
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);
        }
Esempio 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>
        /// <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);
        }
Esempio n. 4
0
        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);
            }
        }
Esempio n. 5
0
        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);
            }
Esempio n. 7
0
        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));
        }
Esempio n. 9
0
 public void TryGetIntegrationId_ReturnsFalseForUnknownIntegration()
 {
     IntegrationRegistry.TryGetIntegrationId("blobby", out _).Should().BeFalse();
 }