Skip to content

Refactor sampler config to use SamplerOptions and IOptions#7192

Open
stevejgordon wants to merge 4 commits into
open-telemetry:mainfrom
stevejgordon:7178-sampler-options
Open

Refactor sampler config to use SamplerOptions and IOptions#7192
stevejgordon wants to merge 4 commits into
open-telemetry:mainfrom
stevejgordon:7178-sampler-options

Conversation

@stevejgordon
Copy link
Copy Markdown
Contributor

@stevejgordon stevejgordon commented Apr 28, 2026

Fixes #7178

Changes

  • Introduce SamplerOptions to encapsulate trace sampler configuration, parsing OTEL_TRACES_SAMPLER and OTEL_TRACES_SAMPLER_ARG from IConfiguration.
  • Refactor TracerProviderSdk to resolve sampler settings via IOptions<SamplerOptions>, enabling standard options pipeline overrides and improved diagnostics.
  • Add tests for configuration, overrides, and error handling.
  • Update public API and diagnostics to reflect new options-based approach.

Merge requirement checklist

  • CONTRIBUTING guidelines followed (license requirements, nullable enabled, static analysis, etc.)
  • Unit tests added/updated
  • Appropriate CHANGELOG.md files updated for non-trivial changes
  • Changes in public API reviewed (if applicable)

@stevejgordon stevejgordon requested a review from a team as a code owner April 28, 2026 06:46
@github-actions github-actions Bot added pkg:OpenTelemetry Issues related to OpenTelemetry NuGet package pkg:OpenTelemetry.Extensions.Hosting Issues related to OpenTelemetry.Extensions.Hosting NuGet package labels Apr 28, 2026
@stevejgordon
Copy link
Copy Markdown
Contributor Author

@martincostello I'm a bit baffled by these build warnings. I don't see them locally (running dotnet build ./build/OpenTelemetry.proj --configuration Release -p:ExposeExperimentalFeatures=true) and the tests pass too.

@martincostello
Copy link
Copy Markdown
Member

martincostello commented Apr 28, 2026

Your branch is out-of-date following a refactor I did in #7175.

You don't see it locally because you're not building a merge commit with main.

@stevejgordon
Copy link
Copy Markdown
Contributor Author

Ah darn! okay, thanks!

@stevejgordon stevejgordon force-pushed the 7178-sampler-options branch from 54d1705 to 21b5f82 Compare April 28, 2026 10:01
- Introduce `SamplerOptions` to encapsulate trace sampler configuration, parsing OTEL_TRACES_SAMPLER and OTEL_TRACES_SAMPLER_ARG from IConfiguration.
- Refactor `TracerProviderSdk` to resolve sampler settings via `IOptions<SamplerOptions>`, enabling standard options pipeline overrides and improved diagnostics.
- Add tests for configuration, overrides, and error handling.
- Update public API and diagnostics to reflect new options-based approach.
@stevejgordon stevejgordon force-pushed the 7178-sampler-options branch from 21b5f82 to 9c1c993 Compare April 28, 2026 10:24
@stevejgordon
Copy link
Copy Markdown
Contributor Author

@martincostello Should all be resolved now and ready for review.

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 28, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 89.56%. Comparing base (6232fb2) to head (0eb374d).
⚠️ Report is 91 commits behind head on main.
✅ All tests successful. No failed tests found.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #7192      +/-   ##
==========================================
+ Coverage   89.54%   89.56%   +0.02%     
==========================================
  Files         272      273       +1     
  Lines       13301    13327      +26     
==========================================
+ Hits        11910    11936      +26     
  Misses       1391     1391              
