From ee14861b33933cfef7a205c0ab986d6dc74d9a47 Mon Sep 17 00:00:00 2001 From: Yevhenii Solomchenko Date: Fri, 24 Oct 2025 09:26:37 +0200 Subject: [PATCH 1/3] Add file-based sampler configuration --- CHANGELOG.md | 1 + docs/file-based-configuration.md | 32 +++- .../EnvironmentConfigurationTracerHelper.cs | 12 +- .../AlwaysOffSamplerConfig.cs | 11 ++ .../AlwaysOnSamplerConfig.cs | 11 ++ .../ParentBasedSamplerConfig.cs | 26 +++ .../FileBasedConfiguration/SamplerConfig.cs | 26 +++ .../FileBasedConfiguration/SamplerFactory.cs | 171 ++++++++++++++++++ .../TraceIdRatioSamplerConfig.cs | 12 ++ .../TracerProviderConfiguration.cs | 3 + .../Configurations/TracerSettings.cs | 8 + .../FileBased/FilebasedTracesSettingsTests.cs | 51 ++++++ .../FileBased/Files/TestTracesFile.yaml | 14 +- .../FileBased/Parser/ParserTracesTests.cs | 14 ++ 14 files changed, 387 insertions(+), 5 deletions(-) create mode 100644 src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/AlwaysOffSamplerConfig.cs create mode 100644 src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/AlwaysOnSamplerConfig.cs create mode 100644 src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/ParentBasedSamplerConfig.cs create mode 100644 src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/SamplerConfig.cs create mode 100644 src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/SamplerFactory.cs create mode 100644 src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/TraceIdRatioSamplerConfig.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 585cd9cff5..ded7736f39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ This component adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.h ### Added - Configuration based instrumentation. +- Add support for configuring tracer sampler via file-based configuration. ### Changed diff --git a/docs/file-based-configuration.md b/docs/file-based-configuration.md index c5ee3fd538..4400e23945 100644 --- a/docs/file-based-configuration.md +++ b/docs/file-based-configuration.md @@ -56,7 +56,7 @@ tracer_provider: processors: # Batch processor for OTLP HTTP - batch: - # Configure delay interval (in milliseconds) between two consecutive exports. + # Configure delay interval (in milliseconds) between two consecutive exports. # Value must be non-negative. # If omitted or null, 5000 is used. schedule_delay: 5000 @@ -110,6 +110,36 @@ tracer_provider: - simple: exporter: console: + + # Configure the sampler. If omitted, parent based sampler with a root of always_on is used. + sampler: + # Configure sampler to be parent_based. + parent_based: + # Configure root sampler. + # If omitted or null, always_on is used. + root: + # Configure sampler to be always_on. + always_on: + # Configure remote_parent_sampled sampler. + # If omitted or null, always_on is used. + remote_parent_sampled: + # Configure sampler to be always_on. + always_on: + # Configure remote_parent_not_sampled sampler. + # If omitted or null, always_off is used. + remote_parent_not_sampled: + # Configure sampler to be always_off. + always_off: + # Configure local_parent_sampled sampler. + # If omitted or null, always_on is used. + local_parent_sampled: + # Configure sampler to be always_on. + always_on: + # Configure local_parent_not_sampled sampler. + # If omitted or null, always_off is used. + local_parent_not_sampled: + # Configure sampler to be always_off. + always_off: ``` ### Resource Configuration diff --git a/src/OpenTelemetry.AutoInstrumentation/Configurations/EnvironmentConfigurationTracerHelper.cs b/src/OpenTelemetry.AutoInstrumentation/Configurations/EnvironmentConfigurationTracerHelper.cs index 48fc7b6441..312780201f 100644 --- a/src/OpenTelemetry.AutoInstrumentation/Configurations/EnvironmentConfigurationTracerHelper.cs +++ b/src/OpenTelemetry.AutoInstrumentation/Configurations/EnvironmentConfigurationTracerHelper.cs @@ -62,11 +62,17 @@ public static TracerProviderBuilder UseEnvironmentVariables( builder.AddOpenTracingShimSource(); } - builder + builder = builder // Exporters can cause dependency loads. // Should be called later if dependency listeners are already setup. - .SetExporter(settings, pluginManager) - .AddSource(settings.ActivitySources.ToArray()); + .SetExporter(settings, pluginManager); + + if (settings.Sampler != null) + { + builder = builder.SetSampler(settings.Sampler); + } + + builder = builder.AddSource(settings.ActivitySources.ToArray()); foreach (var legacySource in settings.AdditionalLegacySources) { diff --git a/src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/AlwaysOffSamplerConfig.cs b/src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/AlwaysOffSamplerConfig.cs new file mode 100644 index 0000000000..06cd68de6f --- /dev/null +++ b/src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/AlwaysOffSamplerConfig.cs @@ -0,0 +1,11 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +using OpenTelemetry.AutoInstrumentation.Configurations.FileBasedConfiguration.Parser; + +namespace OpenTelemetry.AutoInstrumentation.Configurations.FileBasedConfiguration; + +[EmptyObjectOnEmptyYaml] +internal class AlwaysOffSamplerConfig +{ +} diff --git a/src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/AlwaysOnSamplerConfig.cs b/src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/AlwaysOnSamplerConfig.cs new file mode 100644 index 0000000000..c9a8f361ab --- /dev/null +++ b/src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/AlwaysOnSamplerConfig.cs @@ -0,0 +1,11 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +using OpenTelemetry.AutoInstrumentation.Configurations.FileBasedConfiguration.Parser; + +namespace OpenTelemetry.AutoInstrumentation.Configurations.FileBasedConfiguration; + +[EmptyObjectOnEmptyYaml] +internal class AlwaysOnSamplerConfig +{ +} diff --git a/src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/ParentBasedSamplerConfig.cs b/src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/ParentBasedSamplerConfig.cs new file mode 100644 index 0000000000..9fb72ca84b --- /dev/null +++ b/src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/ParentBasedSamplerConfig.cs @@ -0,0 +1,26 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +using OpenTelemetry.AutoInstrumentation.Configurations.FileBasedConfiguration.Parser; +using Vendors.YamlDotNet.Serialization; + +namespace OpenTelemetry.AutoInstrumentation.Configurations.FileBasedConfiguration; + +[EmptyObjectOnEmptyYaml] +internal class ParentBasedSamplerConfig +{ + [YamlMember(Alias = "root")] + public SamplerConfig? Root { get; set; } + + [YamlMember(Alias = "remote_parent_sampled")] + public SamplerConfig? RemoteParentSampled { get; set; } + + [YamlMember(Alias = "remote_parent_not_sampled")] + public SamplerConfig? RemoteParentNotSampled { get; set; } + + [YamlMember(Alias = "local_parent_sampled")] + public SamplerConfig? LocalParentSampled { get; set; } + + [YamlMember(Alias = "local_parent_not_sampled")] + public SamplerConfig? LocalParentNotSampled { get; set; } +} diff --git a/src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/SamplerConfig.cs b/src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/SamplerConfig.cs new file mode 100644 index 0000000000..b4025c648d --- /dev/null +++ b/src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/SamplerConfig.cs @@ -0,0 +1,26 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +using OpenTelemetry.AutoInstrumentation.Configurations.FileBasedConfiguration.Parser; +using Vendors.YamlDotNet.Serialization; + +namespace OpenTelemetry.AutoInstrumentation.Configurations.FileBasedConfiguration; + +[EmptyObjectOnEmptyYaml] +internal class SamplerConfig +{ + [YamlMember(Alias = "always_on")] + public AlwaysOnSamplerConfig? AlwaysOn { get; set; } + + [YamlMember(Alias = "always_off")] + public AlwaysOffSamplerConfig? AlwaysOff { get; set; } + + [YamlMember(Alias = "trace_id_ratio")] + public TraceIdRatioSamplerConfig? TraceIdRatio { get; set; } + + [YamlMember(Alias = "traceidratio")] + public TraceIdRatioSamplerConfig? TraceIdRatioLegacy { get; set; } + + [YamlMember(Alias = "parent_based")] + public ParentBasedSamplerConfig? ParentBased { get; set; } +} diff --git a/src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/SamplerFactory.cs b/src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/SamplerFactory.cs new file mode 100644 index 0000000000..5c88fa2971 --- /dev/null +++ b/src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/SamplerFactory.cs @@ -0,0 +1,171 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +using System; +using System.Collections.Generic; +using System.Linq; +using OpenTelemetry.AutoInstrumentation.Logging; +using OpenTelemetry.Trace; + +namespace OpenTelemetry.AutoInstrumentation.Configurations.FileBasedConfiguration; + +internal static class SamplerFactory +{ + private static readonly IOtelLogger Logger = OtelLogging.GetLogger(); + + public static Sampler? CreateSampler(SamplerConfig? samplerConfig, bool failFast) + { + try + { + return CreateSamplerInternal(samplerConfig, failFast, "tracer_provider.sampler"); + } + catch (Exception ex) when (!failFast) + { + Logger.Error(ex, "Failed to create sampler from file-based configuration."); + return null; + } + } + + private static Sampler? CreateSamplerInternal(SamplerConfig? samplerConfig, bool failFast, string path) + { + if (samplerConfig == null) + { + return null; + } + + var configuredSamplers = new List<(string key, Sampler? sampler)>(); + + if (samplerConfig.AlwaysOn != null) + { + configuredSamplers.Add(("always_on", new AlwaysOnSampler())); + } + + if (samplerConfig.AlwaysOff != null) + { + configuredSamplers.Add(("always_off", new AlwaysOffSampler())); + } + + if (samplerConfig.TraceIdRatio != null) + { + configuredSamplers.Add(("trace_id_ratio", CreateTraceIdRatioSampler(samplerConfig.TraceIdRatio, failFast, path + ".trace_id_ratio"))); + } + + if (samplerConfig.TraceIdRatioLegacy != null) + { + configuredSamplers.Add(("traceidratio", CreateTraceIdRatioSampler(samplerConfig.TraceIdRatioLegacy, failFast, path + ".traceidratio"))); + } + + if (samplerConfig.ParentBased != null) + { + configuredSamplers.Add(("parent_based", CreateParentBasedSampler(samplerConfig.ParentBased, failFast, path + ".parent_based"))); + } + + if (configuredSamplers.Count == 0) + { + var message = $"Sampler configuration '{path}' does not specify a sampler type."; + Logger.Warning(message); + + if (failFast) + { + throw new InvalidOperationException(message); + } + + return null; + } + + if (configuredSamplers.Count > 1) + { + var configuredNames = string.Join(", ", configuredSamplers.Select(s => s.key)); + var message = $"Sampler configuration '{path}' specifies multiple sampler types ({configuredNames}). Only one sampler can be configured."; + Logger.Error(message); + + if (failFast) + { + throw new InvalidOperationException(message); + } + + return null; + } + + var configuredSampler = configuredSamplers[0].sampler; + if (configuredSampler == null) + { + var message = $"Sampler configuration '{path}' is invalid."; + Logger.Error(message); + + if (failFast) + { + throw new InvalidOperationException(message); + } + } + + return configuredSampler; + } + + private static Sampler? CreateTraceIdRatioSampler(TraceIdRatioSamplerConfig config, bool failFast, string path) + { + if (!config.Ratio.HasValue) + { + var message = $"Sampler configuration '{path}' must define the 'ratio' property."; + Logger.Error(message); + + if (failFast) + { + throw new InvalidOperationException(message); + } + + return null; + } + + var ratio = config.Ratio.Value; + if (ratio is < 0 or > 1) + { + var message = $"Sampler configuration '{path}' ratio must be between 0 and 1 inclusive."; + Logger.Error(message); + + if (failFast) + { + throw new InvalidOperationException(message); + } + + return null; + } + + return new TraceIdRatioBasedSampler(ratio); + } + + private static Sampler CreateParentBasedSampler(ParentBasedSamplerConfig config, bool failFast, string path) + { + var rootSampler = GetSamplerOrDefault(config.Root, new AlwaysOnSampler(), failFast, path + ".root", "always_on"); + var remoteParentSampled = GetSamplerOrDefault(config.RemoteParentSampled, new AlwaysOnSampler(), failFast, path + ".remote_parent_sampled", "always_on"); + var remoteParentNotSampled = GetSamplerOrDefault(config.RemoteParentNotSampled, new AlwaysOffSampler(), failFast, path + ".remote_parent_not_sampled", "always_off"); + var localParentSampled = GetSamplerOrDefault(config.LocalParentSampled, new AlwaysOnSampler(), failFast, path + ".local_parent_sampled", "always_on"); + var localParentNotSampled = GetSamplerOrDefault(config.LocalParentNotSampled, new AlwaysOffSampler(), failFast, path + ".local_parent_not_sampled", "always_off"); + + return new ParentBasedSampler(rootSampler, remoteParentSampled, remoteParentNotSampled, localParentSampled, localParentNotSampled); + } + + private static Sampler GetSamplerOrDefault(SamplerConfig? samplerConfig, Sampler defaultSampler, bool failFast, string path, string defaultSamplerName) + { + if (samplerConfig == null) + { + return defaultSampler; + } + + var sampler = CreateSamplerInternal(samplerConfig, failFast, path); + if (sampler != null) + { + return sampler; + } + + var message = $"Sampler configuration '{path}' is invalid. Falling back to default '{defaultSamplerName}' sampler."; + Logger.Warning(message); + + if (failFast) + { + throw new InvalidOperationException(message); + } + + return defaultSampler; + } +} diff --git a/src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/TraceIdRatioSamplerConfig.cs b/src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/TraceIdRatioSamplerConfig.cs new file mode 100644 index 0000000000..521f8d8e46 --- /dev/null +++ b/src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/TraceIdRatioSamplerConfig.cs @@ -0,0 +1,12 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +using Vendors.YamlDotNet.Serialization; + +namespace OpenTelemetry.AutoInstrumentation.Configurations.FileBasedConfiguration; + +internal class TraceIdRatioSamplerConfig +{ + [YamlMember(Alias = "ratio")] + public double? Ratio { get; set; } +} diff --git a/src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/TracerProviderConfiguration.cs b/src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/TracerProviderConfiguration.cs index 1d52312968..f67811fc44 100644 --- a/src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/TracerProviderConfiguration.cs +++ b/src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/TracerProviderConfiguration.cs @@ -9,4 +9,7 @@ internal class TracerProviderConfiguration { [YamlMember(Alias = "processors")] public List Processors { get; set; } = new(); + + [YamlMember(Alias = "sampler")] + public SamplerConfig? Sampler { get; set; } } diff --git a/src/OpenTelemetry.AutoInstrumentation/Configurations/TracerSettings.cs b/src/OpenTelemetry.AutoInstrumentation/Configurations/TracerSettings.cs index bb3d6b01bf..52b894feb4 100644 --- a/src/OpenTelemetry.AutoInstrumentation/Configurations/TracerSettings.cs +++ b/src/OpenTelemetry.AutoInstrumentation/Configurations/TracerSettings.cs @@ -4,6 +4,7 @@ using OpenTelemetry.AutoInstrumentation.Configurations.FileBasedConfiguration; using OpenTelemetry.AutoInstrumentation.Configurations.Otlp; using OpenTelemetry.AutoInstrumentation.Logging; +using OpenTelemetry.Trace; namespace OpenTelemetry.AutoInstrumentation.Configurations; @@ -63,6 +64,11 @@ internal class TracerSettings : Settings /// public IReadOnlyList? Processors { get; private set; } = null; + /// + /// Gets the sampler configured via file-based configuration. + /// + public Sampler? Sampler { get; private set; } + protected override void OnLoadEnvVar(Configuration configuration) { TracesExporters = ParseTracesExporter(configuration); @@ -112,6 +118,8 @@ protected override void OnLoadFile(YamlConfiguration configuration) } Processors = processors; + + Sampler = SamplerFactory.CreateSampler(configuration.TracerProvider?.Sampler, configuration.FailFast); } private static List ParseTracesExporter(Configuration configuration) diff --git a/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/FileBased/FilebasedTracesSettingsTests.cs b/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/FileBased/FilebasedTracesSettingsTests.cs index d3adca198d..e229390ceb 100644 --- a/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/FileBased/FilebasedTracesSettingsTests.cs +++ b/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/FileBased/FilebasedTracesSettingsTests.cs @@ -1,8 +1,10 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 +using System.Diagnostics; using OpenTelemetry.AutoInstrumentation.Configurations; using OpenTelemetry.AutoInstrumentation.Configurations.FileBasedConfiguration; +using OpenTelemetry.Trace; using Xunit; namespace OpenTelemetry.AutoInstrumentation.Tests.Configurations.FileBased; @@ -358,4 +360,53 @@ internal SkipConfigurationTestCase(YamlConfiguration configuration) internal YamlConfiguration Configuration { get; } } + + [Fact] + public void LoadFile_ConfiguresParentBasedSampler() + { + var samplerConfig = new SamplerConfig + { + ParentBased = new ParentBasedSamplerConfig + { + Root = new SamplerConfig { AlwaysOn = new AlwaysOnSamplerConfig() }, + RemoteParentSampled = new SamplerConfig { AlwaysOn = new AlwaysOnSamplerConfig() }, + RemoteParentNotSampled = new SamplerConfig { AlwaysOff = new AlwaysOffSamplerConfig() }, + LocalParentSampled = new SamplerConfig { AlwaysOn = new AlwaysOnSamplerConfig() }, + LocalParentNotSampled = new SamplerConfig { AlwaysOff = new AlwaysOffSamplerConfig() } + } + }; + + var conf = new YamlConfiguration + { + TracerProvider = new TracerProviderConfiguration + { + Sampler = samplerConfig + } + }; + + var settings = new TracerSettings(); + + settings.LoadFile(conf); + + var sampler = Assert.IsType(settings.Sampler); + + Assert.Equal(SamplingDecision.RecordAndSample, sampler.ShouldSample(CreateSamplingParameters(default)).Decision); + + var remoteSampledParent = new ActivityContext(ActivityTraceId.CreateRandom(), ActivitySpanId.CreateRandom(), ActivityTraceFlags.Recorded, traceState: null, isRemote: true); + Assert.Equal(SamplingDecision.RecordAndSample, sampler.ShouldSample(CreateSamplingParameters(remoteSampledParent)).Decision); + + var remoteNotSampledParent = new ActivityContext(ActivityTraceId.CreateRandom(), ActivitySpanId.CreateRandom(), ActivityTraceFlags.None, traceState: null, isRemote: true); + Assert.Equal(SamplingDecision.Drop, sampler.ShouldSample(CreateSamplingParameters(remoteNotSampledParent)).Decision); + + var localSampledParent = new ActivityContext(ActivityTraceId.CreateRandom(), ActivitySpanId.CreateRandom(), ActivityTraceFlags.Recorded, traceState: null, isRemote: false); + Assert.Equal(SamplingDecision.RecordAndSample, sampler.ShouldSample(CreateSamplingParameters(localSampledParent)).Decision); + + var localNotSampledParent = new ActivityContext(ActivityTraceId.CreateRandom(), ActivitySpanId.CreateRandom(), ActivityTraceFlags.None, traceState: null, isRemote: false); + Assert.Equal(SamplingDecision.Drop, sampler.ShouldSample(CreateSamplingParameters(localNotSampledParent)).Decision); + } + + private static SamplingParameters CreateSamplingParameters(ActivityContext parentContext) + { + return new SamplingParameters(parentContext, ActivityTraceId.CreateRandom(), "span", ActivityKind.Internal, new TagList(), new ActivityLink[] { }); + } } diff --git a/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/FileBased/Files/TestTracesFile.yaml b/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/FileBased/Files/TestTracesFile.yaml index 514737454e..0caaaf6cdc 100644 --- a/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/FileBased/Files/TestTracesFile.yaml +++ b/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/FileBased/Files/TestTracesFile.yaml @@ -9,7 +9,7 @@ tracer_provider: otlp_http: endpoint: http://localhost:4318/v1/traces timeout: 10000 - headers: + headers: - name: header1234 value: "1234" - batch: @@ -24,3 +24,15 @@ tracer_provider: - simple: exporter: console: + sampler: + parent_based: + root: + always_on: + remote_parent_sampled: + always_on: + remote_parent_not_sampled: + always_off: + local_parent_sampled: + always_on: + local_parent_not_sampled: + always_off: diff --git a/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/FileBased/Parser/ParserTracesTests.cs b/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/FileBased/Parser/ParserTracesTests.cs index 48eeb664e3..c2e32addb5 100644 --- a/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/FileBased/Parser/ParserTracesTests.cs +++ b/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/FileBased/Parser/ParserTracesTests.cs @@ -57,6 +57,20 @@ public void Parse_FullConfigYaml_ShouldPopulateModelCorrectly() Assert.NotNull(simpleProcessor); Assert.NotNull(simpleProcessor.Exporter); Assert.NotNull(simpleProcessor.Exporter.Console); + + var sampler = config.TracerProvider.Sampler; + Assert.NotNull(sampler); + Assert.NotNull(sampler.ParentBased); + Assert.NotNull(sampler.ParentBased.Root); + Assert.NotNull(sampler.ParentBased.Root.AlwaysOn); + Assert.NotNull(sampler.ParentBased.RemoteParentSampled); + Assert.NotNull(sampler.ParentBased.RemoteParentSampled.AlwaysOn); + Assert.NotNull(sampler.ParentBased.RemoteParentNotSampled); + Assert.NotNull(sampler.ParentBased.RemoteParentNotSampled.AlwaysOff); + Assert.NotNull(sampler.ParentBased.LocalParentSampled); + Assert.NotNull(sampler.ParentBased.LocalParentSampled.AlwaysOn); + Assert.NotNull(sampler.ParentBased.LocalParentNotSampled); + Assert.NotNull(sampler.ParentBased.LocalParentNotSampled.AlwaysOff); } [Fact] From 18d2f5d166ce33c2275beb8c925c41c0ec6fa14e Mon Sep 17 00:00:00 2001 From: Yevhenii Solomchenko Date: Fri, 24 Oct 2025 09:28:05 +0200 Subject: [PATCH 2/3] Update CHANGELOG to remove tracer sampler config note Removed the line about file-based configuration for tracer sampler. --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ded7736f39..585cd9cff5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,6 @@ This component adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.h ### Added - Configuration based instrumentation. -- Add support for configuring tracer sampler via file-based configuration. ### Changed From fcc2da25297a30bdb875f1a6383026e550283462 Mon Sep 17 00:00:00 2001 From: Yevhenii Solomchenko Date: Fri, 24 Oct 2025 10:19:37 +0200 Subject: [PATCH 3/3] remove empty classes, use dictionary instead of list of tuple --- .../AlwaysOffSamplerConfig.cs | 11 ------- .../AlwaysOnSamplerConfig.cs | 11 ------- .../FileBasedConfiguration/SamplerConfig.cs | 8 ++--- .../FileBasedConfiguration/SamplerFactory.cs | 22 ++++--------- .../FileBased/FilebasedTracesSettingsTests.cs | 32 +++++++++---------- 5 files changed, 25 insertions(+), 59 deletions(-) delete mode 100644 src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/AlwaysOffSamplerConfig.cs delete mode 100644 src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/AlwaysOnSamplerConfig.cs diff --git a/src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/AlwaysOffSamplerConfig.cs b/src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/AlwaysOffSamplerConfig.cs deleted file mode 100644 index 06cd68de6f..0000000000 --- a/src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/AlwaysOffSamplerConfig.cs +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -using OpenTelemetry.AutoInstrumentation.Configurations.FileBasedConfiguration.Parser; - -namespace OpenTelemetry.AutoInstrumentation.Configurations.FileBasedConfiguration; - -[EmptyObjectOnEmptyYaml] -internal class AlwaysOffSamplerConfig -{ -} diff --git a/src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/AlwaysOnSamplerConfig.cs b/src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/AlwaysOnSamplerConfig.cs deleted file mode 100644 index c9a8f361ab..0000000000 --- a/src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/AlwaysOnSamplerConfig.cs +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -using OpenTelemetry.AutoInstrumentation.Configurations.FileBasedConfiguration.Parser; - -namespace OpenTelemetry.AutoInstrumentation.Configurations.FileBasedConfiguration; - -[EmptyObjectOnEmptyYaml] -internal class AlwaysOnSamplerConfig -{ -} diff --git a/src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/SamplerConfig.cs b/src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/SamplerConfig.cs index b4025c648d..61f44e0a91 100644 --- a/src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/SamplerConfig.cs +++ b/src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/SamplerConfig.cs @@ -6,21 +6,17 @@ namespace OpenTelemetry.AutoInstrumentation.Configurations.FileBasedConfiguration; -[EmptyObjectOnEmptyYaml] internal class SamplerConfig { [YamlMember(Alias = "always_on")] - public AlwaysOnSamplerConfig? AlwaysOn { get; set; } + public object? AlwaysOn { get; set; } [YamlMember(Alias = "always_off")] - public AlwaysOffSamplerConfig? AlwaysOff { get; set; } + public object? AlwaysOff { get; set; } [YamlMember(Alias = "trace_id_ratio")] public TraceIdRatioSamplerConfig? TraceIdRatio { get; set; } - [YamlMember(Alias = "traceidratio")] - public TraceIdRatioSamplerConfig? TraceIdRatioLegacy { get; set; } - [YamlMember(Alias = "parent_based")] public ParentBasedSamplerConfig? ParentBased { get; set; } } diff --git a/src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/SamplerFactory.cs b/src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/SamplerFactory.cs index 5c88fa2971..0b488a1dbd 100644 --- a/src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/SamplerFactory.cs +++ b/src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/SamplerFactory.cs @@ -1,9 +1,6 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 -using System; -using System.Collections.Generic; -using System.Linq; using OpenTelemetry.AutoInstrumentation.Logging; using OpenTelemetry.Trace; @@ -33,31 +30,26 @@ internal static class SamplerFactory return null; } - var configuredSamplers = new List<(string key, Sampler? sampler)>(); + var configuredSamplers = new Dictionary(); if (samplerConfig.AlwaysOn != null) { - configuredSamplers.Add(("always_on", new AlwaysOnSampler())); + configuredSamplers.Add("always_on", new AlwaysOnSampler()); } if (samplerConfig.AlwaysOff != null) { - configuredSamplers.Add(("always_off", new AlwaysOffSampler())); + configuredSamplers.Add("always_off", new AlwaysOffSampler()); } if (samplerConfig.TraceIdRatio != null) { - configuredSamplers.Add(("trace_id_ratio", CreateTraceIdRatioSampler(samplerConfig.TraceIdRatio, failFast, path + ".trace_id_ratio"))); - } - - if (samplerConfig.TraceIdRatioLegacy != null) - { - configuredSamplers.Add(("traceidratio", CreateTraceIdRatioSampler(samplerConfig.TraceIdRatioLegacy, failFast, path + ".traceidratio"))); + configuredSamplers.Add("trace_id_ratio", CreateTraceIdRatioSampler(samplerConfig.TraceIdRatio, failFast, path + ".trace_id_ratio")); } if (samplerConfig.ParentBased != null) { - configuredSamplers.Add(("parent_based", CreateParentBasedSampler(samplerConfig.ParentBased, failFast, path + ".parent_based"))); + configuredSamplers.Add("parent_based", CreateParentBasedSampler(samplerConfig.ParentBased, failFast, path + ".parent_based")); } if (configuredSamplers.Count == 0) @@ -75,7 +67,7 @@ internal static class SamplerFactory if (configuredSamplers.Count > 1) { - var configuredNames = string.Join(", ", configuredSamplers.Select(s => s.key)); + var configuredNames = string.Join(", ", configuredSamplers.Keys); var message = $"Sampler configuration '{path}' specifies multiple sampler types ({configuredNames}). Only one sampler can be configured."; Logger.Error(message); @@ -87,7 +79,7 @@ internal static class SamplerFactory return null; } - var configuredSampler = configuredSamplers[0].sampler; + var configuredSampler = configuredSamplers.Values.First(); if (configuredSampler == null) { var message = $"Sampler configuration '{path}' is invalid."; diff --git a/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/FileBased/FilebasedTracesSettingsTests.cs b/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/FileBased/FilebasedTracesSettingsTests.cs index e229390ceb..7010f67e5f 100644 --- a/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/FileBased/FilebasedTracesSettingsTests.cs +++ b/test/OpenTelemetry.AutoInstrumentation.Tests/Configurations/FileBased/FilebasedTracesSettingsTests.cs @@ -351,16 +351,6 @@ public void LoadMethod_SkipWrongExporterConfiguration(SkipConfigurationTestCase Assert.Empty(settings.TracesExporters); } - public class SkipConfigurationTestCase - { - internal SkipConfigurationTestCase(YamlConfiguration configuration) - { - Configuration = configuration; - } - - internal YamlConfiguration Configuration { get; } - } - [Fact] public void LoadFile_ConfiguresParentBasedSampler() { @@ -368,11 +358,11 @@ public void LoadFile_ConfiguresParentBasedSampler() { ParentBased = new ParentBasedSamplerConfig { - Root = new SamplerConfig { AlwaysOn = new AlwaysOnSamplerConfig() }, - RemoteParentSampled = new SamplerConfig { AlwaysOn = new AlwaysOnSamplerConfig() }, - RemoteParentNotSampled = new SamplerConfig { AlwaysOff = new AlwaysOffSamplerConfig() }, - LocalParentSampled = new SamplerConfig { AlwaysOn = new AlwaysOnSamplerConfig() }, - LocalParentNotSampled = new SamplerConfig { AlwaysOff = new AlwaysOffSamplerConfig() } + Root = new SamplerConfig { AlwaysOn = new object() }, + RemoteParentSampled = new SamplerConfig { AlwaysOn = new object() }, + RemoteParentNotSampled = new SamplerConfig { AlwaysOff = new object() }, + LocalParentSampled = new SamplerConfig { AlwaysOn = new object() }, + LocalParentNotSampled = new SamplerConfig { AlwaysOff = new object() } } }; @@ -407,6 +397,16 @@ public void LoadFile_ConfiguresParentBasedSampler() private static SamplingParameters CreateSamplingParameters(ActivityContext parentContext) { - return new SamplingParameters(parentContext, ActivityTraceId.CreateRandom(), "span", ActivityKind.Internal, new TagList(), new ActivityLink[] { }); + return new SamplingParameters(parentContext, ActivityTraceId.CreateRandom(), "span", ActivityKind.Internal, default(TagList), new ActivityLink[] { }); + } + + public class SkipConfigurationTestCase + { + internal SkipConfigurationTestCase(YamlConfiguration configuration) + { + Configuration = configuration; + } + + internal YamlConfiguration Configuration { get; } } }