Skip to content

Commit

Permalink
feat: handle SDK environment variables
Browse files Browse the repository at this point in the history
  • Loading branch information
jtmalinowski committed Feb 10, 2021
1 parent 2395de6 commit 8ecec4a
Show file tree
Hide file tree
Showing 14 changed files with 448 additions and 65 deletions.
22 changes: 22 additions & 0 deletions packages/opentelemetry-api/src/api/propagation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
defaultTextMapSetter,
TextMapGetter,
TextMapPropagator,
TextMapPropagatorFactory,
TextMapSetter,
} from '../context/propagation/TextMapPropagator';
import {
Expand All @@ -36,6 +37,8 @@ import {
export class PropagationAPI {
private static _instance?: PropagationAPI;

private _namedPropagatorsMap = new Map<string, TextMapPropagatorFactory>();

/** Empty private constructor prevents end users from constructing a new instance of the API */
private constructor() {}

Expand Down Expand Up @@ -108,6 +111,25 @@ export class PropagationAPI {
delete _global[GLOBAL_PROPAGATION_API_KEY];
}

/**
* Adds a propagator factory under a unique name
*/
public registerNamedPropagator(
name: string,
factory: TextMapPropagatorFactory
) {
this._namedPropagatorsMap.set(name, factory);
}

/**
* Instantiates a propagator using a factory registered under a given name
*/
public getRegisteredPropagatorByName(
name: string
): TextMapPropagator | undefined {
return this._namedPropagatorsMap.get(name)?.();
}

private _getGlobalPropagator(): TextMapPropagator {
return (
_global[GLOBAL_PROPAGATION_API_KEY]?.(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,3 +135,5 @@ export const defaultTextMapSetter: TextMapSetter = {
carrier[key] = value;
},
};

export type TextMapPropagatorFactory = () => TextMapPropagator;
33 changes: 33 additions & 0 deletions packages/opentelemetry-api/test/api/api.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,4 +177,37 @@ describe('API', () => {
});
});
});

describe('registering named propagators', () => {
it('returns an instance of a registered propagator', () => {
class DummyPropagator implements TextMapPropagator {
inject(
context: Context,
carrier: any,
setter: TextMapSetter<any>
): void {
throw new Error('Method not implemented.');
}
extract(
context: Context,
carrier: any,
getter: TextMapGetter<any>
): Context {
throw new Error('Method not implemented.');
}
fields(): string[] {
throw new Error('Method not implemented.');
}
}

api.propagation.registerNamedPropagator(
'dummy',
() => new DummyPropagator()
);
assert.ok(
api.propagation.getRegisteredPropagatorByName('dummy') instanceof
DummyPropagator
);
});
});
});
10 changes: 9 additions & 1 deletion packages/opentelemetry-core/src/utils/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,10 @@ function isEnvVarANumber(key: unknown): key is keyof ENVIRONMENT_NUMBERS {
);
}

const ENVIRONMENT_LISTS_KEYS = ['OTEL_NO_PATCH_MODULES'] as const;
const ENVIRONMENT_LISTS_KEYS = [
'OTEL_NO_PATCH_MODULES',
'OTEL_PROPAGATORS',
] as const;