Flag Coverage Δ
unittests-Project-Experimental 89.45% <100.00%> (-0.03%) ⬇️
unittests-Project-Stable 89.18% <100.00%> (-0.30%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
...lder/ProviderBuilderServiceCollectionExtensions.cs 100.00% <100.00%> (ø)
src/OpenTelemetry/Trace/SamplerOptions.cs 100.00% <100.00%> (ø)
src/OpenTelemetry/Trace/TracerProviderSdk.cs 99.68% <100.00%> (+0.32%) ⬆️

... and 4 files with indirect coverage changes

/// <summary>
/// Gets or sets the sampler argument.
/// </summary>
public double? SamplerArg
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not think that we should expose this.
You can potentially add some internal property/method to parse the value. Env. vars treats it as string.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This again was to support the use case services.Configure<SamplerOptions>(o => o.SamplerArg = 0.5) for configuring sampling in code, which today is not possible, unlike other options types.

/// OTEL_TRACES_SAMPLER and OTEL_TRACES_SAMPLER_ARG environment variables
/// are parsed during object construction.
/// </summary>
public sealed class SamplerOptions
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have some dobuts that this class is what you really need for configuration SDK

lets consider: https://github.com/open-telemetry/opentelemetry-configuration/blob/4ad327807a9e7dc7d440dac6ca106e789df1233b/schema-docs.md#parentbasedsampler-

There is some implementation for ParentBasedSampler in https://github.com/open-telemetry/opentelemetry-dotnet-instrumentation/blob/00324dc0662996142548065278cc92c28323e1da/src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/SamplerFactory.cs#L129-L138

Technically you can include lets say TraceIdSampler in ParentBased. According to spec, it should be perfectly valid case.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One more thing, We can make probably some interim step. Make this class internal, and when you will be working on Configuration SDK we can adjust public contracts.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, this type is scoped to the env-var path (flat keys) and bringing consistency to the options types in general. It isn't intended to represent the full declarative config sampler tree which can (probably) be implemented without this. I think declarative config will hook in earlier in TracerProviderSdk.GetSampler method, falling back to this when that can't provider the richer factory approach. I may need to go back and read the specs more though as it's definitely possible I'm missing something.

As for making it internal, that prevents the consumer use case of services.Configure<SamplerOptions>(o => o.SamplerArg = 0.5) which many of the other options types currently enable. The goal of adding this was feature parity after it was identified in the config analysis.

Comment on lines +2 to +3
OpenTelemetry.Trace.SamplerOptions.SamplerArg.get -> double?
OpenTelemetry.Trace.SamplerOptions.SamplerArg.set -> void
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SamplerArgument?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we keep this as a public type, I'm okay with the rename. It was mirroring the env var names but a clearer name is likely better and it's not hard to see how they match up.

Comment thread src/OpenTelemetry/CHANGELOG.md Outdated
Comment thread src/OpenTelemetry/Trace/SamplerOptions.cs Outdated
Comment thread src/OpenTelemetry/Trace/TracerProviderSdk.cs Outdated
Comment on lines -446 to +459
if (configuration.TryGetStringValue(TracesSamplerArgConfigKey, out var configValue) &&
double.TryParse(configValue, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out var traceIdRatio) &&
!double.IsNaN(traceIdRatio) &&
!double.IsInfinity(traceIdRatio) &&
traceIdRatio >= 0.0 &&
traceIdRatio <= 1.0)
var samplerArg = options.SamplerArg;
if (samplerArg.HasValue
&& !double.IsNaN(samplerArg.Value)
&& !double.IsInfinity(samplerArg.Value)
&& samplerArg.Value >= 0.0
&& samplerArg.Value <= 1.0)
{
return traceIdRatio;
}
else
{
OpenTelemetrySdkEventSource.Log.TracesSamplerArgConfigInvalid(configValue ?? string.Empty);
return samplerArg.Value;
}

// No valid ratio. Log the original config string for diagnostic fidelity and fall back to 1.0.
OpenTelemetrySdkEventSource.Log.TracesSamplerArgConfigInvalid(
options.SamplerArgRaw
?? (samplerArg.HasValue
? samplerArg.Value.ToString(CultureInfo.InvariantCulture)
: string.Empty));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would refactor this to avoid calling .Value multiple times as there's a (small) overhead from it internally re-checking if there's a value internally so it could throw if there wasn't.

Comment on lines +174 to +175
using var clearSamplerEnv = EnvironmentVariableScope.Create(SamplerOptions.TracesSamplerConfigKey, null);
using var clearSamplerArgEnv = EnvironmentVariableScope.Create(SamplerOptions.TracesSamplerArgConfigKey, null);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should be able to just have one by using an array of tuples:

Suggested change
using var clearSamplerEnv = EnvironmentVariableScope.Create(SamplerOptions.TracesSamplerConfigKey, null);
using var clearSamplerArgEnv = EnvironmentVariableScope.Create(SamplerOptions.TracesSamplerArgConfigKey, null);
using var scope = EnvironmentVariableScope.Create(
[
(SamplerOptions.TracesSamplerConfigKey, null),
(SamplerOptions.TracesSamplerArgConfigKey, null),
]);

Comment on lines +202 to +203
using var clearSamplerEnv = EnvironmentVariableScope.Create(SamplerOptions.TracesSamplerConfigKey, null);
using var clearSamplerArgEnv = EnvironmentVariableScope.Create(SamplerOptions.TracesSamplerArgConfigKey, null);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

Comment on lines +14 to +17
public SamplerOptionsTests()
{
ClearSamplerEnvVars();
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's refactor this to use EnvironmentVariableScope.

I did a similar clean-up in another PR yesterday (but I've forgotten which one to point to the code).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stevejgordon and others added 3 commits April 28, 2026 12:48
Co-authored-by: Martin Costello <martin@martincostello.com>
Co-authored-by: Martin Costello <martin@martincostello.com>
Co-authored-by: Martin Costello <martin@martincostello.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 6, 2026

This PR was marked stale due to lack of activity and will be closed in 7 days. Commenting or pushing will instruct the bot to automatically remove the label. This bot runs once per day.

@github-actions github-actions Bot added the Stale Issues and pull requests which have been flagged for closing due to inactivity label May 6, 2026
@Kielek Kielek removed the Stale Issues and pull requests which have been flagged for closing due to inactivity label May 6, 2026
@stevejgordon
Copy link
Copy Markdown
Contributor Author

I haven't made all PR review changes yet. @Kielek what do you think on the responses to your comments on internal vs. public? If we plan to introduce the new declarative config package as part of the SDK (new project) then we could start with internal for that use case assuming we can make the internals visible to that project, however, we'd still lose the Configure<T> use case for consumers. Happy to discuss further as I'd like to unblock this one.

@github-actions
Copy link
Copy Markdown
Contributor

This PR was marked stale due to lack of activity and will be closed in 7 days. Commenting or pushing will instruct the bot to automatically remove the label. This bot runs once per day.

@github-actions github-actions Bot added Stale Issues and pull requests which have been flagged for closing due to inactivity and removed Stale Issues and pull requests which have been flagged for closing due to inactivity labels May 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pkg:OpenTelemetry.Extensions.Hosting Issues related to OpenTelemetry.Extensions.Hosting NuGet package pkg:OpenTelemetry Issues related to OpenTelemetry NuGet package

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[feature request] Add SamplerOptions

3 participants