feat: enable propagators using ENV vars (#1929)
* feat: enable propagators using ENV vars * fix: browser tests * fix: remove rebase mistakes * fix: feedback from PR Co-authored-by: Valentin Marchaud <contact@vmarchaud.fr>
This commit is contained in:
parent
031b0f4286
commit
9fc1b109e7
|
|
@ -41,7 +41,7 @@ export class CompositePropagator implements TextMapPropagator {
|
|||
this._propagators
|
||||
// older propagators may not have fields function, null check to be sure
|
||||
.map(p => (typeof p.fields === 'function' ? p.fields() : []))
|
||||
.reduce((x, y) => x.concat(y))
|
||||
.reduce((x, y) => x.concat(y), [])
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,7 +43,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[];
|
||||
|
|
@ -83,21 +86,22 @@ export const DEFAULT_ENVIRONMENT: Required<ENVIRONMENT> = {
|
|||
HOSTNAME: '',
|
||||
KUBERNETES_SERVICE_HOST: '',
|
||||
NAMESPACE: '',
|
||||
OTEL_BSP_EXPORT_TIMEOUT: 30000,
|
||||
OTEL_BSP_MAX_EXPORT_BATCH_SIZE: 512,
|
||||
OTEL_BSP_MAX_QUEUE_SIZE: 2048,
|
||||
OTEL_BSP_SCHEDULE_DELAY: 5000,
|
||||
OTEL_EXPORTER_JAEGER_AGENT_HOST: '',
|
||||
OTEL_EXPORTER_JAEGER_ENDPOINT: '',
|
||||
OTEL_EXPORTER_JAEGER_PASSWORD: '',
|
||||
OTEL_EXPORTER_JAEGER_USER: '',
|
||||
OTEL_LOG_LEVEL: DiagLogLevel.INFO,
|
||||
OTEL_NO_PATCH_MODULES: [],
|
||||
OTEL_PROPAGATORS: ['tracecontext', 'baggage'],
|
||||
OTEL_RESOURCE_ATTRIBUTES: '',
|
||||
OTEL_SAMPLING_PROBABILITY: 1,
|
||||
OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT: 1000,
|
||||
OTEL_SPAN_EVENT_COUNT_LIMIT: 1000,
|
||||
OTEL_SPAN_LINK_COUNT_LIMIT: 1000,
|
||||
OTEL_BSP_EXPORT_TIMEOUT: 30000,
|
||||
OTEL_BSP_MAX_EXPORT_BATCH_SIZE: 512,
|
||||
OTEL_BSP_MAX_QUEUE_SIZE: 2048,
|
||||
OTEL_BSP_SCHEDULE_DELAY: 5000,
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -66,6 +66,8 @@
|
|||
"@opentelemetry/context-async-hooks": "^0.18.2",
|
||||
"@opentelemetry/core": "^0.18.2",
|
||||
"@opentelemetry/tracing": "^0.18.2",
|
||||
"@opentelemetry/propagator-b3": "^0.18.0",
|
||||
"@opentelemetry/propagator-jaeger": "^0.18.0",
|
||||
"semver": "^7.1.3"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,17 +13,20 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { TextMapPropagator } from '@opentelemetry/api';
|
||||
import {
|
||||
AsyncHooksContextManager,
|
||||
AsyncLocalStorageContextManager,
|
||||
} from '@opentelemetry/context-async-hooks';
|
||||
import { B3Propagator, B3InjectEncoding } from '@opentelemetry/propagator-b3';
|
||||
import {
|
||||
BasicTracerProvider,
|
||||
PROPAGATOR_FACTORY,
|
||||
SDKRegistrationConfig,
|
||||
} from '@opentelemetry/tracing';
|
||||
import * as semver from 'semver';
|
||||
import { NodeTracerConfig } from './config';
|
||||
import { JaegerHttpTracePropagator } from '@opentelemetry/propagator-jaeger';
|
||||
|
||||
/**
|
||||
* Register this TracerProvider for use with the OpenTelemetry API.
|
||||
|
|
@ -33,6 +36,22 @@ import { NodeTracerConfig } from './config';
|
|||
* @param config Configuration object for SDK registration
|
||||
*/
|
||||
export class NodeTracerProvider extends BasicTracerProvider {
|
||||
protected static readonly _registeredPropagators = new Map<
|
||||
string,
|
||||
PROPAGATOR_FACTORY
|
||||
>([
|
||||
[
|
||||
'b3',
|
||||
() =>
|
||||
new B3Propagator({ injectEncoding: B3InjectEncoding.SINGLE_HEADER }),
|
||||
],
|
||||
[
|
||||
'b3multi',
|
||||
() => new B3Propagator({ injectEncoding: B3InjectEncoding.MULTI_HEADER }),
|
||||
],
|
||||
['jaeger', () => new JaegerHttpTracePropagator()],
|
||||
]);
|
||||
|
||||
constructor(config: NodeTracerConfig = {}) {
|
||||
super(config);
|
||||
}
|
||||
|
|
@ -48,4 +67,11 @@ export class NodeTracerProvider extends BasicTracerProvider {
|
|||
|
||||
super.register(config);
|
||||
}
|
||||
|
||||
protected _getPropagator(name: string): TextMapPropagator | undefined {
|
||||
return (
|
||||
super._getPropagator(name) ||
|
||||
NodeTracerProvider._registeredPropagators.get(name)?.()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import {
|
|||
setSpan,
|
||||
setSpanContext,
|
||||
getSpan,
|
||||
propagation,
|
||||
} from '@opentelemetry/api';
|
||||
import { AlwaysOnSampler, AlwaysOffSampler } from '@opentelemetry/core';
|
||||
import { AsyncHooksContextManager } from '@opentelemetry/context-async-hooks';
|
||||
|
|
@ -211,4 +212,37 @@ describe('NodeTracerProvider', () => {
|
|||
return patchedFn();
|
||||
});
|
||||
});
|
||||
|
||||
describe('.register()', () => {
|
||||
let originalPropagators: string | number | undefined | string[];
|
||||
beforeEach(() => {
|
||||
originalPropagators = process.env.OTEL_PROPAGATORS;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
// otherwise we may assign 'undefined' (a string)
|
||||
if (originalPropagators !== undefined) {
|
||||
(process.env as any).OTEL_PROPAGATORS = originalPropagators;
|
||||
} else {
|
||||
delete (process.env as any).OTEL_PROPAGATORS;
|
||||
}
|
||||
});
|
||||
|
||||
it('should allow propagators as per the specification', () => {
|
||||
(process.env as any).OTEL_PROPAGATORS = 'b3,b3multi,jaeger';
|
||||
|
||||
const provider = new NodeTracerProvider();
|
||||
provider.register();
|
||||
|
||||
assert.deepStrictEqual(propagation.fields(), [
|
||||
'b3',
|
||||
'x-b3-traceid',
|
||||
'x-b3-spanid',
|
||||
'x-b3-flags',
|
||||
'x-b3-sampled',
|
||||
'x-b3-parentspanid',
|
||||
'uber-trace-id',
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -15,6 +15,12 @@
|
|||
{
|
||||
"path": "../opentelemetry-core"
|
||||
},
|
||||
{
|
||||
"path": "../opentelemetry-propagator-b3"
|
||||
},
|
||||
{
|
||||
"path": "../opentelemetry-propagator-jaeger"
|
||||
},
|
||||
{
|
||||
"path": "../opentelemetry-resources"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -19,11 +19,14 @@ import {
|
|||
trace,
|
||||
context,
|
||||
propagation,
|
||||
TextMapPropagator,
|
||||
diag,
|
||||
} from '@opentelemetry/api';
|
||||
import {
|
||||
CompositePropagator,
|
||||
HttpBaggage,
|
||||
HttpTraceContext,
|
||||
HttpBaggage,
|
||||
getEnv,
|
||||
} from '@opentelemetry/core';
|
||||
import { Resource } from '@opentelemetry/resources';
|
||||
import { SpanProcessor, Tracer } from '.';
|
||||
|
|
@ -32,10 +35,21 @@ import { MultiSpanProcessor } from './MultiSpanProcessor';
|
|||
import { NoopSpanProcessor } from './NoopSpanProcessor';
|
||||
import { SDKRegistrationConfig, TracerConfig } from './types';
|
||||
import merge = require('lodash.merge');
|
||||
|
||||
export type PROPAGATOR_FACTORY = () => TextMapPropagator;
|
||||
|
||||
/**
|
||||
* This class represents a basic tracer provider which platform libraries can extend
|
||||
*/
|
||||
export class BasicTracerProvider implements TracerProvider {
|
||||
protected static readonly _registeredPropagators = new Map<
|
||||
string,
|
||||
PROPAGATOR_FACTORY
|
||||
>([
|
||||
['tracecontext', () => new HttpTraceContext()],
|
||||
['baggage', () => new HttpBaggage()],
|
||||
]);
|
||||
|
||||
private readonly _config: TracerConfig;
|
||||
private readonly _registeredSpanProcessors: SpanProcessor[] = [];
|
||||
private readonly _tracers: Map<string, Tracer> = new Map();
|
||||
|
|
@ -86,9 +100,7 @@ export class BasicTracerProvider implements TracerProvider {
|
|||
register(config: SDKRegistrationConfig = {}) {
|
||||
trace.setGlobalTracerProvider(this);
|
||||
if (config.propagator === undefined) {
|
||||
config.propagator = new CompositePropagator({
|
||||
propagators: [new HttpBaggage(), new HttpTraceContext()],
|
||||
});
|
||||
config.propagator = this._buildPropagatorFromEnv();
|
||||
}
|
||||
|
||||
if (config.contextManager) {
|
||||
|
|
@ -103,4 +115,43 @@ export class BasicTracerProvider implements TracerProvider {
|
|||
shutdown() {
|
||||
return this.activeSpanProcessor.shutdown();
|
||||
}
|
||||
|
||||
protected _getPropagator(name: string): TextMapPropagator | undefined {
|
||||
return BasicTracerProvider._registeredPropagators.get(name)?.();
|
||||
}
|
||||
|
||||
protected _buildPropagatorFromEnv(): TextMapPropagator | undefined {
|
||||
// per spec, propagators from env must be deduplicated
|
||||
const uniquePropagatorNames = [...new Set(getEnv().OTEL_PROPAGATORS)];
|
||||
|
||||
const propagators = uniquePropagatorNames.map(name => {
|
||||
const propagator = this._getPropagator(name);
|
||||
if (!propagator) {
|
||||
diag.warn(
|
||||
`Propagator "${name}" requested through environment variable is unavailable.`
|
||||
);
|
||||
}
|
||||
|
||||
return propagator;
|
||||
});
|
||||
const validPropagators = propagators.reduce<TextMapPropagator[]>(
|
||||
(list, item) => {
|
||||
if (item) {
|
||||
list.push(item);
|
||||
}
|
||||
return list;
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
if (validPropagators.length === 0) {
|
||||
return;
|
||||
} else if (uniquePropagatorNames.length === 1) {
|
||||
return validPropagators[0];
|
||||
} else {
|
||||
return new CompositePropagator({
|
||||
propagators: validPropagators,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,14 @@ import {
|
|||
setSpan,
|
||||
setSpanContext,
|
||||
getSpan,
|
||||
TextMapPropagator,
|
||||
TextMapSetter,
|
||||
Context,
|
||||
TextMapGetter,
|
||||
propagation,
|
||||
diag,
|
||||
} from '@opentelemetry/api';
|
||||
import { CompositePropagator } from '@opentelemetry/core';
|
||||
import {
|
||||
AlwaysOnSampler,
|
||||
AlwaysOffSampler,
|
||||
|
|
@ -115,6 +122,99 @@ describe('BasicTracerProvider', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('.register()', () => {
|
||||
const envSource = (typeof window !== 'undefined'
|
||||
? window
|
||||
: process.env) as any;
|
||||
|
||||
describe('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.');
|
||||
}
|
||||
}
|
||||
|
||||
let setGlobalPropagatorStub: sinon.SinonSpy<
|
||||
[TextMapPropagator],
|
||||
TextMapPropagator
|
||||
>;
|
||||
let originalPropagators: string | number | undefined | string[];
|
||||
beforeEach(() => {
|
||||
setGlobalPropagatorStub = sinon.spy(propagation, 'setGlobalPropagator');
|
||||
originalPropagators = envSource.OTEL_PROPAGATORS;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
setGlobalPropagatorStub.restore();
|
||||
|
||||
// otherwise we may assign 'undefined' (a string)
|
||||
if (originalPropagators !== undefined) {
|
||||
envSource.OTEL_PROPAGATORS = originalPropagators;
|
||||
} else {
|
||||
delete envSource.OTEL_PROPAGATORS;
|
||||
}
|
||||
});
|
||||
|
||||
it('should be set to a given value if it it provided', () => {
|
||||
const provider = new BasicTracerProvider();
|
||||
provider.register({
|
||||
propagator: new DummyPropagator(),
|
||||
});
|
||||
assert.ok(
|
||||
setGlobalPropagatorStub.calledOnceWithExactly(
|
||||
sinon.match.instanceOf(DummyPropagator)
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
it('should be composite if 2 or more propagators provided in an environment variable', () => {
|
||||
const provider = new BasicTracerProvider();
|
||||
provider.register();
|
||||
|
||||
assert.ok(
|
||||
setGlobalPropagatorStub.calledOnceWithExactly(
|
||||
sinon.match.instanceOf(CompositePropagator)
|
||||
)
|
||||
);
|
||||
assert.deepStrictEqual(setGlobalPropagatorStub.args[0][0].fields(), [
|
||||
'traceparent',
|
||||
'tracestate',
|
||||
'baggage',
|
||||
]);
|
||||
});
|
||||
|
||||
it('warns if there is no propagator registered with a given name', () => {
|
||||
const warnStub = sinon.spy(diag, 'warn');
|
||||
|
||||
envSource.OTEL_PROPAGATORS = 'missing-propagator';
|
||||
const provider = new BasicTracerProvider({});
|
||||
provider.register();
|
||||
|
||||
assert.ok(
|
||||
warnStub.calledOnceWithExactly(
|
||||
'Propagator "missing-propagator" requested through environment variable is unavailable.'
|
||||
)
|
||||
);
|
||||
|
||||
warnStub.restore();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('.startSpan()', () => {
|
||||
it('should start a span with name only', () => {
|
||||
const tracer = new BasicTracerProvider().getTracer('default');
|
||||
|
|
|
|||
Loading…
Reference in New Issue