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): `TimeOriginLegacy` 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
* (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)
* Renames `Resource` class to `ResourceImpl` and makes it package-private
* Renames `IResource` interface to `Resource`

View File

@ -1,11 +1,11 @@
/*!
/*
* 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
*
* 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
* distributed under the License is distributed on an "AS IS" BASIS,
@ -17,8 +17,12 @@
const karmaWebpackConfig = require('../../karma.webpack');
const karmaBaseConfig = require('../../karma.base');
module.exports = (config) => {
config.set(Object.assign({}, karmaBaseConfig, {
module.exports = config => {
config.set(
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
*
* 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
*
* 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
* distributed under the License is distributed on an "AS IS" BASIS,
@ -17,8 +17,17 @@
const karmaWebpackConfig = require('../../karma.webpack');
const karmaBaseConfig = require('../../karma.worker');
module.exports = (config) => {
config.set(Object.assign({}, karmaBaseConfig, {
module.exports = config => {
config.set(
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 { getEnv, ENVIRONMENT } from '@opentelemetry/core';
import { getNumberFromEnv, getStringFromEnv } from '@opentelemetry/core';
import { Sampler } from './Sampler';
import { AlwaysOffSampler } from './sampler/AlwaysOffSampler';
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
// envs are parsed before tests are ran - causes tests using these envs to fail
export function loadDefaultConfig() {
const env = getEnv();
return {
sampler: buildSamplerFromEnv(env),
sampler: buildSamplerFromEnv(),
forceFlushTimeoutMillis: 30000,
generalLimits: {
attributeValueLengthLimit: env.OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT,
attributeCountLimit: env.OTEL_ATTRIBUTE_COUNT_LIMIT,
attributeValueLengthLimit:
getNumberFromEnv('OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT') ?? Infinity,
attributeCountLimit:
getNumberFromEnv('OTEL_ATTRIBUTE_COUNT_LIMIT') ?? 128,
},
spanLimits: {
attributeValueLengthLimit: env.OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT,
attributeCountLimit: env.OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT,
linkCountLimit: env.OTEL_SPAN_LINK_COUNT_LIMIT,
eventCountLimit: env.OTEL_SPAN_EVENT_COUNT_LIMIT,
attributeValueLengthLimit:
getNumberFromEnv('OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT') ?? Infinity,
attributeCountLimit:
getNumberFromEnv('OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT') ?? 128,
linkCountLimit: getNumberFromEnv('OTEL_SPAN_LINK_COUNT_LIMIT') ?? 128,
eventCountLimit: getNumberFromEnv('OTEL_SPAN_EVENT_COUNT_LIMIT') ?? 128,
attributePerEventCountLimit:
env.OTEL_SPAN_ATTRIBUTE_PER_EVENT_COUNT_LIMIT,
attributePerLinkCountLimit: env.OTEL_SPAN_ATTRIBUTE_PER_LINK_COUNT_LIMIT,
getNumberFromEnv('OTEL_SPAN_ATTRIBUTE_PER_EVENT_COUNT_LIMIT') ?? 128,
attributePerLinkCountLimit:
getNumberFromEnv('OTEL_SPAN_ATTRIBUTE_PER_LINK_COUNT_LIMIT') ?? 128,
},
};
}
/**
* 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(
environment: Required<ENVIRONMENT> = getEnv()
): Sampler {
switch (environment.OTEL_TRACES_SAMPLER) {
export function buildSamplerFromEnv(): Sampler {
const sampler =
getStringFromEnv('OTEL_TRACES_SAMPLER') ??
TracesSamplerValues.ParentBasedAlwaysOn;
switch (sampler) {
case TracesSamplerValues.AlwaysOn:
return new AlwaysOnSampler();
case TracesSamplerValues.AlwaysOff:
@ -86,48 +89,31 @@ export function buildSamplerFromEnv(
root: new AlwaysOffSampler(),
});
case TracesSamplerValues.TraceIdRatio:
return new TraceIdRatioBasedSampler(
getSamplerProbabilityFromEnv(environment)
);
return new TraceIdRatioBasedSampler(getSamplerProbabilityFromEnv());
case TracesSamplerValues.ParentBasedTraceIdRatio:
return new ParentBasedSampler({
root: new TraceIdRatioBasedSampler(
getSamplerProbabilityFromEnv(environment)
),
root: new TraceIdRatioBasedSampler(getSamplerProbabilityFromEnv()),
});
default:
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();
}
}
function getSamplerProbabilityFromEnv(
environment: Required<ENVIRONMENT>
): number | undefined {
if (
environment.OTEL_TRACES_SAMPLER_ARG === undefined ||
environment.OTEL_TRACES_SAMPLER_ARG === ''
) {
function getSamplerProbabilityFromEnv(): number | undefined {
const probability = getNumberFromEnv('OTEL_TRACES_SAMPLER_ARG');
if (probability == null) {
diag.error(
`OTEL_TRACES_SAMPLER_ARG is blank, defaulting to ${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) {
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;
}

View File

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

View File

@ -20,7 +20,7 @@ import { SpanLimits, TracerConfig, GeneralLimits } from './types';
import {
DEFAULT_ATTRIBUTE_COUNT_LIMIT,
DEFAULT_ATTRIBUTE_VALUE_LENGTH_LIMIT,
getEnvWithoutDefaults,
getNumberFromEnv,
} from '@opentelemetry/core';
/**
@ -68,16 +68,14 @@ export function mergeConfig(userConfig: TracerConfig): TracerConfig & {
export function reconfigureLimits(userConfig: TracerConfig): TracerConfig {
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
*/
spanLimits.attributeCountLimit =
userConfig.spanLimits?.attributeCountLimit ??
userConfig.generalLimits?.attributeCountLimit ??
parsedEnvConfig.OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT ??
parsedEnvConfig.OTEL_ATTRIBUTE_COUNT_LIMIT ??
getNumberFromEnv('OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT') ??
getNumberFromEnv('OTEL_ATTRIBUTE_COUNT_LIMIT') ??
DEFAULT_ATTRIBUTE_COUNT_LIMIT;
/**
@ -86,8 +84,8 @@ export function reconfigureLimits(userConfig: TracerConfig): TracerConfig {
spanLimits.attributeValueLengthLimit =
userConfig.spanLimits?.attributeValueLengthLimit ??
userConfig.generalLimits?.attributeValueLengthLimit ??
parsedEnvConfig.OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT ??
parsedEnvConfig.OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT ??
getNumberFromEnv('OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT') ??
getNumberFromEnv('OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT') ??
DEFAULT_ATTRIBUTE_VALUE_LENGTH_LIMIT;
return Object.assign({}, userConfig, { spanLimits });

View File

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

View File

@ -62,16 +62,9 @@ class DummyPropagator implements TextMapPropagator {
}
describe('BasicTracerProvider', () => {
let envSource: Record<string, any>;
let setGlobalPropagatorStub: sinon.SinonSpy<[TextMapPropagator], 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(() => {
// to avoid actually registering the TraceProvider and leaking env to other tests
sinon.stub(trace, 'setGlobalTracerProvider');
@ -237,56 +230,7 @@ describe('BasicTracerProvider', () => {
});
});
describe('when attribute value length limit is defined via env', () => {
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', () => {
describe('when attribute value length limit is not defined', () => {
it('should use default value of Infinity', () => {
const tracer = new BasicTracerProvider().getTracer(
'default'
@ -298,56 +242,7 @@ describe('BasicTracerProvider', () => {
});
});
describe('when attribute count limit is defined via env', () => {
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', () => {
describe('when attribute count limit is not defined', () => {
it('should use default value of 128', () => {
const tracer = new BasicTracerProvider().getTracer(
'default'

View File

@ -50,12 +50,6 @@ import { Tracer } from '../../src/Tracer';
describe('Tracer', () => {
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 {
constructor(private readonly traceState?: TraceState) {}
@ -106,8 +100,6 @@ describe('Tracer', () => {
afterEach(() => {
context.disable();
delete envSource.OTEL_TRACES_SAMPLER;
delete envSource.OTEL_TRACES_SAMPLER_ARG;
});
it('should create a Tracer instance', () => {
@ -327,51 +319,6 @@ describe('Tracer', () => {
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', () => {
const tracer = new Tracer(
{ name: 'default', version: '0.0.1' },

View File

@ -97,32 +97,6 @@ describe('BatchSpanProcessorBase', () => {
assert.ok(processor instanceof BatchSpanProcessor);
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', () => {

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';
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()', () => {
afterEach(() => {
delete envSource.OTEL_TRACES_SAMPLER;
delete envSource.OTEL_TRACES_SAMPLER_ARG;
delete process.env.OTEL_TRACES_SAMPLER;
delete process.env.OTEL_TRACES_SAMPLER_ARG;
});
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.strictEqual(buildSamplerFromEnv().toString(), 'AlwaysOnSampler');
});
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.strictEqual(buildSamplerFromEnv().toString(), 'AlwaysOffSampler');
});
it('should handle traceidratio case', () => {
envSource.OTEL_TRACES_SAMPLER = 'traceidratio';
envSource.OTEL_TRACES_SAMPLER_ARG = '0.1';
process.env.OTEL_TRACES_SAMPLER = 'traceidratio';
process.env.OTEL_TRACES_SAMPLER_ARG = '0.1';
assert.ok(buildSamplerFromEnv() instanceof TraceIdRatioBasedSampler);
assert.strictEqual(
buildSamplerFromEnv().toString(),
@ -60,7 +53,7 @@ describe('config', () => {
});
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.strictEqual(
buildSamplerFromEnv().toString(),
@ -69,7 +62,7 @@ describe('config', () => {
});
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.strictEqual(
buildSamplerFromEnv().toString(),
@ -78,8 +71,8 @@ describe('config', () => {
});
it('should handle parentbased_traceidratio case', () => {
envSource.OTEL_TRACES_SAMPLER = 'parentbased_traceidratio';
envSource.OTEL_TRACES_SAMPLER_ARG = '0.2';
process.env.OTEL_TRACES_SAMPLER = 'parentbased_traceidratio';
process.env.OTEL_TRACES_SAMPLER_ARG = '0.2';
assert.ok(buildSamplerFromEnv() instanceof ParentBasedSampler);
assert.strictEqual(
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]);
});
});
});