Skip to content

Commit 1758fa6

Browse files
vmarchaudobecnydyladan
authored
feat(tracing): allow to configure exporter by environment #1676 (#2100)
Co-authored-by: Bartlomiej Obecny <[email protected]> Co-authored-by: Daniel Dyla <[email protected]>
1 parent 02239b5 commit 1758fa6

File tree

5 files changed

+106
-10
lines changed

5 files changed

+106
-10
lines changed

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

+2
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ export type ENVIRONMENT = {
7676
OTEL_EXPORTER_ZIPKIN_ENDPOINT?: string;
7777
OTEL_LOG_LEVEL?: DiagLogLevel;
7878
OTEL_RESOURCE_ATTRIBUTES?: string;
79+
OTEL_TRACES_EXPORTER?: string;
7980
OTEL_TRACES_SAMPLER_ARG?: string;
8081
OTEL_TRACES_SAMPLER?: string;
8182
} & ENVIRONMENT_NUMBERS &
@@ -117,6 +118,7 @@ export const DEFAULT_ENVIRONMENT: Required<ENVIRONMENT> = {
117118
OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT: 128,
118119
OTEL_SPAN_EVENT_COUNT_LIMIT: 128,
119120
OTEL_SPAN_LINK_COUNT_LIMIT: 128,
121+
OTEL_TRACES_EXPORTER: 'none',
120122
OTEL_TRACES_SAMPLER: TracesSamplerValues.ParentBasedAlwaysOn,
121123
OTEL_TRACES_SAMPLER_ARG: '',
122124
};

packages/opentelemetry-tracing/src/BasicTracerProvider.ts

+46-2
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,14 @@ import { Resource } from '@opentelemetry/resources';
3232
import { SpanProcessor, Tracer } from '.';
3333
import { DEFAULT_CONFIG } from './config';
3434
import { MultiSpanProcessor } from './MultiSpanProcessor';
35-
import { NoopSpanProcessor } from './NoopSpanProcessor';
35+
import { NoopSpanProcessor } from './export/NoopSpanProcessor';
3636
import { SDKRegistrationConfig, TracerConfig } from './types';
3737
const merge = require('lodash.merge');
38+
import { SpanExporter } from './export/SpanExporter';
39+
import { BatchSpanProcessor } from './export/BatchSpanProcessor';
3840

3941
export type PROPAGATOR_FACTORY = () => TextMapPropagator;
42+
export type EXPORTER_FACTORY = () => SpanExporter;
4043

4144
/**
4245
* This class represents a basic tracer provider which platform libraries can extend
@@ -50,11 +53,16 @@ export class BasicTracerProvider implements TracerProvider {
5053
['baggage', () => new HttpBaggagePropagator()],
5154
]);
5255

56+
protected static readonly _registeredExporters = new Map<
57+
string,
58+
EXPORTER_FACTORY
59+
>();
60+
5361
private readonly _config: TracerConfig;
5462
private readonly _registeredSpanProcessors: SpanProcessor[] = [];
5563
private readonly _tracers: Map<string, Tracer> = new Map();
5664

57-
activeSpanProcessor: SpanProcessor = new NoopSpanProcessor();
65+
activeSpanProcessor: SpanProcessor;
5866
readonly resource: Resource;
5967

6068
constructor(config: TracerConfig = {}) {
@@ -64,6 +72,14 @@ export class BasicTracerProvider implements TracerProvider {
6472
this._config = Object.assign({}, mergedConfig, {
6573
resource: this.resource,
6674
});
75+
76+
const defaultExporter = this._buildExporterFromEnv();
77+
if (defaultExporter !== undefined) {
78+
const batchProcessor = new BatchSpanProcessor(defaultExporter);
79+
this.activeSpanProcessor = batchProcessor;
80+
} else {
81+
this.activeSpanProcessor = new NoopSpanProcessor();
82+
}
6783
}
6884

6985
getTracer(name: string, version?: string): Tracer {
@@ -80,6 +96,18 @@ export class BasicTracerProvider implements TracerProvider {
8096
* @param spanProcessor the new SpanProcessor to be added.
8197
*/
8298
addSpanProcessor(spanProcessor: SpanProcessor): void {
99+
if (this._registeredSpanProcessors.length === 0) {
100+
// since we might have enabled by default a batchProcessor, we disable it
101+
// before adding the new one
102+
this.activeSpanProcessor
103+
.shutdown()
104+
.catch(err =>
105+
diag.error(
106+
'Error while trying to shutdown current span processor',
107+
err
108+
)
109+
);
110+
}
83111
this._registeredSpanProcessors.push(spanProcessor);
84112
this.activeSpanProcessor = new MultiSpanProcessor(
85113
this._registeredSpanProcessors
@@ -120,6 +148,10 @@ export class BasicTracerProvider implements TracerProvider {
120148
return BasicTracerProvider._registeredPropagators.get(name)?.();
121149
}
122150

151+
protected _getSpanExporter(name: string): SpanExporter | undefined {
152+
return BasicTracerProvider._registeredExporters.get(name)?.();
153+
}
154+
123155
protected _buildPropagatorFromEnv(): TextMapPropagator | undefined {
124156
// per spec, propagators from env must be deduplicated
125157
const uniquePropagatorNames = Array.from(
@@ -156,4 +188,16 @@ export class BasicTracerProvider implements TracerProvider {
156188
});
157189
}
158190
}
191+
192+
protected _buildExporterFromEnv(): SpanExporter | undefined {
193+
const exporterName = getEnv().OTEL_TRACES_EXPORTER;
194+
if (exporterName === 'none') return;
195+
const exporter = this._getSpanExporter(exporterName);
196+
if (!exporter) {
197+
diag.error(
198+
`Exporter "${exporterName}" requested through environment variable is unavailable.`
199+
);
200+
}
201+
return exporter;
202+
}
159203
}

