feat(sdk-trace-base)!: do not read environment variables from window (#5455)

Co-authored-by: Trent Mick <trentm@gmail.com>
This commit is contained in:
Marc Pichler 2025-02-17 12:19:51 +01:00 committed by GitHub
parent 1a5b57b7f3
commit e4381fcb18
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 335 additions and 277 deletions

View File

@ -93,8 +93,12 @@ For semantic convention package changes, see the [semconv CHANGELOG](packages/se
* (user-facing): `baggageUtils.parsePairKeyValue` was an internal utility function that was unintentionally exported. It has been removed without replacement. * (user-facing): `baggageUtils.parsePairKeyValue` was an internal utility function that was unintentionally exported. It has been removed without replacement.
* (user-facing): `TimeOriginLegacy` has been removed without replacement. * (user-facing): `TimeOriginLegacy` has been removed without replacement.
* (user-facing): `isAttributeKey` was an internal utility function that was unintentionally exported. It has been removed without replacement. * (user-facing): `isAttributeKey` was an internal utility function that was unintentionally exported. It has been removed without replacement.
* feat(sdk-trace-base)!: do not read environment variables from window in browsers [#5445](https://github.com/open-telemetry/opentelemetry-js/pull/5455) @pichlermarc
* (user-facing): all configuration previously possible via `window.OTEL_*` is now not supported anymore, please pass configuration options to constructors instead.
* Note: Node.js environment variable configuration continues to work as-is.
* feat(exporter-zipkin)!: do not read environment variables from window in browsers [#5465](https://github.com/open-telemetry/opentelemetry-js/pull/5465) @pichlermarc * feat(exporter-zipkin)!: do not read environment variables from window in browsers [#5465](https://github.com/open-telemetry/opentelemetry-js/pull/5465) @pichlermarc
* (user-facing): all configuration previously possible via `window.OTEL_*` is now not supported anymore, please pass configuration options to constructors instead. * (user-facing): all configuration previously possible via `window.OTEL_*` is now not supported anymore, please pass configuration options to constructors instead.
* Note: Node.js environment variable configuration continues to work as-is.
* feat(resource)!: Remove resource class export in favor of functions and types only to aid in cross-version compatibility [#5421](https://github.com/open-telemetry/opentelemetry-js/pull/5421) * feat(resource)!: Remove resource class export in favor of functions and types only to aid in cross-version compatibility [#5421](https://github.com/open-telemetry/opentelemetry-js/pull/5421)
* Renames `Resource` class to `ResourceImpl` and makes it package-private * Renames `Resource` class to `ResourceImpl` and makes it package-private
* Renames `IResource` interface to `Resource` * Renames `IResource` interface to `Resource`

View File

@ -1,11 +1,11 @@
/*! /*
* Copyright The OpenTelemetry Authors * Copyright The OpenTelemetry Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * https://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
@ -17,8 +17,12 @@
const karmaWebpackConfig = require('../../karma.webpack'); const karmaWebpackConfig = require('../../karma.webpack');
const karmaBaseConfig = require('../../karma.base'); const karmaBaseConfig = require('../../karma.base');
module.exports = (config) => { module.exports = config => {
config.set(Object.assign({}, karmaBaseConfig, { config.set(
webpack: karmaWebpackConfig, Object.assign({}, karmaBaseConfig, {
})) webpack: karmaWebpackConfig,
files: ['test/browser/index-webpack.ts'],
preprocessors: { 'test/browser/index-webpack.ts': ['webpack'] },
})
);
}; };

View File

@ -1,11 +1,11 @@
/*! /*
* Copyright The OpenTelemetry Authors * Copyright The OpenTelemetry Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * https://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
@ -17,8 +17,17 @@
const karmaWebpackConfig = require('../../karma.webpack'); const karmaWebpackConfig = require('../../karma.webpack');
const karmaBaseConfig = require('../../karma.worker'); const karmaBaseConfig = require('../../karma.worker');
module.exports = (config) => { module.exports = config => {
config.set(Object.assign({}, karmaBaseConfig, { config.set(
webpack: karmaWebpackConfig, Object.assign({}, karmaBaseConfig, {
})) webpack: karmaWebpackConfig,
files: [
{
pattern: 'test/browser/index-webpack.worker.ts',
included: false,
},
],
preprocessors: { 'test/browser/index-webpack.worker.ts': ['webpack'] },
})
);
}; };

View File

@ -15,7 +15,7 @@
*/ */
import { diag } from '@opentelemetry/api'; import { diag } from '@opentelemetry/api';
import { getEnv, ENVIRONMENT } from '@opentelemetry/core'; import { getNumberFromEnv, getStringFromEnv } from '@opentelemetry/core';
import { Sampler } from './Sampler'; import { Sampler } from './Sampler';
import { AlwaysOffSampler } from './sampler/AlwaysOffSampler'; import { AlwaysOffSampler } from './sampler/AlwaysOffSampler';
import { AlwaysOnSampler } from './sampler/AlwaysOnSampler'; import { AlwaysOnSampler } from './sampler/AlwaysOnSampler';
@ -44,35 +44,38 @@ const DEFAULT_RATIO = 1;
// object needs to be wrapped in this function and called when needed otherwise // object needs to be wrapped in this function and called when needed otherwise
// envs are parsed before tests are ran - causes tests using these envs to fail // envs are parsed before tests are ran - causes tests using these envs to fail
export function loadDefaultConfig() { export function loadDefaultConfig() {
const env = getEnv();
return { return {
sampler: buildSamplerFromEnv(env), sampler: buildSamplerFromEnv(),
forceFlushTimeoutMillis: 30000, forceFlushTimeoutMillis: 30000,
generalLimits: { generalLimits: {
attributeValueLengthLimit: env.OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT, attributeValueLengthLimit:
attributeCountLimit: env.OTEL_ATTRIBUTE_COUNT_LIMIT, getNumberFromEnv('OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT') ?? Infinity,
attributeCountLimit:
getNumberFromEnv('OTEL_ATTRIBUTE_COUNT_LIMIT') ?? 128,
}, },
spanLimits: { spanLimits: {
attributeValueLengthLimit: env.OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT, attributeValueLengthLimit:
attributeCountLimit: env.OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT, getNumberFromEnv('OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT') ?? Infinity,
linkCountLimit: env.OTEL_SPAN_LINK_COUNT_LIMIT, attributeCountLimit:
eventCountLimit: env.OTEL_SPAN_EVENT_COUNT_LIMIT, getNumberFromEnv('OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT') ?? 128,
linkCountLimit: getNumberFromEnv('OTEL_SPAN_LINK_COUNT_LIMIT') ?? 128,
eventCountLimit: getNumberFromEnv('OTEL_SPAN_EVENT_COUNT_LIMIT') ?? 128,
attributePerEventCountLimit: attributePerEventCountLimit:
env.OTEL_SPAN_ATTRIBUTE_PER_EVENT_COUNT_LIMIT, getNumberFromEnv('OTEL_SPAN_ATTRIBUTE_PER_EVENT_COUNT_LIMIT') ?? 128,
attributePerLinkCountLimit: env.OTEL_SPAN_ATTRIBUTE_PER_LINK_COUNT_LIMIT, attributePerLinkCountLimit:
getNumberFromEnv('OTEL_SPAN_ATTRIBUTE_PER_LINK_COUNT_LIMIT') ?? 128,
}, },
}; };
} }
/** /**
* Based on environment, builds a sampler, complies with specification. * Based on environment, builds a sampler, complies with specification.
* @param environment optional, by default uses getEnv(), but allows passing a value to reuse parsed environment
*/ */
export function buildSamplerFromEnv( export function buildSamplerFromEnv(): Sampler {
environment: Required<ENVIRONMENT> = getEnv() const sampler =
): Sampler { getStringFromEnv('OTEL_TRACES_SAMPLER') ??
switch (environment.OTEL_TRACES_SAMPLER) { TracesSamplerValues.ParentBasedAlwaysOn;
switch (sampler) {
case TracesSamplerValues.AlwaysOn: case TracesSamplerValues.AlwaysOn:
return new AlwaysOnSampler(); return new AlwaysOnSampler();
case TracesSamplerValues.AlwaysOff: case TracesSamplerValues.AlwaysOff:
@ -86,48 +89,31 @@ export function buildSamplerFromEnv(
root: new AlwaysOffSampler(), root: new AlwaysOffSampler(),
}); });
case TracesSamplerValues.TraceIdRatio: case TracesSamplerValues.TraceIdRatio:
return new TraceIdRatioBasedSampler( return new TraceIdRatioBasedSampler(getSamplerProbabilityFromEnv());
getSamplerProbabilityFromEnv(environment)
);
case TracesSamplerValues.ParentBasedTraceIdRatio: case TracesSamplerValues.ParentBasedTraceIdRatio:
return new ParentBasedSampler({ return new ParentBasedSampler({
root: new TraceIdRatioBasedSampler( root: new TraceIdRatioBasedSampler(getSamplerProbabilityFromEnv()),
getSamplerProbabilityFromEnv(environment)
),
}); });
default: default:
diag.error( diag.error(
`OTEL_TRACES_SAMPLER value "${environment.OTEL_TRACES_SAMPLER} invalid, defaulting to ${FALLBACK_OTEL_TRACES_SAMPLER}".` `OTEL_TRACES_SAMPLER value "${sampler}" invalid, defaulting to "${FALLBACK_OTEL_TRACES_SAMPLER}".`
); );
return new AlwaysOnSampler(); return new AlwaysOnSampler();
} }
} }
function getSamplerProbabilityFromEnv( function getSamplerProbabilityFromEnv(): number | undefined {
environment: Required<ENVIRONMENT> const probability = getNumberFromEnv('OTEL_TRACES_SAMPLER_ARG');
): number | undefined { if (probability == null) {
if (
environment.OTEL_TRACES_SAMPLER_ARG === undefined ||
environment.OTEL_TRACES_SAMPLER_ARG === ''
) {
diag.error( diag.error(
`OTEL_TRACES_SAMPLER_ARG is blank, defaulting to ${DEFAULT_RATIO}.` `OTEL_TRACES_SAMPLER_ARG is blank, defaulting to ${DEFAULT_RATIO}.`
); );
return DEFAULT_RATIO; return DEFAULT_RATIO;
} }
const probability = Number(environment.OTEL_TRACES_SAMPLER_ARG);
if (isNaN(probability)) {
diag.error(
`OTEL_TRACES_SAMPLER_ARG=${environment.OTEL_TRACES_SAMPLER_ARG} was given, but it is invalid, defaulting to ${DEFAULT_RATIO}.`
);
return DEFAULT_RATIO;
}
if (probability < 0 || probability > 1) { if (probability < 0 || probability > 1) {
diag.error( diag.error(
`OTEL_TRACES_SAMPLER_ARG=${environment.OTEL_TRACES_SAMPLER_ARG} was given, but it is out of range ([0..1]), defaulting to ${DEFAULT_RATIO}.` `OTEL_TRACES_SAMPLER_ARG=${probability} was given, but it is out of range ([0..1]), defaulting to ${DEFAULT_RATIO}.`
); );
return DEFAULT_RATIO; return DEFAULT_RATIO;
} }

View File

@ -18,7 +18,7 @@ import { context, Context, diag, TraceFlags } from '@opentelemetry/api';
import { import {
BindOnceFuture, BindOnceFuture,
ExportResultCode, ExportResultCode,
getEnv, getNumberFromEnv,
globalErrorHandler, globalErrorHandler,
suppressTracing, suppressTracing,
unrefTimer, unrefTimer,
@ -51,23 +51,22 @@ export abstract class BatchSpanProcessorBase<T extends BufferConfig>
private readonly _exporter: SpanExporter, private readonly _exporter: SpanExporter,
config?: T config?: T
) { ) {
const env = getEnv();
this._maxExportBatchSize = this._maxExportBatchSize =
typeof config?.maxExportBatchSize === 'number' typeof config?.maxExportBatchSize === 'number'
? config.maxExportBatchSize ? config.maxExportBatchSize
: env.OTEL_BSP_MAX_EXPORT_BATCH_SIZE; : (getNumberFromEnv('OTEL_BSP_MAX_EXPORT_BATCH_SIZE') ?? 512);
this._maxQueueSize = this._maxQueueSize =
typeof config?.maxQueueSize === 'number' typeof config?.maxQueueSize === 'number'
? config.maxQueueSize ? config.maxQueueSize
: env.OTEL_BSP_MAX_QUEUE_SIZE; : (getNumberFromEnv('OTEL_BSP_MAX_QUEUE_SIZE') ?? 2048);
this._scheduledDelayMillis = this._scheduledDelayMillis =
typeof config?.scheduledDelayMillis === 'number' typeof config?.scheduledDelayMillis === 'number'
? config.scheduledDelayMillis ? config.scheduledDelayMillis
: env.OTEL_BSP_SCHEDULE_DELAY; : (getNumberFromEnv('OTEL_BSP_SCHEDULE_DELAY') ?? 5000);
this._exportTimeoutMillis = this._exportTimeoutMillis =
typeof config?.exportTimeoutMillis === 'number' typeof config?.exportTimeoutMillis === 'number'
? config.exportTimeoutMillis ? config.exportTimeoutMillis
: env.OTEL_BSP_EXPORT_TIMEOUT; : (getNumberFromEnv('OTEL_BSP_EXPORT_TIMEOUT') ?? 30000);
this._shutdownOnce = new BindOnceFuture(this._shutdown, this); this._shutdownOnce = new BindOnceFuture(this._shutdown, this);

View File

@ -20,7 +20,7 @@ import { SpanLimits, TracerConfig, GeneralLimits } from './types';
import { import {
DEFAULT_ATTRIBUTE_COUNT_LIMIT, DEFAULT_ATTRIBUTE_COUNT_LIMIT,
DEFAULT_ATTRIBUTE_VALUE_LENGTH_LIMIT, DEFAULT_ATTRIBUTE_VALUE_LENGTH_LIMIT,
getEnvWithoutDefaults, getNumberFromEnv,
} from '@opentelemetry/core'; } from '@opentelemetry/core';
/** /**
@ -68,16 +68,14 @@ export function mergeConfig(userConfig: TracerConfig): TracerConfig & {
export function reconfigureLimits(userConfig: TracerConfig): TracerConfig { export function reconfigureLimits(userConfig: TracerConfig): TracerConfig {
const spanLimits = Object.assign({}, userConfig.spanLimits); const spanLimits = Object.assign({}, userConfig.spanLimits);
const parsedEnvConfig = getEnvWithoutDefaults();
/** /**
* Reassign span attribute count limit to use first non null value defined by user or use default value * Reassign span attribute count limit to use first non null value defined by user or use default value
*/ */
spanLimits.attributeCountLimit = spanLimits.attributeCountLimit =
userConfig.spanLimits?.attributeCountLimit ?? userConfig.spanLimits?.attributeCountLimit ??
userConfig.generalLimits?.attributeCountLimit ?? userConfig.generalLimits?.attributeCountLimit ??
parsedEnvConfig.OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT ?? getNumberFromEnv('OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT') ??
parsedEnvConfig.OTEL_ATTRIBUTE_COUNT_LIMIT ?? getNumberFromEnv('OTEL_ATTRIBUTE_COUNT_LIMIT') ??
DEFAULT_ATTRIBUTE_COUNT_LIMIT; DEFAULT_ATTRIBUTE_COUNT_LIMIT;
/** /**
@ -86,8 +84,8 @@ export function reconfigureLimits(userConfig: TracerConfig): TracerConfig {
spanLimits.attributeValueLengthLimit = spanLimits.attributeValueLengthLimit =
userConfig.spanLimits?.attributeValueLengthLimit ?? userConfig.spanLimits?.attributeValueLengthLimit ??
userConfig.generalLimits?.attributeValueLengthLimit ?? userConfig.generalLimits?.attributeValueLengthLimit ??
parsedEnvConfig.OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT ?? getNumberFromEnv('OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT') ??
parsedEnvConfig.OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT ?? getNumberFromEnv('OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT') ??
DEFAULT_ATTRIBUTE_VALUE_LENGTH_LIMIT; DEFAULT_ATTRIBUTE_VALUE_LENGTH_LIMIT;
return Object.assign({}, userConfig, { spanLimits }); return Object.assign({}, userConfig, { spanLimits });

View File

@ -13,8 +13,11 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
const testsContext = require.context('../browser', true, /test$/);
testsContext.keys().forEach(testsContext);
{ const testsContextCommon = require.context('../common', true, /test$/);
const testsContext = require.context('./', true, /test$/); testsContextCommon.keys().forEach(testsContextCommon);
testsContext.keys().forEach(testsContext);
} const srcContext = require.context('.', true, /src$/);
srcContext.keys().forEach(srcContext);

View File

@ -13,8 +13,11 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
const testsContext = require.context('../browser', true, /test$/);
testsContext.keys().forEach(testsContext);
{ const testsContextCommon = require.context('../common', true, /test$/);
const testsContext = require.context('./', true, /test$/); testsContextCommon.keys().forEach(testsContextCommon);
testsContext.keys().forEach(testsContext);
} const srcContext = require.context('.', true, /src$/);
srcContext.keys().forEach(srcContext);

View File

@ -62,16 +62,9 @@ class DummyPropagator implements TextMapPropagator {
} }
describe('BasicTracerProvider', () => { describe('BasicTracerProvider', () => {
let envSource: Record<string, any>;
let setGlobalPropagatorStub: sinon.SinonSpy<[TextMapPropagator], boolean>; let setGlobalPropagatorStub: sinon.SinonSpy<[TextMapPropagator], boolean>;
let setGlobalContextManagerStub: sinon.SinonSpy<[ContextManager], boolean>; let setGlobalContextManagerStub: sinon.SinonSpy<[ContextManager], boolean>;
if (global.process?.versions?.node === undefined) {
envSource = globalThis as unknown as Record<string, any>;
} else {
envSource = process.env as Record<string, any>;
}
beforeEach(() => { beforeEach(() => {
// to avoid actually registering the TraceProvider and leaking env to other tests // to avoid actually registering the TraceProvider and leaking env to other tests
sinon.stub(trace, 'setGlobalTracerProvider'); sinon.stub(trace, 'setGlobalTracerProvider');
@ -237,56 +230,7 @@ describe('BasicTracerProvider', () => {
}); });
}); });
describe('when attribute value length limit is defined via env', () => { describe('when attribute value length limit is not defined', () => {
it('should have general attribute value length limits value as defined with env', () => {
envSource.OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT = '115';
const tracer = new BasicTracerProvider().getTracer(
'default'
) as Tracer;
const generalLimits = tracer.getGeneralLimits();
assert.strictEqual(generalLimits.attributeValueLengthLimit, 115);
delete envSource.OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT;
});
it('should have span attribute value length limit value same as general limit value', () => {
envSource.OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT = '125';
const tracer = new BasicTracerProvider().getTracer(
'default'
) as Tracer;
const generalLimits = tracer.getGeneralLimits();
const spanLimits = tracer.getSpanLimits();
assert.strictEqual(generalLimits.attributeValueLengthLimit, 125);
assert.strictEqual(spanLimits.attributeValueLengthLimit, 125);
delete envSource.OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT;
});
it('should have span and general attribute value length limits as defined in env', () => {
envSource.OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT = '125';
envSource.OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT = '109';
const tracer = new BasicTracerProvider().getTracer(
'default'
) as Tracer;
const spanLimits = tracer.getSpanLimits();
const generalLimits = tracer.getGeneralLimits();
assert.strictEqual(generalLimits.attributeValueLengthLimit, 125);
assert.strictEqual(spanLimits.attributeValueLengthLimit, 109);
delete envSource.OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT;
delete envSource.OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT;
});
it('should have span attribute value length limit as default of Infinity', () => {
envSource.OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT = '125';
envSource.OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT = 'Infinity';
const tracer = new BasicTracerProvider().getTracer(
'default'
) as Tracer;
const spanLimits = tracer.getSpanLimits();
const generalLimits = tracer.getGeneralLimits();
assert.strictEqual(generalLimits.attributeValueLengthLimit, 125);
assert.strictEqual(spanLimits.attributeValueLengthLimit, Infinity);
delete envSource.OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT;
delete envSource.OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT;
});
});
describe('when attribute value length limit is not defined via env', () => {
it('should use default value of Infinity', () => { it('should use default value of Infinity', () => {
const tracer = new BasicTracerProvider().getTracer( const tracer = new BasicTracerProvider().getTracer(
'default' 'default'
@ -298,56 +242,7 @@ describe('BasicTracerProvider', () => {
}); });
}); });
describe('when attribute count limit is defined via env', () => { describe('when attribute count limit is not defined', () => {
it('should general attribute count limit as defined with env', () => {
envSource.OTEL_ATTRIBUTE_COUNT_LIMIT = '25';
const tracer = new BasicTracerProvider({}).getTracer(
'default'
) as Tracer;
const generalLimits = tracer.getGeneralLimits();
assert.strictEqual(generalLimits.attributeCountLimit, 25);
delete envSource.OTEL_ATTRIBUTE_COUNT_LIMIT;
});
it('should have span attribute count limit value same as general limit value', () => {
envSource.OTEL_ATTRIBUTE_COUNT_LIMIT = '20';
const tracer = new BasicTracerProvider().getTracer(
'default'
) as Tracer;
const generalLimits = tracer.getGeneralLimits();
const spanLimits = tracer.getSpanLimits();
assert.strictEqual(generalLimits.attributeCountLimit, 20);
assert.strictEqual(spanLimits.attributeCountLimit, 20);
delete envSource.OTEL_ATTRIBUTE_COUNT_LIMIT;
});
it('should have span and general attribute count limits as defined in env', () => {
envSource.OTEL_ATTRIBUTE_COUNT_LIMIT = '20';
envSource.OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT = '35';
const tracer = new BasicTracerProvider().getTracer(
'default'
) as Tracer;
const spanLimits = tracer.getSpanLimits();
const generalLimits = tracer.getGeneralLimits();
assert.strictEqual(generalLimits.attributeCountLimit, 20);
assert.strictEqual(spanLimits.attributeCountLimit, 35);
delete envSource.OTEL_ATTRIBUTE_COUNT_LIMIT;
delete envSource.OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT;
});
it('should have span attribute count limit as default of 128', () => {
envSource.OTEL_ATTRIBUTE_COUNT_LIMIT = '20';
envSource.OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT = '128';
const tracer = new BasicTracerProvider().getTracer(
'default'
) as Tracer;
const spanLimits = tracer.getSpanLimits();
const generalLimits = tracer.getGeneralLimits();
assert.strictEqual(generalLimits.attributeCountLimit, 20);
assert.strictEqual(spanLimits.attributeCountLimit, 128);
delete envSource.OTEL_ATTRIBUTE_COUNT_LIMIT;
delete envSource.OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT;
});
});
describe('when attribute count limit is not defined via env', () => {
it('should use default value of 128', () => { it('should use default value of 128', () => {
const tracer = new BasicTracerProvider().getTracer( const tracer = new BasicTracerProvider().getTracer(
'default' 'default'

View File

@ -50,12 +50,6 @@ import { Tracer } from '../../src/Tracer';
describe('Tracer', () => { describe('Tracer', () => {
const tracerProvider = new BasicTracerProvider(); const tracerProvider = new BasicTracerProvider();
let envSource: Record<string, any>;
if (global.process?.versions?.node === undefined) {
envSource = globalThis as unknown as Record<string, any>;
} else {
envSource = process.env as Record<string, any>;
}
class TestSampler implements Sampler { class TestSampler implements Sampler {
constructor(private readonly traceState?: TraceState) {} constructor(private readonly traceState?: TraceState) {}
@ -106,8 +100,6 @@ describe('Tracer', () => {
afterEach(() => { afterEach(() => {
context.disable(); context.disable();
delete envSource.OTEL_TRACES_SAMPLER;
delete envSource.OTEL_TRACES_SAMPLER_ARG;
}); });
it('should create a Tracer instance', () => { it('should create a Tracer instance', () => {
@ -327,51 +319,6 @@ describe('Tracer', () => {
assert.strictEqual(trace.getSpan(samplerContext), undefined); assert.strictEqual(trace.getSpan(samplerContext), undefined);
}); });
it('should sample a trace when OTEL_TRACES_SAMPLER_ARG is unset', () => {
envSource.OTEL_TRACES_SAMPLER = 'traceidratio';
envSource.OTEL_TRACES_SAMPLER_ARG = '';
const tracer = new Tracer(
{ name: 'default', version: '0.0.1' },
{},
tracerProvider['_resource'],
tracerProvider['_activeSpanProcessor']
);
const span = tracer.startSpan('my-span');
const context = span.spanContext();
assert.strictEqual(context.traceFlags, TraceFlags.SAMPLED);
span.end();
});
it('should not sample a trace when OTEL_TRACES_SAMPLER_ARG is out of range', () => {
envSource.OTEL_TRACES_SAMPLER = 'traceidratio';
envSource.OTEL_TRACES_SAMPLER_ARG = '2';
const tracer = new Tracer(
{ name: 'default', version: '0.0.1' },
{},
tracerProvider['_resource'],
tracerProvider['_activeSpanProcessor']
);
const span = tracer.startSpan('my-span');
const context = span.spanContext();
assert.strictEqual(context.traceFlags, TraceFlags.SAMPLED);
span.end();
});
it('should not sample a trace when OTEL_TRACES_SAMPLER_ARG is 0', () => {
envSource.OTEL_TRACES_SAMPLER = 'traceidratio';
envSource.OTEL_TRACES_SAMPLER_ARG = '0';
const tracer = new Tracer(
{ name: 'default', version: '0.0.1' },
{},
tracerProvider['_resource'],
tracerProvider['_activeSpanProcessor']
);
const span = tracer.startSpan('my-span');
const context = span.spanContext();
assert.strictEqual(context.traceFlags, TraceFlags.NONE);
span.end();
});
it('should start an active span with name and function args', () => { it('should start an active span with name and function args', () => {
const tracer = new Tracer( const tracer = new Tracer(
{ name: 'default', version: '0.0.1' }, { name: 'default', version: '0.0.1' },

View File

@ -97,32 +97,6 @@ describe('BatchSpanProcessorBase', () => {
assert.ok(processor instanceof BatchSpanProcessor); assert.ok(processor instanceof BatchSpanProcessor);
processor.shutdown(); processor.shutdown();
}); });
it('should read defaults from environment', () => {
const bspConfig = {
OTEL_BSP_MAX_EXPORT_BATCH_SIZE: 256,
OTEL_BSP_SCHEDULE_DELAY: 2500,
};
let env: Record<string, any>;
if (global.process?.versions?.node === undefined) {
env = globalThis as unknown as Record<string, any>;
} else {
env = process.env as Record<string, any>;
}
Object.entries(bspConfig).forEach(([k, v]) => {
env[k] = v;
});
const processor = new BatchSpanProcessor(exporter);
assert.ok(processor instanceof BatchSpanProcessor);
assert.strictEqual(processor['_maxExportBatchSize'], 256);
assert.strictEqual(processor['_scheduledDelayMillis'], 2500);
processor.shutdown();
Object.keys(bspConfig).forEach(k => delete env[k]);
});
}); });
describe('.onStart/.onEnd/.shutdown', () => { describe('.onStart/.onEnd/.shutdown', () => {

View File

@ -0,0 +1,109 @@
/*
* 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.
*/
import { context, trace } from '@opentelemetry/api';
import * as assert from 'assert';
import * as sinon from 'sinon';
import { BasicTracerProvider } from '../../src';
import { Tracer } from '../../src/Tracer';
describe('BasicTracerProvider - Node', () => {
beforeEach(() => {
// to avoid actually registering the TraceProvider and leaking env to other tests
sinon.stub(trace, 'setGlobalTracerProvider');
context.disable();
});
afterEach(() => {
sinon.restore();
});
describe('constructor', () => {
describe('spanLimits', () => {
describe('when attribute value length limit is defined via env', () => {
it('should have general attribute value length limits value as defined with env', () => {
process.env.OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT = '115';
const tracer = new BasicTracerProvider().getTracer(
'default'
) as Tracer;
const generalLimits = tracer.getGeneralLimits();
assert.strictEqual(generalLimits.attributeValueLengthLimit, 115);
delete process.env.OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT;
});
it('should have span attribute value length limit value same as general limit value', () => {
process.env.OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT = '125';
const tracer = new BasicTracerProvider().getTracer(
'default'
) as Tracer;
const generalLimits = tracer.getGeneralLimits();
const spanLimits = tracer.getSpanLimits();
assert.strictEqual(generalLimits.attributeValueLengthLimit, 125);
assert.strictEqual(spanLimits.attributeValueLengthLimit, 125);
delete process.env.OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT;
});
it('should have span and general attribute value length limits as defined in env', () => {
process.env.OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT = '125';
process.env.OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT = '109';
const tracer = new BasicTracerProvider().getTracer(
'default'
) as Tracer;
const spanLimits = tracer.getSpanLimits();
const generalLimits = tracer.getGeneralLimits();
assert.strictEqual(generalLimits.attributeValueLengthLimit, 125);
assert.strictEqual(spanLimits.attributeValueLengthLimit, 109);
delete process.env.OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT;
delete process.env.OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT;
});
});
describe('when attribute count limit is defined via env', () => {
it('should general attribute count limit as defined with env', () => {
process.env.OTEL_ATTRIBUTE_COUNT_LIMIT = '25';
const tracer = new BasicTracerProvider({}).getTracer(
'default'
) as Tracer;
const generalLimits = tracer.getGeneralLimits();
assert.strictEqual(generalLimits.attributeCountLimit, 25);
delete process.env.OTEL_ATTRIBUTE_COUNT_LIMIT;
});
it('should have span attribute count limit value same as general limit value', () => {
process.env.OTEL_ATTRIBUTE_COUNT_LIMIT = '20';
const tracer = new BasicTracerProvider().getTracer(
'default'
) as Tracer;
const generalLimits = tracer.getGeneralLimits();
const spanLimits = tracer.getSpanLimits();
assert.strictEqual(generalLimits.attributeCountLimit, 20);
assert.strictEqual(spanLimits.attributeCountLimit, 20);
delete process.env.OTEL_ATTRIBUTE_COUNT_LIMIT;
});
it('should have span and general attribute count limits as defined in env', () => {
process.env.OTEL_ATTRIBUTE_COUNT_LIMIT = '20';
process.env.OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT = '35';
const tracer = new BasicTracerProvider().getTracer(
'default'
) as Tracer;
const spanLimits = tracer.getSpanLimits();
const generalLimits = tracer.getGeneralLimits();
assert.strictEqual(generalLimits.attributeCountLimit, 20);
assert.strictEqual(spanLimits.attributeCountLimit, 35);
delete process.env.OTEL_ATTRIBUTE_COUNT_LIMIT;
delete process.env.OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT;
});
});
});
});
});

View File

@ -0,0 +1,80 @@
/*
* 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.
*/
import { context, TraceFlags } from '@opentelemetry/api';
import * as assert from 'assert';
import { BasicTracerProvider } from '../../src';
import { TestStackContextManager } from '../common/export/TestStackContextManager';
import { Tracer } from '../../src/Tracer';
describe('Tracer', () => {
const tracerProvider = new BasicTracerProvider();
beforeEach(() => {
const contextManager = new TestStackContextManager().enable();
context.setGlobalContextManager(contextManager);
});
afterEach(() => {
context.disable();
delete process.env.OTEL_TRACES_SAMPLER;
delete process.env.OTEL_TRACES_SAMPLER_ARG;
});
it('should sample a trace when OTEL_TRACES_SAMPLER_ARG is unset', () => {
process.env.OTEL_TRACES_SAMPLER = 'traceidratio';
process.env.OTEL_TRACES_SAMPLER_ARG = '';
const tracer = new Tracer(
{ name: 'default', version: '0.0.1' },
{},
tracerProvider['_resource'],
tracerProvider['_activeSpanProcessor']
);
const span = tracer.startSpan('my-span');
const context = span.spanContext();
assert.strictEqual(context.traceFlags, TraceFlags.SAMPLED);
span.end();
});
it('should not sample a trace when OTEL_TRACES_SAMPLER_ARG is out of range', () => {
process.env.OTEL_TRACES_SAMPLER = 'traceidratio';
process.env.OTEL_TRACES_SAMPLER_ARG = '2';
const tracer = new Tracer(
{ name: 'default', version: '0.0.1' },
{},
tracerProvider['_resource'],
tracerProvider['_activeSpanProcessor']
);
const span = tracer.startSpan('my-span');
const context = span.spanContext();
assert.strictEqual(context.traceFlags, TraceFlags.SAMPLED);
span.end();
});
it('should not sample a trace when OTEL_TRACES_SAMPLER_ARG is 0', () => {
process.env.OTEL_TRACES_SAMPLER = 'traceidratio';
process.env.OTEL_TRACES_SAMPLER_ARG = '0';
const tracer = new Tracer(
{ name: 'default', version: '0.0.1' },
{},
tracerProvider['_resource'],
tracerProvider['_activeSpanProcessor']
);
const span = tracer.startSpan('my-span');
const context = span.spanContext();
assert.strictEqual(context.traceFlags, TraceFlags.NONE);
span.end();
});
});

View File

@ -24,34 +24,27 @@ import {
import { buildSamplerFromEnv } from '../../src/config'; import { buildSamplerFromEnv } from '../../src/config';
describe('config', () => { describe('config', () => {
let envSource: Record<string, any>;
if (global.process?.versions?.node === undefined) {
envSource = globalThis as unknown as Record<string, any>;
} else {
envSource = process.env as Record<string, any>;
}
describe('buildSamplerFromEnv()', () => { describe('buildSamplerFromEnv()', () => {
afterEach(() => { afterEach(() => {
delete envSource.OTEL_TRACES_SAMPLER; delete process.env.OTEL_TRACES_SAMPLER;
delete envSource.OTEL_TRACES_SAMPLER_ARG; delete process.env.OTEL_TRACES_SAMPLER_ARG;
}); });
it('should handle always_on case', () => { it('should handle always_on case', () => {
envSource.OTEL_TRACES_SAMPLER = 'always_on'; process.env.OTEL_TRACES_SAMPLER = 'always_on';
assert.ok(buildSamplerFromEnv() instanceof AlwaysOnSampler); assert.ok(buildSamplerFromEnv() instanceof AlwaysOnSampler);
assert.strictEqual(buildSamplerFromEnv().toString(), 'AlwaysOnSampler'); assert.strictEqual(buildSamplerFromEnv().toString(), 'AlwaysOnSampler');
}); });
it('should handle always_off case', () => { it('should handle always_off case', () => {
envSource.OTEL_TRACES_SAMPLER = 'always_off'; process.env.OTEL_TRACES_SAMPLER = 'always_off';
assert.ok(buildSamplerFromEnv() instanceof AlwaysOffSampler); assert.ok(buildSamplerFromEnv() instanceof AlwaysOffSampler);
assert.strictEqual(buildSamplerFromEnv().toString(), 'AlwaysOffSampler'); assert.strictEqual(buildSamplerFromEnv().toString(), 'AlwaysOffSampler');
}); });
it('should handle traceidratio case', () => { it('should handle traceidratio case', () => {
envSource.OTEL_TRACES_SAMPLER = 'traceidratio'; process.env.OTEL_TRACES_SAMPLER = 'traceidratio';
envSource.OTEL_TRACES_SAMPLER_ARG = '0.1'; process.env.OTEL_TRACES_SAMPLER_ARG = '0.1';
assert.ok(buildSamplerFromEnv() instanceof TraceIdRatioBasedSampler); assert.ok(buildSamplerFromEnv() instanceof TraceIdRatioBasedSampler);
assert.strictEqual( assert.strictEqual(
buildSamplerFromEnv().toString(), buildSamplerFromEnv().toString(),
@ -60,7 +53,7 @@ describe('config', () => {
}); });
it('should handle parentbased_always_on case', () => { it('should handle parentbased_always_on case', () => {
envSource.OTEL_TRACES_SAMPLER = 'parentbased_always_on'; process.env.OTEL_TRACES_SAMPLER = 'parentbased_always_on';
assert.ok(buildSamplerFromEnv() instanceof ParentBasedSampler); assert.ok(buildSamplerFromEnv() instanceof ParentBasedSampler);
assert.strictEqual( assert.strictEqual(
buildSamplerFromEnv().toString(), buildSamplerFromEnv().toString(),
@ -69,7 +62,7 @@ describe('config', () => {
}); });
it('should handle parentbased_always_off case', () => { it('should handle parentbased_always_off case', () => {
envSource.OTEL_TRACES_SAMPLER = 'parentbased_always_off'; process.env.OTEL_TRACES_SAMPLER = 'parentbased_always_off';
assert.ok(buildSamplerFromEnv() instanceof ParentBasedSampler); assert.ok(buildSamplerFromEnv() instanceof ParentBasedSampler);
assert.strictEqual( assert.strictEqual(
buildSamplerFromEnv().toString(), buildSamplerFromEnv().toString(),
@ -78,8 +71,8 @@ describe('config', () => {
}); });
it('should handle parentbased_traceidratio case', () => { it('should handle parentbased_traceidratio case', () => {
envSource.OTEL_TRACES_SAMPLER = 'parentbased_traceidratio'; process.env.OTEL_TRACES_SAMPLER = 'parentbased_traceidratio';
envSource.OTEL_TRACES_SAMPLER_ARG = '0.2'; process.env.OTEL_TRACES_SAMPLER_ARG = '0.2';
assert.ok(buildSamplerFromEnv() instanceof ParentBasedSampler); assert.ok(buildSamplerFromEnv() instanceof ParentBasedSampler);
assert.strictEqual( assert.strictEqual(
buildSamplerFromEnv().toString(), buildSamplerFromEnv().toString(),

View File

@ -0,0 +1,54 @@
/*
* 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.
*/
import * as assert from 'assert';
import { BufferConfig, InMemorySpanExporter } from '../../../src';
import { BatchSpanProcessorBase } from '../../../src/export/BatchSpanProcessorBase';
class BatchSpanProcessor extends BatchSpanProcessorBase<BufferConfig> {
onShutdown() {}
}
describe('BatchSpanProcessorBase', () => {
describe('constructor', () => {
it('should read defaults from environment', () => {
const exporter = new InMemorySpanExporter();
const bspConfig = {
OTEL_BSP_MAX_EXPORT_BATCH_SIZE: 256,
OTEL_BSP_SCHEDULE_DELAY: 2500,
};
let env: Record<string, any>;
if (global.process?.versions?.node === undefined) {
env = globalThis as unknown as Record<string, any>;
} else {
env = process.env as Record<string, any>;
}
Object.entries(bspConfig).forEach(([k, v]) => {
env[k] = v;
});
const processor = new BatchSpanProcessor(exporter);
assert.ok(processor instanceof BatchSpanProcessor);
assert.strictEqual(processor['_maxExportBatchSize'], 256);
assert.strictEqual(processor['_scheduledDelayMillis'], 2500);
processor.shutdown();
Object.keys(bspConfig).forEach(k => delete env[k]);
});
});
});