Skip to content

Commit

Permalink
feat: handle OTEL_TRACES_SAMPLER env var (open-telemetry#2111)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
jtmalinowski authored Apr 20, 2021
1 parent 09c2aa7 commit d268bc6
Show file tree
Hide file tree
Showing 12 changed files with 290 additions and 108 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -248,9 +248,11 @@ To request automatic tracing support for a module not on this list, please [file
### 0.18.x to 0.19.0

- All plugins have been removed in favor of instrumentations.

- 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.

- 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.

### 0.17.0 to 0.18.0

- `diag.setLogLevel` is removed and LogLevel can be set by an optional second parameter to `setLogger`
Expand Down
1 change: 1 addition & 0 deletions packages/opentelemetry-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,4 @@ export * from './trace/TraceState';
export * from './trace/IdGenerator';
export * from './utils/url';
export * from './utils/wrap';
export * from './utils/sampling';
11 changes: 5 additions & 6 deletions packages/opentelemetry-core/src/utils/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/

import { DiagLogLevel } from '@opentelemetry/api';
import { TracesSamplerValues } from './sampling';

const DEFAULT_LIST_SEPARATOR = ',';

Expand All @@ -27,7 +28,6 @@ const ENVIRONMENT_NUMBERS_KEYS = [
'OTEL_BSP_MAX_EXPORT_BATCH_SIZE',
'OTEL_BSP_MAX_QUEUE_SIZE',
'OTEL_BSP_SCHEDULE_DELAY',
'OTEL_SAMPLING_PROBABILITY',
'OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT',
'OTEL_SPAN_EVENT_COUNT_LIMIT',
'OTEL_SPAN_LINK_COUNT_LIMIT',
Expand Down Expand Up @@ -70,6 +70,8 @@ export type ENVIRONMENT = {
OTEL_EXPORTER_ZIPKIN_ENDPOINT?: string;
OTEL_LOG_LEVEL?: DiagLogLevel;
OTEL_RESOURCE_ATTRIBUTES?: string;
OTEL_TRACES_SAMPLER_ARG?: string;
OTEL_TRACES_SAMPLER?: string;
} & ENVIRONMENT_NUMBERS &
ENVIRONMENT_LISTS;

Expand Down Expand Up @@ -100,10 +102,11 @@ export const DEFAULT_ENVIRONMENT: Required<ENVIRONMENT> = {
OTEL_NO_PATCH_MODULES: [],
OTEL_PROPAGATORS: ['tracecontext', 'baggage'],
OTEL_RESOURCE_ATTRIBUTES: '',
OTEL_SAMPLING_PROBABILITY: 1,
OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT: 128,
OTEL_SPAN_EVENT_COUNT_LIMIT: 128,
OTEL_SPAN_LINK_COUNT_LIMIT: 128,
OTEL_TRACES_SAMPLER: TracesSamplerValues.ParentBasedAlwaysOn,
OTEL_TRACES_SAMPLER_ARG: '',
};

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

switch (key) {
case 'OTEL_SAMPLING_PROBABILITY':
parseNumber(key, environment, values, 0, 1);
break;

case 'OTEL_LOG_LEVEL':
setLogLevelFromEnv(key, environment, values);
break;
Expand Down
24 changes: 24 additions & 0 deletions packages/opentelemetry-core/src/utils/sampling.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright The OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

export enum TracesSamplerValues {
AlwaysOff = 'always_off',
AlwaysOn = 'always_on',
ParentBasedAlwaysOff = 'parentbased_always_off',
ParentBasedAlwaysOn = 'parentbased_always_on',
ParentBasedTraceIdRatio = 'parentbased_traceidratio',
TraceIdRatio = 'traceidratio',
}
28 changes: 10 additions & 18 deletions packages/opentelemetry-core/test/utils/environment.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
import * as assert from 'assert';
import * as sinon from 'sinon';
import { DiagLogLevel } from '@opentelemetry/api';
import { TracesSamplerValues } from '../../src';

let lastMock: RAW_ENVIRONMENT = {};

Expand Down Expand Up @@ -83,15 +84,15 @@ describe('environment', () => {
OTEL_LOG_LEVEL: 'ERROR',
OTEL_NO_PATCH_MODULES: 'a,b,c',
OTEL_RESOURCE_ATTRIBUTES: '<attrs>',
OTEL_SAMPLING_PROBABILITY: '0.5',
OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT: 10,
OTEL_SPAN_EVENT_COUNT_LIMIT: 20,
OTEL_SPAN_LINK_COUNT_LIMIT: 30,
OTEL_TRACES_SAMPLER: 'always_on',
OTEL_TRACES_SAMPLER_ARG: '0.5',
});
const env = getEnv();
assert.deepStrictEqual(env.OTEL_NO_PATCH_MODULES, ['a', 'b', 'c']);
assert.strictEqual(env.OTEL_LOG_LEVEL, DiagLogLevel.ERROR);
assert.strictEqual(env.OTEL_SAMPLING_PROBABILITY, 0.5);
assert.strictEqual(env.OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT, 10);
assert.strictEqual(env.OTEL_SPAN_EVENT_COUNT_LIMIT, 20);
assert.strictEqual(env.OTEL_SPAN_LINK_COUNT_LIMIT, 30);
Expand All @@ -117,20 +118,8 @@ describe('environment', () => {
assert.strictEqual(env.OTEL_RESOURCE_ATTRIBUTES, '<attrs>');
assert.strictEqual(env.OTEL_BSP_MAX_EXPORT_BATCH_SIZE, 40);
assert.strictEqual(env.OTEL_BSP_SCHEDULE_DELAY, 50);
});

it('should match invalid values to closest valid equivalent', () => {
mockEnvironment({
OTEL_SAMPLING_PROBABILITY: '-0.1',
});
const minEnv = getEnv();
assert.strictEqual(minEnv.OTEL_SAMPLING_PROBABILITY, 0);

mockEnvironment({
OTEL_SAMPLING_PROBABILITY: '1.1',
});
const maxEnv = getEnv();
assert.strictEqual(maxEnv.OTEL_SAMPLING_PROBABILITY, 1);
assert.strictEqual(env.OTEL_TRACES_SAMPLER, 'always_on');
assert.strictEqual(env.OTEL_TRACES_SAMPLER_ARG, '0.5');
});

it('should parse OTEL_LOG_LEVEL despite casing', () => {
Expand Down Expand Up @@ -158,12 +147,15 @@ describe('environment', () => {
it('should remove a mock environment', () => {
mockEnvironment({
OTEL_LOG_LEVEL: 'DEBUG',
OTEL_SAMPLING_PROBABILITY: 0.5,
OTEL_TRACES_SAMPLER: TracesSamplerValues.AlwaysOff,
});
removeMockEnvironment();
const env = getEnv();
assert.strictEqual(env.OTEL_LOG_LEVEL, DiagLogLevel.INFO);
assert.strictEqual(env.OTEL_SAMPLING_PROBABILITY, 1);
assert.strictEqual(
env.OTEL_TRACES_SAMPLER,
TracesSamplerValues.ParentBasedAlwaysOn
);
});
});
});
3 changes: 1 addition & 2 deletions packages/opentelemetry-tracing/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,7 @@ span.end();

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

## Example

Expand Down
85 changes: 83 additions & 2 deletions packages/opentelemetry-tracing/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,18 @@
* limitations under the License.
*/

import { AlwaysOnSampler, getEnv } from '@opentelemetry/core';
import { diag, Sampler } from '@opentelemetry/api';
import {
AlwaysOffSampler,
AlwaysOnSampler,
getEnv,
TracesSamplerValues,
ParentBasedSampler,
TraceIdRatioBasedSampler,
} from '@opentelemetry/core';
import { ENVIRONMENT } from '@opentelemetry/core/src/utils/environment';

const env = getEnv();

/**
* Default configuration. For fields with primitive values, any user-provided
Expand All @@ -23,10 +34,80 @@ import { AlwaysOnSampler, getEnv } from '@opentelemetry/core';
* used to extend the default value.
*/
export const DEFAULT_CONFIG = {
sampler: new AlwaysOnSampler(),
sampler: buildSamplerFromEnv(env),
traceParams: {
numberOfAttributesPerSpan: getEnv().OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT,
numberOfLinksPerSpan: getEnv().OTEL_SPAN_LINK_COUNT_LIMIT,
numberOfEventsPerSpan: getEnv().OTEL_SPAN_EVENT_COUNT_LIMIT,
},
};

const FALLBACK_OTEL_TRACES_SAMPLER = TracesSamplerValues.AlwaysOn;

/**
* Based on environment, builds a sampler, complies with specification.
* @param env optional, by default uses getEnv(), but allows passing a value to reuse parsed environment
*/
export function buildSamplerFromEnv(
env: Required<ENVIRONMENT> = getEnv()
): Sampler {
switch (env.OTEL_TRACES_SAMPLER) {
case TracesSamplerValues.AlwaysOn:
return new AlwaysOnSampler();
case TracesSamplerValues.AlwaysOff:
return new AlwaysOffSampler();
case TracesSamplerValues.ParentBasedAlwaysOn:
return new ParentBasedSampler({
root: new AlwaysOnSampler(),
});
case TracesSamplerValues.ParentBasedAlwaysOff:
return new ParentBasedSampler({
root: new AlwaysOffSampler(),
});
case TracesSamplerValues.TraceIdRatio:
return new TraceIdRatioBasedSampler(getSamplerProbabilityFromEnv(env));
case TracesSamplerValues.ParentBasedTraceIdRatio:
return new ParentBasedSampler({
root: new TraceIdRatioBasedSampler(getSamplerProbabilityFromEnv(env)),
});
default:
diag.error(
`OTEL_TRACES_SAMPLER value "${env.OTEL_TRACES_SAMPLER} invalid, defaulting to ${FALLBACK_OTEL_TRACES_SAMPLER}".`
);
return new AlwaysOnSampler();
}
}

const DEFAULT_RATIO = 1;

function getSamplerProbabilityFromEnv(
env: Required<ENVIRONMENT>
): number | undefined {
if (
env.OTEL_TRACES_SAMPLER_ARG === undefined ||
env.OTEL_TRACES_SAMPLER_ARG === ''
) {
diag.error(
`OTEL_TRACES_SAMPLER_ARG is blank, defaulting to ${DEFAULT_RATIO}.`
);
return DEFAULT_RATIO;
}

const probability = Number(env.OTEL_TRACES_SAMPLER_ARG);

if (isNaN(probability)) {
diag.error(
`OTEL_TRACES_SAMPLER_ARG=${env.OTEL_TRACES_SAMPLER_ARG} was given, but it is invalid, defaulting to ${DEFAULT_RATIO}.`
);
return DEFAULT_RATIO;
}

if (probability < 0 || probability > 1) {
diag.error(
`OTEL_TRACES_SAMPLER_ARG=${env.OTEL_TRACES_SAMPLER_ARG} was given, but it is out of range ([0..1]), defaulting to ${DEFAULT_RATIO}.`
);
return DEFAULT_RATIO;
}

return probability;
}
20 changes: 5 additions & 15 deletions packages/opentelemetry-tracing/src/utility.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,32 +14,22 @@
* limitations under the License.
*/

import { DEFAULT_CONFIG } from './config';
import { buildSamplerFromEnv, DEFAULT_CONFIG } from './config';
import { TracerConfig } from './types';
import {
ParentBasedSampler,
TraceIdRatioBasedSampler,
getEnv,
} from '@opentelemetry/core';

/**
* Function to merge Default configuration (as specified in './config') with
* user provided configurations.
*/
export function mergeConfig(userConfig: TracerConfig) {
const otelSamplingProbability = getEnv().OTEL_SAMPLING_PROBABILITY;
const perInstanceDefaults: Partial<TracerConfig> = {
sampler: buildSamplerFromEnv(),
};

const target = Object.assign(
{},
DEFAULT_CONFIG,
// use default AlwaysOnSampler if otelSamplingProbability is 1
otelSamplingProbability !== undefined && otelSamplingProbability < 1
? {
sampler: new ParentBasedSampler({
root: new TraceIdRatioBasedSampler(otelSamplingProbability),
}),
}
: {},
perInstanceDefaults,
userConfig
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,9 @@ describe('BasicTracerProvider', () => {
});

it('should start a span with name and with invalid parent span', () => {
const tracer = new BasicTracerProvider().getTracer('default');
const tracer = new BasicTracerProvider({
sampler: new AlwaysOnSampler(),
}).getTracer('default');
const span = tracer.startSpan(
'my-span',
{},
Expand Down
Loading

0 comments on commit d268bc6

Please sign in to comment.