Skip to content

Commit d268bc6

Browse files
authored
feat: handle OTEL_TRACES_SAMPLER env var (#2111)
* feat: handle OTEL_TRACES_SAMPLER env var Adds support for OTEL_TRACES_SAMPLER and OTEL_TRACES_SAMPLER_ARG, in favor of OTEL_SAMPLING_PROBABILITY. * fix: feedback in the PR * refactor: extract OTEL_TRACES_SAMPLER values to enum
1 parent 09c2aa7 commit d268bc6

File tree

12 files changed

+290
-108
lines changed

12 files changed

+290
-108
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,9 +248,11 @@ To request automatic tracing support for a module not on this list, please [file
248248
### 0.18.x to 0.19.0
249249

250250
- All plugins have been removed in favor of instrumentations.
251-
251+
252252
- The `@opentelemetry/propagator-b3` package previously exported three propagators: `B3Propagator`,`B3SinglePropagator`, and `B3MultiPropagator`, but now only exports the `B3Propagator`. It extracts b3 context in single and multi-header encodings, and injects context using the single-header encoding by default, but can be configured to inject context using the multi-header endcoding during construction: `new B3Propagator({ injectEncoding: B3InjectEncoding.MULTI_HEADER })`. If you were previously using the `B3SinglePropagator` or `B3MultiPropagator` directly, you should update your code to use the `B3Propagator` with the appropriate configuration. See the [readme](./packages/opentelemetry-propagator-b3/readme.md) for full details and usage.
253253

254+
- Sampling configuration via environment variable has changed. If you were using `OTEL_SAMPLING_PROBABILITY` then you should replace it with `OTEL_TRACES_SAMPLER=parentbased_traceidratio` and `OTEL_TRACES_SAMPLER_ARG=<number>` where `<number>` is a number in the [0..1] range, e.g. "0.25". Default is 1.0 if unset.
255+
254256
### 0.17.0 to 0.18.0
255257

256258
- `diag.setLogLevel` is removed and LogLevel can be set by an optional second parameter to `setLogger`

packages/opentelemetry-core/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,4 @@ export * from './trace/TraceState';
3434
export * from './trace/IdGenerator';
3535
export * from './utils/url';
3636
export * from './utils/wrap';
37+
export * from './utils/sampling';

packages/opentelemetry-core/src/utils/environment.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616

1717
import { DiagLogLevel } from '@opentelemetry/api';
18+
import { TracesSamplerValues } from './sampling';
1819

1920
const DEFAULT_LIST_SEPARATOR = ',';
2021

@@ -27,7 +28,6 @@ const ENVIRONMENT_NUMBERS_KEYS = [
2728
'OTEL_BSP_MAX_EXPORT_BATCH_SIZE',
2829
'OTEL_BSP_MAX_QUEUE_SIZE',
2930
'OTEL_BSP_SCHEDULE_DELAY',
30-
'OTEL_SAMPLING_PROBABILITY',
3131
'OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT',
3232
'OTEL_SPAN_EVENT_COUNT_LIMIT',
3333
'OTEL_SPAN_LINK_COUNT_LIMIT',
@@ -70,6 +70,8 @@ export type ENVIRONMENT = {
7070
OTEL_EXPORTER_ZIPKIN_ENDPOINT?: string;
7171
OTEL_LOG_LEVEL?: DiagLogLevel;
7272
OTEL_RESOURCE_ATTRIBUTES?: string;
73+
OTEL_TRACES_SAMPLER_ARG?: string;
74+
OTEL_TRACES_SAMPLER?: string;
7375
} & ENVIRONMENT_NUMBERS &
7476
ENVIRONMENT_LISTS;
7577

@@ -100,10 +102,11 @@ export const DEFAULT_ENVIRONMENT: Required<ENVIRONMENT> = {
100102
OTEL_NO_PATCH_MODULES: [],
101103
OTEL_PROPAGATORS: ['tracecontext', 'baggage'],
102104
OTEL_RESOURCE_ATTRIBUTES: '',
103-
OTEL_SAMPLING_PROBABILITY: 1,
104105
OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT: 128,
105106
OTEL_SPAN_EVENT_COUNT_LIMIT: 128,
106107
OTEL_SPAN_LINK_COUNT_LIMIT: 128,
108+
OTEL_TRACES_SAMPLER: TracesSamplerValues.ParentBasedAlwaysOn,
109+
OTEL_TRACES_SAMPLER_ARG: '',
107110
};
108111

109112
/**
@@ -196,10 +199,6 @@ export function parseEnvironment(values: RAW_ENVIRONMENT): ENVIRONMENT {
196199
const key = env as keyof ENVIRONMENT;
197200

198201
switch (key) {
199-
case 'OTEL_SAMPLING_PROBABILITY':
200-
parseNumber(key, environment, values, 0, 1);
201-
break;
202-
203202
case 'OTEL_LOG_LEVEL':
204203
setLogLevelFromEnv(key, environment, values);
205204
break;
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
export enum TracesSamplerValues {
18+
AlwaysOff = 'always_off',
19+
AlwaysOn = 'always_on',
20+
ParentBasedAlwaysOff = 'parentbased_always_off',
21+
ParentBasedAlwaysOn = 'parentbased_always_on',
22+
ParentBasedTraceIdRatio = 'parentbased_traceidratio',
23+
TraceIdRatio = 'traceidratio',
24+
}

packages/opentelemetry-core/test/utils/environment.test.ts

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
import * as assert from 'assert';
2424
import * as sinon from 'sinon';
2525
import { DiagLogLevel } from '@opentelemetry/api';
26+
import { TracesSamplerValues } from '../../src';
2627

2728
let lastMock: RAW_ENVIRONMENT = {};
2829

@@ -83,15 +84,15 @@ describe('environment', () => {
8384
OTEL_LOG_LEVEL: 'ERROR',
8485
OTEL_NO_PATCH_MODULES: 'a,b,c',
8586
OTEL_RESOURCE_ATTRIBUTES: '<attrs>',
86-
OTEL_SAMPLING_PROBABILITY: '0.5',
8787
OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT: 10,
8888
OTEL_SPAN_EVENT_COUNT_LIMIT: 20,
8989
OTEL_SPAN_LINK_COUNT_LIMIT: 30,
90+
OTEL_TRACES_SAMPLER: 'always_on',
91+
OTEL_TRACES_SAMPLER_ARG: '0.5',
9092
});
9193
const env = getEnv();
9294
assert.deepStrictEqual(env.OTEL_NO_PATCH_MODULES, ['a', 'b', 'c']);
9395
assert.strictEqual(env.OTEL_LOG_LEVEL, DiagLogLevel.ERROR);
94-
assert.strictEqual(env.OTEL_SAMPLING_PROBABILITY, 0.5);
9596
assert.strictEqual(env.OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT, 10);
9697
assert.strictEqual(env.OTEL_SPAN_EVENT_COUNT_LIMIT, 20);
9798
assert.strictEqual(env.OTEL_SPAN_LINK_COUNT_LIMIT, 30);
@@ -117,20 +118,8 @@ describe('environment', () => {
117118
assert.strictEqual(env.OTEL_RESOURCE_ATTRIBUTES, '<attrs>');
118119
assert.strictEqual(env.OTEL_BSP_MAX_EXPORT_BATCH_SIZE, 40);
119120
assert.strictEqual(env.OTEL_BSP_SCHEDULE_DELAY, 50);
120-
});
121-
122-
it('should match invalid values to closest valid equivalent', () => {
123-
mockEnvironment({
124-
OTEL_SAMPLING_PROBABILITY: '-0.1',
125-
});
126-
const minEnv = getEnv();
127-
assert.strictEqual(minEnv.OTEL_SAMPLING_PROBABILITY, 0);
128-
129-
mockEnvironment({
130-
OTEL_SAMPLING_PROBABILITY: '1.1',
131-
});
132-
const maxEnv = getEnv();
133-
assert.strictEqual(maxEnv.OTEL_SAMPLING_PROBABILITY, 1);
121+
assert.strictEqual(env.OTEL_TRACES_SAMPLER, 'always_on');
122+
assert.strictEqual(env.OTEL_TRACES_SAMPLER_ARG, '0.5');
134123
});
135124

136125
it('should parse OTEL_LOG_LEVEL despite casing', () => {
@@ -158,12 +147,15 @@ describe('environment', () => {
158147
it('should remove a mock environment', () => {
159148
mockEnvironment({
160149
OTEL_LOG_LEVEL: 'DEBUG',
161-
OTEL_SAMPLING_PROBABILITY: 0.5,
150+
OTEL_TRACES_SAMPLER: TracesSamplerValues.AlwaysOff,
162151
});
163152
removeMockEnvironment();
164153
const env = getEnv();
165154
assert.strictEqual(env.OTEL_LOG_LEVEL, DiagLogLevel.INFO);
166-
assert.strictEqual(env.OTEL_SAMPLING_PROBABILITY, 1);
155+
assert.strictEqual(
156+
env.OTEL_TRACES_SAMPLER,
157+
TracesSamplerValues.ParentBasedAlwaysOn
158+
);
167159
});
168160
});
169161
});

packages/opentelemetry-tracing/README.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,7 @@ span.end();
4646

4747
Tracing configuration is a merge of user supplied configuration with both the default
4848
configuration as specified in [config.ts](./src/config.ts) and an
49-
environmentally configurable (via `OTEL_SAMPLING_PROBABILITY`) probability
50-
sampler delegate of a [ParentBased](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/sdk.md#parentbased) sampler.
49+
environmentally configurable sampling (via `OTEL_TRACES_SAMPLER` and `OTEL_TRACES_SAMPLER_ARG`).
5150

5251
## Example
5352

packages/opentelemetry-tracing/src/config.ts

Lines changed: 83 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,18 @@
1414
* limitations under the License.
1515
*/
1616

17-
import { AlwaysOnSampler, getEnv } from '@opentelemetry/core';
17+
import { diag, Sampler } from '@opentelemetry/api';
18+
import {
19+
AlwaysOffSampler,
20+
AlwaysOnSampler,
21+
getEnv,
22+
TracesSamplerValues,
23+
ParentBasedSampler,
24+
TraceIdRatioBasedSampler,
25+
} from '@opentelemetry/core';
26+
import { ENVIRONMENT } from '@opentelemetry/core/src/utils/environment';
27+
28+
const env = getEnv();
1829

1930
/**
2031
* Default configuration. For fields with primitive values, any user-provided
@@ -23,10 +34,80 @@ import { AlwaysOnSampler, getEnv } from '@opentelemetry/core';
2334
* used to extend the default value.
2435
*/
2536
export const DEFAULT_CONFIG = {
26-
sampler: new AlwaysOnSampler(),
37+
sampler: buildSamplerFromEnv(env),
2738
traceParams: {
2839
numberOfAttributesPerSpan: getEnv().OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT,
2940
numberOfLinksPerSpan: getEnv().OTEL_SPAN_LINK_COUNT_LIMIT,
3041
numberOfEventsPerSpan: getEnv().OTEL_SPAN_EVENT_COUNT_LIMIT,
3142
},
3243
};
44+
45+
const FALLBACK_OTEL_TRACES_SAMPLER = TracesSamplerValues.AlwaysOn;
46+
47+
/**
48+
* Based on environment, builds a sampler, complies with specification.
49+
* @param env optional, by default uses getEnv(), but allows passing a value to reuse parsed environment
50+
*/
51+
export function buildSamplerFromEnv(
52+
env: Required<ENVIRONMENT> = getEnv()
53+
): Sampler {
54+
switch (env.OTEL_TRACES_SAMPLER) {
55+
case TracesSamplerValues.AlwaysOn:
56+
return new AlwaysOnSampler();
57+
case TracesSamplerValues.AlwaysOff:
58+
return new AlwaysOffSampler();
59+
case TracesSamplerValues.ParentBasedAlwaysOn:
60+
return new ParentBasedSampler({
61+
root: new AlwaysOnSampler(),
62+
});
63+
case TracesSamplerValues.ParentBasedAlwaysOff:
64+
return new ParentBasedSampler({
65+
root: new AlwaysOffSampler(),
66+
});
67+
case TracesSamplerValues.TraceIdRatio:
68+
return new TraceIdRatioBasedSampler(getSamplerProbabilityFromEnv(env));
69+
case TracesSamplerValues.ParentBasedTraceIdRatio:
70+
return new ParentBasedSampler({
71+
root: new TraceIdRatioBasedSampler(getSamplerProbabilityFromEnv(env)),
72+
});
73+
default:
74+
diag.error(
75+
`OTEL_TRACES_SAMPLER value "${env.OTEL_TRACES_SAMPLER} invalid, defaulting to ${FALLBACK_OTEL_TRACES_SAMPLER}".`
76+
);
77+
return new AlwaysOnSampler();
78+
}
79+
}
80+
81+
const DEFAULT_RATIO = 1;
82+
83+
function getSamplerProbabilityFromEnv(
84+
env: Required<ENVIRONMENT>
85+
): number | undefined {
86+
if (
87+
env.OTEL_TRACES_SAMPLER_ARG === undefined ||
88+
env.OTEL_TRACES_SAMPLER_ARG === ''
89+
) {
90+
diag.error(
91+
`OTEL_TRACES_SAMPLER_ARG is blank, defaulting to ${DEFAULT_RATIO}.`
92+
);
93+
return DEFAULT_RATIO;
94+
}
95+
96+
const probability = Number(env.OTEL_TRACES_SAMPLER_ARG);
97+
98+
if (isNaN(probability)) {
99+
diag.error(
100+
`OTEL_TRACES_SAMPLER_ARG=${env.OTEL_TRACES_SAMPLER_ARG} was given, but it is invalid, defaulting to ${DEFAULT_RATIO}.`
101+
);
102+
return DEFAULT_RATIO;
103+
}
104+
105+
if (probability < 0 || probability > 1) {
106+
diag.error(
107+
`OTEL_TRACES_SAMPLER_ARG=${env.OTEL_TRACES_SAMPLER_ARG} was given, but it is out of range ([0..1]), defaulting to ${DEFAULT_RATIO}.`
108+
);
109+
return DEFAULT_RATIO;
110+
}
111+
112+
return probability;
113+
}

packages/opentelemetry-tracing/src/utility.ts

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,32 +14,22 @@
1414
* limitations under the License.
1515
*/
1616

17-
import { DEFAULT_CONFIG } from './config';
17+
import { buildSamplerFromEnv, DEFAULT_CONFIG } from './config';
1818
import { TracerConfig } from './types';
19-
import {
20-
ParentBasedSampler,
21-
TraceIdRatioBasedSampler,
22-
getEnv,
23-
} from '@opentelemetry/core';
2419

2520
/**
2621
* Function to merge Default configuration (as specified in './config') with
2722
* user provided configurations.
2823
*/
2924
export function mergeConfig(userConfig: TracerConfig) {
30-
const otelSamplingProbability = getEnv().OTEL_SAMPLING_PROBABILITY;
25+
const perInstanceDefaults: Partial<TracerConfig> = {
26+
sampler: buildSamplerFromEnv(),
27+
};
3128

3229
const target = Object.assign(
3330
{},
3431
DEFAULT_CONFIG,
35-
// use default AlwaysOnSampler if otelSamplingProbability is 1
36-
otelSamplingProbability !== undefined && otelSamplingProbability < 1
37-
? {
38-
sampler: new ParentBasedSampler({
39-
root: new TraceIdRatioBasedSampler(otelSamplingProbability),
40-
}),
41-
}
42-
: {},
32+
perInstanceDefaults,
4333
userConfig
4434
);
4535

packages/opentelemetry-tracing/test/BasicTracerProvider.test.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,9 @@ describe('BasicTracerProvider', () => {
315315
});
316316

317317
it('should start a span with name and with invalid parent span', () => {
318-
const tracer = new BasicTracerProvider().getTracer('default');
318+
const tracer = new BasicTracerProvider({
319+
sampler: new AlwaysOnSampler(),
320+
}).getTracer('default');
319321
const span = tracer.startSpan(
320322
'my-span',
321323
{},

0 commit comments

Comments
 (0)