Refactor sampler config to use SamplerOptions and IOptions#7192
Refactor sampler config to use SamplerOptions and IOptions#7192stevejgordon wants to merge 4 commits into
SamplerOptions and IOptions#7192Conversation
|
@martincostello I'm a bit baffled by these build warnings. I don't see them locally (running |
|
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. |
|
Ah darn! okay, thanks! |
54d1705 to
21b5f82
Compare
- 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.
21b5f82 to
9c1c993
Compare
|
@martincostello Should all be resolved now and ready for review. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ 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
Flags with carried forward coverage won't be shown. Click here to find out more.
|
| /// <summary> | ||
| /// Gets or sets the sampler argument. | ||
| /// </summary> | ||
| public double? SamplerArg |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
I have some dobuts that this class is what you really need for configuration SDK
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
| OpenTelemetry.Trace.SamplerOptions.SamplerArg.get -> double? | ||
| OpenTelemetry.Trace.SamplerOptions.SamplerArg.set -> void |
There was a problem hiding this comment.
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.
| 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)); | ||
|
|
There was a problem hiding this comment.
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.
| using var clearSamplerEnv = EnvironmentVariableScope.Create(SamplerOptions.TracesSamplerConfigKey, null); | ||
| using var clearSamplerArgEnv = EnvironmentVariableScope.Create(SamplerOptions.TracesSamplerArgConfigKey, null); |
There was a problem hiding this comment.
You should be able to just have one by using an array of tuples:
| 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), | |
| ]); |
| using var clearSamplerEnv = EnvironmentVariableScope.Create(SamplerOptions.TracesSamplerConfigKey, null); | ||
| using var clearSamplerArgEnv = EnvironmentVariableScope.Create(SamplerOptions.TracesSamplerArgConfigKey, null); |
| public SamplerOptionsTests() | ||
| { | ||
| ClearSamplerEnvVars(); | ||
| } |
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
Co-authored-by: Martin Costello <martin@martincostello.com>
Co-authored-by: Martin Costello <martin@martincostello.com>
Co-authored-by: Martin Costello <martin@martincostello.com>
|
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. |
|
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 |
|
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. |
Fixes #7178
Changes
SamplerOptionsto encapsulate trace sampler configuration, parsing OTEL_TRACES_SAMPLER and OTEL_TRACES_SAMPLER_ARG from IConfiguration.TracerProviderSdkto resolve sampler settings viaIOptions<SamplerOptions>, enabling standard options pipeline overrides and improved diagnostics.Merge requirement checklist
CHANGELOG.mdfiles updated for non-trivial changes