Skip to content

Commit 79f9c1d

Browse files
authored
Merge pull request #1 from ysolomchenko/codex/add-file-based-sampler-configuration
Add file-based sampler configuration
2 parents d2961cd + 18d2f5d commit 79f9c1d

File tree

13 files changed

+386
-5
lines changed

13 files changed

+386
-5
lines changed

docs/file-based-configuration.md

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ tracer_provider:
5656
processors:
5757
# Batch processor for OTLP HTTP
5858
- batch:
59-
# Configure delay interval (in milliseconds) between two consecutive exports.
59+
# Configure delay interval (in milliseconds) between two consecutive exports.
6060
# Value must be non-negative.
6161
# If omitted or null, 5000 is used.
6262
schedule_delay: 5000
@@ -110,6 +110,36 @@ tracer_provider:
110110
- simple:
111111
exporter:
112112
console:
113+
114+
# Configure the sampler. If omitted, parent based sampler with a root of always_on is used.
115+
sampler:
116+
# Configure sampler to be parent_based.
117+
parent_based:
118+
# Configure root sampler.
119+
# If omitted or null, always_on is used.
120+
root:
121+
# Configure sampler to be always_on.
122+
always_on:
123+
# Configure remote_parent_sampled sampler.
124+
# If omitted or null, always_on is used.
125+
remote_parent_sampled:
126+
# Configure sampler to be always_on.
127+
always_on:
128+
# Configure remote_parent_not_sampled sampler.
129+
# If omitted or null, always_off is used.
130+
remote_parent_not_sampled:
131+
# Configure sampler to be always_off.
132+
always_off:
133+
# Configure local_parent_sampled sampler.
134+
# If omitted or null, always_on is used.
135+
local_parent_sampled:
136+
# Configure sampler to be always_on.
137+
always_on:
138+
# Configure local_parent_not_sampled sampler.
139+
# If omitted or null, always_off is used.
140+
local_parent_not_sampled:
141+
# Configure sampler to be always_off.
142+
always_off:
113143
```
114144
115145
### Resource Configuration

src/OpenTelemetry.AutoInstrumentation/Configurations/EnvironmentConfigurationTracerHelper.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,17 @@ public static TracerProviderBuilder UseEnvironmentVariables(
6262
builder.AddOpenTracingShimSource();
6363
}
6464

65-
builder
65+
builder = builder
6666
// Exporters can cause dependency loads.
6767
// Should be called later if dependency listeners are already setup.
68-
.SetExporter(settings, pluginManager)
69-
.AddSource(settings.ActivitySources.ToArray());
68+
.SetExporter(settings, pluginManager);
69+
70+
if (settings.Sampler != null)
71+
{
72+
builder = builder.SetSampler(settings.Sampler);
73+
}
74+
75+
builder = builder.AddSource(settings.ActivitySources.ToArray());
7076

7177
foreach (var legacySource in settings.AdditionalLegacySources)
7278
{
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
using OpenTelemetry.AutoInstrumentation.Configurations.FileBasedConfiguration.Parser;
5+
6+
namespace OpenTelemetry.AutoInstrumentation.Configurations.FileBasedConfiguration;
7+
8+
[EmptyObjectOnEmptyYaml]
9+
internal class AlwaysOffSamplerConfig
10+
{
11+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
using OpenTelemetry.AutoInstrumentation.Configurations.FileBasedConfiguration.Parser;
5+
6+
namespace OpenTelemetry.AutoInstrumentation.Configurations.FileBasedConfiguration;
7+
8+
[EmptyObjectOnEmptyYaml]
9+
internal class AlwaysOnSamplerConfig
10+
{
11+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
using OpenTelemetry.AutoInstrumentation.Configurations.FileBasedConfiguration.Parser;
5+
using Vendors.YamlDotNet.Serialization;
6+
7+
namespace OpenTelemetry.AutoInstrumentation.Configurations.FileBasedConfiguration;
8+
9+
[EmptyObjectOnEmptyYaml]
10+
internal class ParentBasedSamplerConfig
11+
{
12+
[YamlMember(Alias = "root")]
13+
public SamplerConfig? Root { get; set; }
14+
15+
[YamlMember(Alias = "remote_parent_sampled")]
16+
public SamplerConfig? RemoteParentSampled { get; set; }
17+
18+
[YamlMember(Alias = "remote_parent_not_sampled")]
19+
public SamplerConfig? RemoteParentNotSampled { get; set; }
20+
21+
[YamlMember(Alias = "local_parent_sampled")]
22+
public SamplerConfig? LocalParentSampled { get; set; }
23+
24+
[YamlMember(Alias = "local_parent_not_sampled")]
25+
public SamplerConfig? LocalParentNotSampled { get; set; }
26+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
using OpenTelemetry.AutoInstrumentation.Configurations.FileBasedConfiguration.Parser;
5+
using Vendors.YamlDotNet.Serialization;
6+
7+
namespace OpenTelemetry.AutoInstrumentation.Configurations.FileBasedConfiguration;
8+
9+
[EmptyObjectOnEmptyYaml]
10+
internal class SamplerConfig
11+
{
12+
[YamlMember(Alias = "always_on")]
13+
public AlwaysOnSamplerConfig? AlwaysOn { get; set; }
14+
15+
[YamlMember(Alias = "always_off")]
16+
public AlwaysOffSamplerConfig? AlwaysOff { get; set; }
17+
18+
[YamlMember(Alias = "trace_id_ratio")]
19+
public TraceIdRatioSamplerConfig? TraceIdRatio { get; set; }
20+
21+
[YamlMember(Alias = "traceidratio")]
22+
public TraceIdRatioSamplerConfig? TraceIdRatioLegacy { get; set; }
23+
24+
[YamlMember(Alias = "parent_based")]
25+
public ParentBasedSamplerConfig? ParentBased { get; set; }
26+
}
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Linq;
7+
using OpenTelemetry.AutoInstrumentation.Logging;
8+
using OpenTelemetry.Trace;
9+
10+
namespace OpenTelemetry.AutoInstrumentation.Configurations.FileBasedConfiguration;
11+
12+
internal static class SamplerFactory
13+
{
14+
private static readonly IOtelLogger Logger = OtelLogging.GetLogger();
15+
16+
public static Sampler? CreateSampler(SamplerConfig? samplerConfig, bool failFast)
17+
{
18+
try
19+
{
20+
return CreateSamplerInternal(samplerConfig, failFast, "tracer_provider.sampler");
21+
}
22+
catch (Exception ex) when (!failFast)
23+
{
24+
Logger.Error(ex, "Failed to create sampler from file-based configuration.");
25+
return null;
26+
}
27+
}
28+
29+
private static Sampler? CreateSamplerInternal(SamplerConfig? samplerConfig, bool failFast, string path)
30+
{
31+
if (samplerConfig == null)
32+
{
33+
return null;
34+
}
35+
36+
var configuredSamplers = new List<(string key, Sampler? sampler)>();
37+
38+
if (samplerConfig.AlwaysOn != null)
39+
{
40+
configuredSamplers.Add(("always_on", new AlwaysOnSampler()));
41+
}
42+
43+
if (samplerConfig.AlwaysOff != null)
44+
{
45+
configuredSamplers.Add(("always_off", new AlwaysOffSampler()));
46+
}
47+
48+
if (samplerConfig.TraceIdRatio != null)
49+
{
50+
configuredSamplers.Add(("trace_id_ratio", CreateTraceIdRatioSampler(samplerConfig.TraceIdRatio, failFast, path + ".trace_id_ratio")));
51+
}
52+
53+
if (samplerConfig.TraceIdRatioLegacy != null)
54+
{
55+
configuredSamplers.Add(("traceidratio", CreateTraceIdRatioSampler(samplerConfig.TraceIdRatioLegacy, failFast, path + ".traceidratio")));
56+
}
57+
58+
if (samplerConfig.ParentBased != null)
59+
{
60+
configuredSamplers.Add(("parent_based", CreateParentBasedSampler(samplerConfig.ParentBased, failFast, path + ".parent_based")));
61+
}
62+
63+
if (configuredSamplers.Count == 0)
64+
{
65+
var message = $"Sampler configuration '{path}' does not specify a sampler type.";
66+
Logger.Warning(message);
67+
68+
if (failFast)
69+
{
70+
throw new InvalidOperationException(message);
71+
}
72+
73+
return null;
74+
}
75+
76+
if (configuredSamplers.Count > 1)
77+
{
78+
var configuredNames = string.Join(", ", configuredSamplers.Select(s => s.key));
79+
var message = $"Sampler configuration '{path}' specifies multiple sampler types ({configuredNames}). Only one sampler can be configured.";
80+
Logger.Error(message);
81+
82+
if (failFast)
83+
{
84+
throw new InvalidOperationException(message);
85+
}
86+
87+
return null;
88+
}
89+
90+
var configuredSampler = configuredSamplers[0].sampler;
91+
if (configuredSampler == null)
92+
{
93+
var message = $"Sampler configuration '{path}' is invalid.";
94+
Logger.Error(message);
95+
96+
if (failFast)
97+
{
98+
throw new InvalidOperationException(message);
99+
}
100+
}
101+
102+
return configuredSampler;
103+
}
104+
105+
private static Sampler? CreateTraceIdRatioSampler(TraceIdRatioSamplerConfig config, bool failFast, string path)
106+
{
107+
if (!config.Ratio.HasValue)
108+
{
109+
var message = $"Sampler configuration '{path}' must define the 'ratio' property.";
110+
Logger.Error(message);
111+
112+
if (failFast)
113+
{
114+
throw new InvalidOperationException(message);
115+
}
116+
117+
return null;
118+
}
119+
120+
var ratio = config.Ratio.Value;
121+
if (ratio is < 0 or > 1)
122+
{
123+
var message = $"Sampler configuration '{path}' ratio must be between 0 and 1 inclusive.";
124+
Logger.Error(message);
125+
126+
if (failFast)
127+
{
128+
throw new InvalidOperationException(message);
129+
}
130+
131+
return null;
132+
}
133+
134+
return new TraceIdRatioBasedSampler(ratio);
135+
}
136+
137+
private static Sampler CreateParentBasedSampler(ParentBasedSamplerConfig config, bool failFast, string path)
138+
{
139+
var rootSampler = GetSamplerOrDefault(config.Root, new AlwaysOnSampler(), failFast, path + ".root", "always_on");
140+
var remoteParentSampled = GetSamplerOrDefault(config.RemoteParentSampled, new AlwaysOnSampler(), failFast, path + ".remote_parent_sampled", "always_on");
141+
var remoteParentNotSampled = GetSamplerOrDefault(config.RemoteParentNotSampled, new AlwaysOffSampler(), failFast, path + ".remote_parent_not_sampled", "always_off");
142+
var localParentSampled = GetSamplerOrDefault(config.LocalParentSampled, new AlwaysOnSampler(), failFast, path + ".local_parent_sampled", "always_on");
143+
var localParentNotSampled = GetSamplerOrDefault(config.LocalParentNotSampled, new AlwaysOffSampler(), failFast, path + ".local_parent_not_sampled", "always_off");
144+
145+
return new ParentBasedSampler(rootSampler, remoteParentSampled, remoteParentNotSampled, localParentSampled, localParentNotSampled);
146+
}
147+
148+
private static Sampler GetSamplerOrDefault(SamplerConfig? samplerConfig, Sampler defaultSampler, bool failFast, string path, string defaultSamplerName)
149+
{
150+
if (samplerConfig == null)
151+
{
152+
return defaultSampler;
153+
}
154+
155+
var sampler = CreateSamplerInternal(samplerConfig, failFast, path);
156+
if (sampler != null)
157+
{
158+
return sampler;
159+
}
160+
161+
var message = $"Sampler configuration '{path}' is invalid. Falling back to default '{defaultSamplerName}' sampler.";
162+
Logger.Warning(message);
163+
164+
if (failFast)
165+
{
166+
throw new InvalidOperationException(message);
167+
}
168+
169+
return defaultSampler;
170+
}
171+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
using Vendors.YamlDotNet.Serialization;
5+
6+
namespace OpenTelemetry.AutoInstrumentation.Configurations.FileBasedConfiguration;
7+
8+
internal class TraceIdRatioSamplerConfig
9+
{
10+
[YamlMember(Alias = "ratio")]
11+
public double? Ratio { get; set; }
12+
}

src/OpenTelemetry.AutoInstrumentation/Configurations/FileBasedConfiguration/TracerProviderConfiguration.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,7 @@ internal class TracerProviderConfiguration
99
{
1010
[YamlMember(Alias = "processors")]
1111
public List<ProcessorConfig> Processors { get; set; } = new();
12+
13+
[YamlMember(Alias = "sampler")]
14+
public SamplerConfig? Sampler { get; set; }
1215
}

src/OpenTelemetry.AutoInstrumentation/Configurations/TracerSettings.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using OpenTelemetry.AutoInstrumentation.Configurations.FileBasedConfiguration;
55
using OpenTelemetry.AutoInstrumentation.Configurations.Otlp;
66
using OpenTelemetry.AutoInstrumentation.Logging;
7+
using OpenTelemetry.Trace;
78

89
namespace OpenTelemetry.AutoInstrumentation.Configurations;
910

@@ -63,6 +64,11 @@ internal class TracerSettings : Settings
6364
/// </summary>
6465
public IReadOnlyList<ProcessorConfig>? Processors { get; private set; } = null;
6566

67+
/// <summary>
68+
/// Gets the sampler configured via file-based configuration.
69+
/// </summary>
70+
public Sampler? Sampler { get; private set; }
71+
6672
protected override void OnLoadEnvVar(Configuration configuration)
6773
{
6874
TracesExporters = ParseTracesExporter(configuration);
@@ -112,6 +118,8 @@ protected override void OnLoadFile(YamlConfiguration configuration)
112118
}
113119

114120
Processors = processors;
121+
122+
Sampler = SamplerFactory.CreateSampler(configuration.TracerProvider?.Sampler, configuration.FailFast);
115123
}
116124

117125
private static List<TracesExporter> ParseTracesExporter(Configuration configuration)

0 commit comments

Comments
 (0)