diff --git a/docs/file-based-configuration.md b/docs/file-based-configuration.md index e779ba1e85..847287e155 100644 --- a/docs/file-based-configuration.md +++ b/docs/file-based-configuration.md @@ -82,3 +82,24 @@ resource: process: # Detects process-level attributes (process.*) processruntime: # Detects process runtime attributes (process.runtime.*) ``` + +### Propagator Configuration + +You can configure text map context propagators directly in YAML or via the +`OTEL_PROPAGATORS` environment variable. +For more details and updates, see: [Propagators list and documentation](config.md/#propagators) +To disable a propagator, comment out or remove its corresponding entry. + +``` yaml +# If no propagators are specified, none will be added automatically. +propagator: + # Composite propagators are evaluated together. + # Entries from .composite_list are appended here (duplicates are filtered out). + composite: + tracecontext: # W3C Trace Context propagator + baggage: # W3C Baggage propagator + b3: # B3 single-header propagator + b3multi: # B3 multi-header propagator + # Alternatively, configure via a comma-separated list (same format as OTEL_PROPAGATORS). + composite_list: ${OTEL_PROPAGATORS} +``` diff --git a/src/OpenTelemetry.AutoInstrumentation/Configurations/EnvironmentConfigurationSdkHelper.cs b/src/OpenTelemetry.AutoInstrumentation/Configurations/EnvironmentConfigurationSdkHelper.cs index eb29c16bd5..17c022ca9e 100644 --- a/src/OpenTelemetry.AutoInstrumentation/Configurations/EnvironmentConfigurationSdkHelper.cs +++ b/src/OpenTelemetry.AutoInstrumentation/Configurations/EnvironmentConfigurationSdkHelper.cs @@ -12,7 +12,7 @@ public static void UseEnvironmentVariables(SdkSettings settings) SetSdkTextMapPropagator(settings.Propagators); } - private static void SetSdkTextMapPropagator(IList settingsPropagators) + private static void SetSdkTextMapPropagator(IReadOnlyList settingsPropagators) { if (settingsPropagators.Count == 0) { @@ -43,18 +43,13 @@ private static void SetSdkTextMapPropagator(IList settingsPropagator private static TextMapPropagator GetTextMapPropagator(Propagator propagator) { - switch (propagator) + return propagator switch { - case Propagator.W3CTraceContext: - return new TraceContextPropagator(); - case Propagator.W3CBaggage: - return new BaggagePropagator(); - case Propagator.B3Multi: - return new Extensions.Propagators.B3Propagator(singleHeader: false); - case Propagator.B3Single: - return new Extensions.Propagators.B3Propagator(singleHeader: true); - } - - throw new ArgumentOutOfRangeException(nameof(propagator), propagator, "Propagator has an unexpected value."); + Propagator.W3CTraceContext => new TraceContextPropagator(), + Propagator.W3CBaggage => new BaggagePropagator(), + Propagator.B3Multi => new Extensions.Propagators.B3Propagator(singleHeader: false), + Propagator.B3Single => new Extensions.Propagators.B3Propagator(singleHeader: true), + _ => throw new ArgumentOutOfRangeException(nameof(propagator), propagator, "Propagator has an unexpected value."), + }; } } diff --git a/src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/PropagatorConfiguration.cs b/src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/PropagatorConfiguration.cs new file mode 100644 index 0000000000..afecedab15 --- /dev/null +++ b/src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/PropagatorConfiguration.cs @@ -0,0 +1,49 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +using Vendors.YamlDotNet.Serialization; + +namespace OpenTelemetry.AutoInstrumentation.Configurations.FileBasedConfiguration; + +internal class PropagatorConfiguration +{ + /// + /// Gets or sets the composite propagator configuration. + /// + [YamlMember(Alias = "composite")] + public Dictionary? Composite { get; set; } + + /// + /// Gets or sets the composite list for the propagator. + /// + [YamlMember(Alias = "composite_list")] + public string? CompositeList { get; set; } + + public IReadOnlyList GetEnabledPropagators() + { + if (Composite is null && string.IsNullOrEmpty(CompositeList)) + { + return []; + } + + var propagatorNames = (Composite?.Keys ?? Enumerable.Empty()) + .Concat(!string.IsNullOrEmpty(CompositeList) + ? CompositeList!.Split(Constants.ConfigurationValues.Separator) + : []) + .Distinct(); + + var propagators = propagatorNames + .Select(name => name switch + { + Constants.ConfigurationValues.Propagators.W3CTraceContext => Propagator.W3CTraceContext, + Constants.ConfigurationValues.Propagators.W3CBaggage => Propagator.W3CBaggage, + Constants.ConfigurationValues.Propagators.B3Multi => Propagator.B3Multi, + Constants.ConfigurationValues.Propagators.B3Single => Propagator.B3Single, + _ => null + }) + .OfType() + .ToList(); + + return propagators; + } +} diff --git a/src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/YamlConfiguration.cs b/src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/YamlConfiguration.cs index 62a3b94821..2d072bd787 100644 --- a/src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/YamlConfiguration.cs +++ b/src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/YamlConfiguration.cs @@ -23,6 +23,13 @@ internal class YamlConfiguration [YamlMember(Alias = "resource")] public ResourceConfiguration? Resource { get; set; } + /// + /// Gets or sets the text map context propagator configuration. + /// If omitted, a noop propagator is used. + /// + [YamlMember(Alias = "propagator")] + public PropagatorConfiguration? Propagator { get; set; } + /// /// Gets or sets a value indicating whether the SDK is disabled. /// If omitted or null, false is used. diff --git a/src/OpenTelemetry.AutoInstrumentation/Configurations/SdkSettings.cs b/src/OpenTelemetry.AutoInstrumentation/Configurations/SdkSettings.cs index 98fdb8c10c..ce6aa61317 100644 --- a/src/OpenTelemetry.AutoInstrumentation/Configurations/SdkSettings.cs +++ b/src/OpenTelemetry.AutoInstrumentation/Configurations/SdkSettings.cs @@ -1,6 +1,7 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 +using OpenTelemetry.AutoInstrumentation.Configurations.FileBasedConfiguration; using OpenTelemetry.AutoInstrumentation.Logging; namespace OpenTelemetry.AutoInstrumentation.Configurations; @@ -15,14 +16,19 @@ internal class SdkSettings : Settings /// /// Gets the list of propagators to be used. /// - public IList Propagators { get; private set; } = new List(); + public IReadOnlyList Propagators { get; private set; } = []; protected override void OnLoadEnvVar(Configuration configuration) { Propagators = ParsePropagator(configuration); } - private static IList ParsePropagator(Configuration configuration) + protected override void OnLoadFile(YamlConfiguration configuration) + { + Propagators = configuration.Propagator?.GetEnabledPropagators() ?? []; + } + + private static List ParsePropagator(Configuration configuration) { var propagatorEnvVar = configuration.GetString(ConfigurationKeys.Sdk.Propagators); var propagators = new List(); diff --git a/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/FileBased/FilebasedSdkSettingsTests.cs b/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/FileBased/FilebasedSdkSettingsTests.cs new file mode 100644 index 0000000000..7b272bad9f --- /dev/null +++ b/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/FileBased/FilebasedSdkSettingsTests.cs @@ -0,0 +1,105 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +using OpenTelemetry.AutoInstrumentation.Configurations; +using OpenTelemetry.AutoInstrumentation.Configurations.FileBasedConfiguration; +using Xunit; + +namespace OpenTelemetry.AutoInstrumentation.Tests.Configurations.FileBased; + +public class FilebasedSdkSettingsTests +{ + [Fact] + public void LoadFile_CompositeOnlyPropagators() + { + var propagator = new PropagatorConfiguration + { + Composite = new Dictionary + { + { "tracecontext", new object() }, + { "baggage", new object() } + } + }; + + var conf = new YamlConfiguration { Propagator = propagator }; + var settings = new SdkSettings(); + + settings.LoadFile(conf); + + Assert.Equal(2, settings.Propagators.Count); + Assert.Contains(Propagator.W3CTraceContext, settings.Propagators); + Assert.Contains(Propagator.W3CBaggage, settings.Propagators); + } + + [Fact] + public void LoadFile_CompositeListOnlyPropagators() + { + var propagator = new PropagatorConfiguration + { + CompositeList = "b3multi,b3" + }; + + var conf = new YamlConfiguration { Propagator = propagator }; + var settings = new SdkSettings(); + + settings.LoadFile(conf); + + Assert.Equal(2, settings.Propagators.Count); + Assert.Contains(Propagator.B3Multi, settings.Propagators); + Assert.Contains(Propagator.B3Single, settings.Propagators); + } + + [Fact] + public void LoadFile_CompositeAndCompositeList_AreMergedWithoutDuplicates() + { + var propagator = new PropagatorConfiguration + { + Composite = new Dictionary + { + { "tracecontext", new object() }, + { "baggage", new object() } + }, + CompositeList = "tracecontext,b3" + }; + + var conf = new YamlConfiguration { Propagator = propagator }; + var settings = new SdkSettings(); + + settings.LoadFile(conf); + + Assert.Equal(3, settings.Propagators.Count); + Assert.Contains(Propagator.W3CTraceContext, settings.Propagators); + Assert.Contains(Propagator.W3CBaggage, settings.Propagators); + Assert.Contains(Propagator.B3Single, settings.Propagators); + } + + [Fact] + public void LoadFile_GetEnabledPropagators_Empty() + { + var propagator = new PropagatorConfiguration(); + + var conf = new YamlConfiguration { Propagator = propagator }; + var settings = new SdkSettings(); + + settings.LoadFile(conf); + + Assert.Empty(settings.Propagators); + } + + [Fact] + public void LoadFile_GetEnabledPropagators_UnknownValues_AreIgnored() + { + var propagator = new PropagatorConfiguration + { + CompositeList = "invalid,b3multi" + }; + + var conf = new YamlConfiguration { Propagator = propagator }; + var settings = new SdkSettings(); + + settings.LoadFile(conf); + + Assert.Single(settings.Propagators); + Assert.Equal(Propagator.B3Multi, settings.Propagators[0]); + } +} diff --git a/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/FileBased/Files/TestPropagatorFile.yaml b/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/FileBased/Files/TestPropagatorFile.yaml new file mode 100644 index 0000000000..67f0b924c1 --- /dev/null +++ b/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/FileBased/Files/TestPropagatorFile.yaml @@ -0,0 +1,9 @@ +file_format: "1.0-rc.1" + +propagator: + composite: + tracecontext: + baggage: + b3: + b3multi: + diff --git a/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/FileBased/Files/TestPropagatorFileEnvVars.yaml b/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/FileBased/Files/TestPropagatorFileEnvVars.yaml new file mode 100644 index 0000000000..813611f2d6 --- /dev/null +++ b/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/FileBased/Files/TestPropagatorFileEnvVars.yaml @@ -0,0 +1,4 @@ +file_format: "1.0-rc.1" + +propagator: + composite_list: ${OTEL_PROPAGATORS} diff --git a/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/FileBased/Parser/ParserPropagatorTests.cs b/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/FileBased/Parser/ParserPropagatorTests.cs new file mode 100644 index 0000000000..06eed5b1d5 --- /dev/null +++ b/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/FileBased/Parser/ParserPropagatorTests.cs @@ -0,0 +1,46 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +using Xunit; +using YamlParser = OpenTelemetry.AutoInstrumentation.Configurations.FileBasedConfiguration.Parser.Parser; + +namespace OpenTelemetry.AutoInstrumentation.Tests.Configurations.FileBased.Parser; + +[Collection("Non-Parallel Collection")] +public class ParserPropagatorTests +{ + [Fact] + public void Parse_FullConfigYaml_ShouldPopulateModelCorrectly() + { + var config = YamlParser.ParseYaml("Configurations/FileBased/Files/TestPropagatorFile.yaml"); + + Assert.NotNull(config); + Assert.NotNull(config.Propagator); + Assert.NotNull(config.Propagator.Composite); + var composite = config.Propagator.Composite; + Assert.Null(config.Propagator.CompositeList); + + string[] expectedPropagators = [ + "tracecontext", "baggage", "b3", "b3multi", + ]; + + foreach (var expected in expectedPropagators) + { + Assert.Contains(expected, composite.Keys); + } + } + + [Fact] + public void Parse_EnvVarYaml_ShouldPopulateModelCompletely() + { + Environment.SetEnvironmentVariable("OTEL_PROPAGATORS", "tracecontext,baggage,b3,b3multi"); + + var config = YamlParser.ParseYaml("Configurations/FileBased/Files/TestPropagatorFileEnvVars.yaml"); + + Assert.NotNull(config); + Assert.NotNull(config.Propagator); + Assert.NotNull(config.Propagator.CompositeList); + Assert.Null(config.Propagator.Composite); + Assert.Equal("tracecontext,baggage,b3,b3multi", config.Propagator.CompositeList); + } +} diff --git a/test/OpenTelemetry.AutoInstrumentation.Tests/OpenTelemetry.AutoInstrumentation.Tests.csproj b/test/OpenTelemetry.AutoInstrumentation.Tests/OpenTelemetry.AutoInstrumentation.Tests.csproj index e34a9eb301..73965ceaf8 100644 --- a/test/OpenTelemetry.AutoInstrumentation.Tests/OpenTelemetry.AutoInstrumentation.Tests.csproj +++ b/test/OpenTelemetry.AutoInstrumentation.Tests/OpenTelemetry.AutoInstrumentation.Tests.csproj @@ -15,12 +15,18 @@ PreserveNewest + + PreserveNewest + PreserveNewest PreserveNewest + + PreserveNewest + PreserveNewest