diff --git a/tracer/src/Datadog.Trace/Configuration/ConfigurationSources/Telemetry/OverrideErrorLog.cs b/tracer/src/Datadog.Trace/Configuration/ConfigurationSources/Telemetry/OverrideErrorLog.cs index 6bbf24938123..17b79d667489 100644 --- a/tracer/src/Datadog.Trace/Configuration/ConfigurationSources/Telemetry/OverrideErrorLog.cs +++ b/tracer/src/Datadog.Trace/Configuration/ConfigurationSources/Telemetry/OverrideErrorLog.cs @@ -106,6 +106,20 @@ public void LogUnsupportedConfiguration(string otelKey, string otelValue, string }); } + public void LogUnsupportedValues(string otelKey, string[] unsupportedValues) + { + EnqueueAction( + (log, metrics) => + { + log.Warning( + "OpenTelemetry configuration {OpenTelemetryConfiguration} contains unsupported values '{UnsupportedValues}', which will be ignored.", + otelKey, + string.Join(",", unsupportedValues)); + OpenTelemetryHelpers.GetConfigurationMetricTags(otelKey, out var openTelemetryConfig, out var datadogConfig); + metrics.RecordCountOpenTelemetryConfigInvalid(datadogConfig, openTelemetryConfig); + }); + } + bool IConfigurationOverrideHandler.TryHandleOverrides( string datadogKey, ConfigurationResult datadogConfigResult, diff --git a/tracer/src/Datadog.Trace/Configuration/TracerSettings.cs b/tracer/src/Datadog.Trace/Configuration/TracerSettings.cs index 5c8d7a390ce2..c06cfd1e5c18 100644 --- a/tracer/src/Datadog.Trace/Configuration/TracerSettings.cs +++ b/tracer/src/Datadog.Trace/Configuration/TracerSettings.cs @@ -529,9 +529,34 @@ not null when string.Equals(value, "otlp", StringComparison.OrdinalIgnoreCase) = var otelPropagation = config .WithKeys(ConfigurationKeys.OpenTelemetry.Propagators) .GetAsClassResult( - validator: injectionValidator, // invalid individual values are rejected later + validator: styles => styles.Any(IsSupportedOtelPropagator), converter: otelConverter); + // Filter the selected value, rather than in the converter, which runs once per source + if (otelPropagation.ConfigurationResult is { Result: { } otelPropagators, IsValid: true }) + { + var propagatorsBySupport = otelPropagators.ToLookup(IsSupportedOtelPropagator); + if (propagatorsBySupport.Contains(false)) + { + ErrorLog.LogUnsupportedValues(otelPropagation.Key, [..propagatorsBySupport[false]]); + otelPropagation = new( + otelPropagation.Telemetry, + otelPropagation.Key, + otelPropagation.RecordValue, + ConfigurationResult.Valid([..propagatorsBySupport[true]], otelPropagation.ConfigurationResult.TelemetryOverride)); + } + } + else if (otelPropagation.ConfigurationResult.IsPresent) + { + // Report once, instead of once per OverrideWith call (inject and extract) + ErrorLog.LogInvalidConfiguration(otelPropagation.Key); + otelPropagation = new( + otelPropagation.Telemetry, + otelPropagation.Key, + otelPropagation.RecordValue, + ConfigurationResult.NotFound()); + } + PropagationStyleInject = config .WithKeys(ConfigurationKeys.PropagationStyleInject) .GetAsClassResult( @@ -1676,6 +1701,10 @@ internal string GetDefaultHttpClientExclusions() } } + private static bool IsSupportedOtelPropagator(string propagator) + => SpanContextPropagatorFactory.GetPropagator(propagator) is not null + || string.Equals(propagator, "none", StringComparison.OrdinalIgnoreCase); + private static ParsingResult UnsupportedOtlpProtocol(string inputValue) { Log.Warning("Unsupported OTLP protocol '{Protocol}'. Supported values are 'grpc', 'http/protobuf' and 'http/json'. Using default: http/protobuf", inputValue); diff --git a/tracer/test/Datadog.Trace.Tests/Configuration/TracerSettingsTests.cs b/tracer/test/Datadog.Trace.Tests/Configuration/TracerSettingsTests.cs index 2e39418e9239..ce5f0dbbf929 100644 --- a/tracer/test/Datadog.Trace.Tests/Configuration/TracerSettingsTests.cs +++ b/tracer/test/Datadog.Trace.Tests/Configuration/TracerSettingsTests.cs @@ -11,6 +11,7 @@ using Datadog.Trace.Configuration; using Datadog.Trace.Configuration.ConfigurationSources.Telemetry; using Datadog.Trace.Configuration.Telemetry; +using Datadog.Trace.Logging; using Datadog.Trace.Sampling; using Datadog.Trace.Tagging; using Datadog.Trace.Telemetry; @@ -18,6 +19,7 @@ using Datadog.Trace.TestHelpers; using FluentAssertions; using FluentAssertions.Execution; +using Moq; using Xunit; namespace Datadog.Trace.Tests.Configuration @@ -650,6 +652,12 @@ public void IsActivityListenerEnabled(string value, string fallbackValue, string [InlineData(null, null, null, "tracecontext,b3,b3multi", new[] { "tracecontext", "b3 single header", "b3multi" })] [InlineData(null, null, null, null, new[] { "Datadog", "tracecontext", "baggage" })] [InlineData(null, null, null, ",", new[] { "Datadog", "tracecontext", "baggage" })] + [InlineData(null, null, null, "jaeger", new[] { "Datadog", "tracecontext", "baggage" })] + [InlineData(null, null, null, "tracecontext,baggage,jaeger,xray", new[] { "tracecontext", "baggage" })] + [InlineData(null, null, null, "b3,ottrace", new[] { "b3 single header" })] + [InlineData(null, null, null, "TRACEContext,B3MULTI", new[] { "TRACEContext", "B3MULTI" })] + [InlineData(null, null, null, "none", new[] { "none" })] + [InlineData(null, null, "tracecontext", "tracecontext,jaeger", new[] { "tracecontext" })] public void PropagationStyleInject(string value, string legacyValue, string fallbackValue, string otelValue, string[] expected) { const string legacyKey = "DD_PROPAGATION_STYLE_INJECT"; @@ -669,14 +677,7 @@ public void PropagationStyleInject(string value, string legacyValue, string fall settings.PropagationStyleInject.Should().BeEquivalentTo(expected); - Count? metric = (value ?? legacyValue ?? fallbackValue, otelValue) switch - { - (null, ",") => Count.OpenTelemetryConfigInvalid, - (not null, not null) => Count.OpenTelemetryConfigHiddenByDatadogConfig, - _ => null, - }; - - errorLog.ShouldHaveExpectedOtelMetric(metric, ConfigurationKeys.OpenTelemetry.Propagators.ToLowerInvariant(), "DD_TRACE_PROPAGATION_STYLE".ToLowerInvariant()); + errorLog.ShouldHaveExpectedOtelMetric(GetExpectedOtelPropagationMetrics(value ?? legacyValue ?? fallbackValue, otelValue)); } } @@ -690,6 +691,12 @@ public void PropagationStyleInject(string value, string legacyValue, string fall [InlineData(null, null, null, "tracecontext,b3,b3multi", new[] { "tracecontext", "b3 single header", "b3multi" })] [InlineData(null, null, null, null, new[] { "Datadog", "tracecontext", "baggage" })] [InlineData(null, null, null, ",", new[] { "Datadog", "tracecontext", "baggage" })] + [InlineData(null, null, null, "jaeger", new[] { "Datadog", "tracecontext", "baggage" })] + [InlineData(null, null, null, "tracecontext,baggage,jaeger,xray", new[] { "tracecontext", "baggage" })] + [InlineData(null, null, null, "b3,ottrace", new[] { "b3 single header" })] + [InlineData(null, null, null, "TRACEContext,B3MULTI", new[] { "TRACEContext", "B3MULTI" })] + [InlineData(null, null, null, "none", new[] { "none" })] + [InlineData(null, null, "tracecontext", "tracecontext,jaeger", new[] { "tracecontext" })] public void PropagationStyleExtract(string value, string legacyValue, string fallbackValue, string otelValue, string[] expected) { const string legacyKey = "DD_PROPAGATION_STYLE_EXTRACT"; @@ -709,17 +716,43 @@ public void PropagationStyleExtract(string value, string legacyValue, string fal settings.PropagationStyleExtract.Should().BeEquivalentTo(expected); - Count? metric = (value ?? legacyValue ?? fallbackValue, otelValue) switch - { - (null, ",") => Count.OpenTelemetryConfigInvalid, - (not null, not null) => Count.OpenTelemetryConfigHiddenByDatadogConfig, - _ => null, - }; - - errorLog.ShouldHaveExpectedOtelMetric(metric, ConfigurationKeys.OpenTelemetry.Propagators.ToLowerInvariant(), "DD_TRACE_PROPAGATION_STYLE".ToLowerInvariant()); + errorLog.ShouldHaveExpectedOtelMetric(GetExpectedOtelPropagationMetrics(value ?? legacyValue ?? fallbackValue, otelValue)); } } + [Theory] + // Values with no supported propagator are reported as invalid instead + [InlineData(null, "not-a-propagator", null)] + [InlineData(null, "jaeger,ottrace", null)] + [InlineData(null, "tracecontext,baggage,Jaeger,b3,XRay", "Jaeger,XRay")] + [InlineData(null, "tracecontext,none", null)] + [InlineData(null, "tracecontext,baggage", null)] + [InlineData("tracecontext", "tracecontext,jaeger", "jaeger")] + public void PropagationStyleUnsupportedOtelPropagatorsWarning(string fallbackValue, string otelValue, string expectedWarningValue) + { + var source = CreateConfigurationSource( + ("DD_TRACE_PROPAGATION_STYLE", fallbackValue), + (ConfigurationKeys.OpenTelemetry.Propagators, otelValue)); + + ShouldWarnAboutUnsupportedOtelPropagators(source, expectedWarningValue); + } + + [Fact] + public void PropagationStyleUnsupportedOtelPropagatorsWarningUsesSelectedSource() + { + // The highest-priority source has no supported propagator, so the lower-priority one is used + var source = new CompositeConfigurationSource + { + new NameValueConfigurationSource(new() { { ConfigurationKeys.OpenTelemetry.Propagators, "jaeger" } }), + new NameValueConfigurationSource(new() { { ConfigurationKeys.OpenTelemetry.Propagators, "tracecontext,xray" } }), + }; + + var settings = ShouldWarnAboutUnsupportedOtelPropagators(source, "xray"); + + settings.PropagationStyleInject.Should().BeEquivalentTo("tracecontext"); + settings.PropagationStyleExtract.Should().BeEquivalentTo("tracecontext"); + } + [Theory] [MemberData(nameof(StringTestCases), "", Strings.AllowEmpty)] public void TraceMethods(string value, string expected) @@ -1366,5 +1399,46 @@ public void ExperimentalFeaturesEnabled_ParsesAndTrimsEntries(string value, stri settings.ExperimentalFeaturesEnabled.Should().BeEquivalentTo(expected); } + + private static IEnumerable<(Count? Metric, string OtelSetting, string DdSetting)> GetExpectedOtelPropagationMetrics(string ddValue, string otelValue) + { + var otelSetting = ConfigurationKeys.OpenTelemetry.Propagators.ToLowerInvariant(); + var ddSetting = "DD_TRACE_PROPAGATION_STYLE".ToLowerInvariant(); + + if (ddValue is not null && otelValue is not null) + { + yield return (Count.OpenTelemetryConfigHiddenByDatadogConfig, otelSetting, ddSetting); + } + else if (otelValue is "," or "jaeger") + { + yield return (Count.OpenTelemetryConfigInvalid, otelSetting, ddSetting); + } + + // Values mixing supported and unsupported propagators + if (otelValue is "tracecontext,baggage,jaeger,xray" or "b3,ottrace" or "tracecontext,jaeger") + { + yield return (Count.OpenTelemetryConfigInvalid, otelSetting, ddSetting); + } + } + + private static TracerSettings ShouldWarnAboutUnsupportedOtelPropagators(IConfigurationSource source, string expectedWarningValue) + { + var errorLog = new OverrideErrorLog(); + var settings = new TracerSettings(source, NullConfigurationTelemetry.Instance, errorLog); + + var log = new Mock(); + errorLog.ProcessAndClearActions(log.Object, new MetricsTelemetryCollector()); + + log.Verify( + l => l.Warning( + It.Is(s => s.Contains("unsupported values")), + ConfigurationKeys.OpenTelemetry.Propagators, + It.Is(v => expectedWarningValue == null || v == expectedWarningValue), + It.IsAny(), + It.IsAny()), + expectedWarningValue is null ? Times.Never() : Times.Once()); + + return settings; + } } }