type ENVIRONMENT_LISTS = {
[K in typeof ENVIRONMENT_LISTS_KEYS[number]]?: string[];
Expand All @@ -64,6 +67,8 @@ export type ENVIRONMENT = {
OTEL_EXPORTER_JAEGER_USER?: string;
OTEL_LOG_LEVEL?: LogLevel;
OTEL_RESOURCE_ATTRIBUTES?: string;
OTEL_TRACE_SAMPLER?: string;
OTEL_TRACE_SAMPLER_ARG?: string;
} & ENVIRONMENT_NUMBERS &
ENVIRONMENT_LISTS;

Expand All @@ -88,12 +93,15 @@ export const DEFAULT_ENVIRONMENT: Required<ENVIRONMENT> = {
OTEL_LOG_LEVEL: LogLevel.INFO,
OTEL_NO_PATCH_MODULES: [],
OTEL_RESOURCE_ATTRIBUTES: '',
OTEL_PROPAGATORS: ['tracecontext', 'baggage'],
OTEL_SAMPLING_PROBABILITY: 1,
OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT: 1000,
OTEL_SPAN_EVENT_COUNT_LIMIT: 1000,
OTEL_SPAN_LINK_COUNT_LIMIT: 1000,
OTEL_BSP_MAX_BATCH_SIZE: 512,
OTEL_BSP_SCHEDULE_DELAY_MILLIS: 5000,
OTEL_TRACE_SAMPLER: 'parentbased_always_on',
OTEL_TRACE_SAMPLER_ARG: '',
};

/**
Expand Down
2 changes: 2 additions & 0 deletions packages/opentelemetry-node/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@
"@opentelemetry/context-async-hooks": "^0.16.0",
"@opentelemetry/core": "^0.16.0",
"@opentelemetry/tracing": "^0.16.0",
"@opentelemetry/propagator-b3": "^0.16.0",
"@opentelemetry/propagator-jaeger": "^0.13.1",
"semver": "^7.1.3"
}
}
17 changes: 17 additions & 0 deletions packages/opentelemetry-node/src/NodeTracerProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,34 @@
* limitations under the License.
*/

import * as api from '@opentelemetry/api';
import {
AsyncHooksContextManager,
AsyncLocalStorageContextManager,
} from '@opentelemetry/context-async-hooks';
import {
B3SinglePropagator,
B3MultiPropagator,
} from '@opentelemetry/propagator-b3';
import {
BasicTracerProvider,
SDKRegistrationConfig,
} from '@opentelemetry/tracing';
import * as semver from 'semver';
import { NodeTracerConfig } from './config';

// TODO: uncomment when new version of @opentelemetry/propagator-jaeger is released
// import { JaegerHttpTracePropagator } from '@opentelemetry/propagator-jaeger';

api.propagation.registerNamedPropagator('b3', () => new B3SinglePropagator());
api.propagation.registerNamedPropagator(
'b3multi',
() => new B3MultiPropagator()
);

// TODO: uncomment when new version of @opentelemetry/propagator-jaeger is released
// api.propagation.registerNamedPropagator('jaeger', () => new JaegerHttpTracePropagator);

/**
* Register this TracerProvider for use with the OpenTelemetry API.
* Undefined values may be replaced with defaults, and
Expand Down
3 changes: 3 additions & 0 deletions packages/opentelemetry-node/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
{
"path": "../opentelemetry-core"
},
{
"path": "../opentelemetry-propagator-b3"
},
{
"path": "../opentelemetry-resources"
},
Expand Down
43 changes: 41 additions & 2 deletions packages/opentelemetry-tracing/src/BasicTracerProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ import * as api from '@opentelemetry/api';
import {
CompositePropagator,
ConsoleLogger,
HttpBaggage,
HttpTraceContext,
HttpBaggage,
getEnv,
} from '@opentelemetry/core';
import { Resource } from '@opentelemetry/resources';
import { SpanProcessor, Tracer } from '.';
Expand All @@ -28,6 +29,17 @@ import { MultiSpanProcessor } from './MultiSpanProcessor';
import { NoopSpanProcessor } from './NoopSpanProcessor';
import { SDKRegistrationConfig, TracerConfig } from './types';
import merge = require('lodash.merge');
import { TextMapPropagator } from '@opentelemetry/api';

/**
* register core propagators on startup
*/
api.propagation.registerNamedPropagator(
'tracecontext',
() => new HttpTraceContext()
);
api.propagation.registerNamedPropagator('baggage', () => new HttpBaggage());

/**
* This class represents a basic tracer provider which platform libraries can extend
*/
Expand Down Expand Up @@ -86,8 +98,10 @@ export class BasicTracerProvider implements api.TracerProvider {
register(config: SDKRegistrationConfig = {}) {
api.trace.setGlobalTracerProvider(this);
if (config.propagator === undefined) {
const propagators = this._getPropagatorsFromEnv();

config.propagator = new CompositePropagator({
propagators: [new HttpBaggage(), new HttpTraceContext()],
propagators,
});
}

Expand All @@ -103,4 +117,29 @@ export class BasicTracerProvider implements api.TracerProvider {
shutdown() {
return this.activeSpanProcessor.shutdown();
}

_getPropagatorsFromEnv() {
const result: TextMapPropagator[] = [];
getEnv().OTEL_PROPAGATORS.forEach(propagatorName => {
const propagator = api.propagation.getRegisteredPropagatorByName(
propagatorName
);
if (propagator) {
result.push(propagator);
} else if (propagatorName === 'b3' || propagatorName === 'b3multi') {
this.logger.warn(
'B3 propagators unavailable, please add @opentelemetry/propagator-b3 to dependencies, and register using api.propagation.registerNamedPropagator().'
);
} else if (propagatorName === 'jaeger') {
this.logger.warn(
'Jaeger propagator unavailable, please add @opentelemetry/propagator-jaeger to dependencies, and register using api.propagation.registerNamedPropagator().'
);
} else {
this.logger.warn(
`Propagator with key "${propagatorName}" was requested, but wasn't registered.`
);
}
});
return result;
}
}
67 changes: 62 additions & 5 deletions packages/opentelemetry-tracing/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,16 @@
* limitations under the License.
*/

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

const env = getEnv();

/**
* Default configuration. For fields with primitive values, any user-provided
Expand All @@ -24,10 +33,58 @@ import { AlwaysOnSampler, getEnv } from '@opentelemetry/core';
*/
export const DEFAULT_CONFIG = {
logLevel: getEnv().OTEL_LOG_LEVEL,
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,
numberOfAttributesPerSpan: env.OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT,
numberOfLinksPerSpan: env.OTEL_SPAN_LINK_COUNT_LIMIT,
numberOfEventsPerSpan: env.OTEL_SPAN_EVENT_COUNT_LIMIT,
},
};

/**
* 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()) {
const probability = getSamplerProbabilityFromEnv(env);
switch (env.OTEL_TRACE_SAMPLER) {
case 'always_on':
return new AlwaysOnSampler();
case 'always_off':
return new AlwaysOffSampler();
case 'traceidratio':
return new TraceIdRatioBasedSampler(probability);
case 'parentbased_always_on':
return new ParentBasedSampler({
root: new AlwaysOnSampler(),
});
case 'parentbased_always_off':
return new ParentBasedSampler({
root: new AlwaysOffSampler(),
});
case 'parentbased_traceidratio':
return new ParentBasedSampler({
root: new TraceIdRatioBasedSampler(probability),
});
default:
// use default AlwaysOnSampler if otelSamplingProbability is 1
if (probability < 1) {
return new ParentBasedSampler({
root: new TraceIdRatioBasedSampler(probability),
});
}
return new AlwaysOnSampler();
}
}

function getSamplerProbabilityFromEnv(env: Required<ENVIRONMENT>) {
const valueFromGenericArg =
env.OTEL_TRACE_SAMPLER_ARG !== ''
? Number(env.OTEL_TRACE_SAMPLER_ARG)
: NaN;
if (!isNaN(valueFromGenericArg)) {
return valueFromGenericArg;
}

return env.OTEL_SAMPLING_PROBABILITY;
}
21 changes: 5 additions & 16 deletions packages/opentelemetry-tracing/src/utility.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,35 +14,24 @@
* 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
);

target.traceParams = Object.assign(
{},
DEFAULT_CONFIG.traceParams,
Expand Down
Loading

0 comments on commit 8ecec4a

Please sign in to comment.