packages/opentelemetry-tracing/src/NoopSpanProcessor.ts renamed to packages/opentelemetry-tracing/src/export/NoopSpanProcessor.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@
1515
*/
1616

1717
import { Context } from '@opentelemetry/api';
18-
import { ReadableSpan } from './export/ReadableSpan';
19-
import { Span } from './Span';
20-
import { SpanProcessor } from './SpanProcessor';
18+
import { ReadableSpan } from './ReadableSpan';
19+
import { Span } from '../Span';
20+
import { SpanProcessor } from '../SpanProcessor';
2121

2222
/** No-op implementation of SpanProcessor */
2323
export class NoopSpanProcessor implements SpanProcessor {

packages/opentelemetry-tracing/src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export * from './export/InMemorySpanExporter';
2222
export * from './export/ReadableSpan';
2323
export * from './export/SimpleSpanProcessor';
2424
export * from './export/SpanExporter';
25+
export * from './export/NoopSpanProcessor';
2526
export * from './Span';
2627
export * from './SpanProcessor';
2728
export * from './TimedEvent';

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

+54-5
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,20 @@ import {
3838
import { Resource } from '@opentelemetry/resources';
3939
import * as assert from 'assert';
4040
import * as sinon from 'sinon';
41-
import { BasicTracerProvider, Span } from '../src';
41+
import {
42+
BasicTracerProvider,
43+
NoopSpanProcessor,
44+
Span,
45+
InMemorySpanExporter,
46+
SpanExporter,
47+
BatchSpanProcessor,
48+
} from '../src';
4249

4350
describe('BasicTracerProvider', () => {
4451
let removeEvent: Function | undefined;
52+
const envSource = (typeof window !== 'undefined'
53+
? window
54+
: process.env) as any;
4555

4656
beforeEach(() => {
4757
context.disable();
@@ -120,13 +130,14 @@ describe('BasicTracerProvider', () => {
120130
const tracer = new BasicTracerProvider();
121131
assert.ok(tracer instanceof BasicTracerProvider);
122132
});
133+
134+
it('should use noop span processor by default', () => {
135+
const tracer = new BasicTracerProvider();
136+
assert.ok(tracer.activeSpanProcessor instanceof NoopSpanProcessor);
137+
});
123138
});
124139

125140
describe('.register()', () => {
126-
const envSource = (typeof window !== 'undefined'
127-
? window
128-
: process.env) as any;
129-
130141
describe('propagator', () => {
131142
class DummyPropagator implements TextMapPropagator {
132143
inject(
@@ -213,6 +224,44 @@ describe('BasicTracerProvider', () => {
213224
warnStub.restore();
214225
});
215226
});
227+
228+
describe('exporter', () => {
229+
class CustomTracerProvider extends BasicTracerProvider {
230+
protected _getSpanExporter(name: string): SpanExporter | undefined {
231+
return name === 'memory'
232+
? new InMemorySpanExporter()
233+
: BasicTracerProvider._registeredExporters.get(name)?.();
234+
}
235+
}
236+
237+
afterEach(() => {
238+
delete envSource.OTEL_TRACES_EXPORTER;
239+
});
240+
241+
it('logs error if there is no exporter registered with a given name', () => {
242+
const errorStub = sinon.spy(diag, 'error');
243+
244+
envSource.OTEL_TRACES_EXPORTER = 'missing-exporter';
245+
const provider = new BasicTracerProvider({});
246+
provider.register();
247+
assert.ok(
248+
errorStub.getCall(0).args[0] ===
249+
'Exporter "missing-exporter" requested through environment variable is unavailable.'
250+
);
251+
errorStub.restore();
252+
});
253+
254+
it('registers trace exporter from environment variable', () => {
255+
envSource.OTEL_TRACES_EXPORTER = 'memory';
256+
const provider = new CustomTracerProvider({});
257+
provider.register();
258+
const processor = provider.getActiveSpanProcessor();
259+
assert(processor instanceof BatchSpanProcessor);
260+
// @ts-expect-error access configured to verify its the correct one
261+
const exporter = processor._exporter;
262+
assert(exporter instanceof InMemorySpanExporter);
263+
});
264+
});
216265
});
217266

218267
describe('.startSpan()', () => {

0 commit comments

Comments
 (0)