Skip to content

Commit

Permalink
Merge branch 'main' into http-debug-enrich-logger
Browse files Browse the repository at this point in the history
  • Loading branch information
obecny committed Sep 9, 2021
2 parents 60e8845 + feea516 commit 915da21
Show file tree
Hide file tree
Showing 13 changed files with 606 additions and 13 deletions.
12 changes: 10 additions & 2 deletions packages/opentelemetry-core/src/utils/environment.ts
Expand Up @@ -28,6 +28,8 @@ const ENVIRONMENT_NUMBERS_KEYS = [
'OTEL_BSP_MAX_EXPORT_BATCH_SIZE',
'OTEL_BSP_MAX_QUEUE_SIZE',
'OTEL_BSP_SCHEDULE_DELAY',
'OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT',
'OTEL_ATTRIBUTE_COUNT_LIMIT',
'OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT',
'OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT',
'OTEL_SPAN_EVENT_COUNT_LIMIT',
Expand Down Expand Up @@ -88,6 +90,10 @@ export type RAW_ENVIRONMENT = {
[key: string]: string | number | undefined | string[];
};

export const DEFAULT_ATTRIBUTE_VALUE_LENGTH_LIMIT = Infinity;

export const DEFAULT_ATTRIBUTE_COUNT_LIMIT = 128;

/**
* Default environment variables
*/
Expand Down Expand Up @@ -118,8 +124,10 @@ export const DEFAULT_ENVIRONMENT: Required<ENVIRONMENT> = {
OTEL_PROPAGATORS: ['tracecontext', 'baggage'],
OTEL_RESOURCE_ATTRIBUTES: '',
OTEL_SERVICE_NAME: '',
OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT: Infinity,
OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT: 128,
OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT: DEFAULT_ATTRIBUTE_VALUE_LENGTH_LIMIT,
OTEL_ATTRIBUTE_COUNT_LIMIT: DEFAULT_ATTRIBUTE_COUNT_LIMIT,
OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT: DEFAULT_ATTRIBUTE_VALUE_LENGTH_LIMIT ,
OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT: DEFAULT_ATTRIBUTE_COUNT_LIMIT,
OTEL_SPAN_EVENT_COUNT_LIMIT: 128,
OTEL_SPAN_LINK_COUNT_LIMIT: 128,
OTEL_TRACES_EXPORTER: 'none',
Expand Down
4 changes: 4 additions & 0 deletions packages/opentelemetry-core/test/utils/environment.test.ts
Expand Up @@ -84,6 +84,8 @@ describe('environment', () => {
OTEL_LOG_LEVEL: 'ERROR',
OTEL_NO_PATCH_MODULES: 'a,b,c',
OTEL_RESOURCE_ATTRIBUTES: '<attrs>',
OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT: 40,
OTEL_ATTRIBUTE_COUNT_LIMIT: 50,
OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT: 100,
OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT: 10,
OTEL_SPAN_EVENT_COUNT_LIMIT: 20,
Expand All @@ -94,6 +96,8 @@ describe('environment', () => {
const env = getEnv();
assert.deepStrictEqual(env.OTEL_NO_PATCH_MODULES, ['a', 'b', 'c']);
assert.strictEqual(env.OTEL_LOG_LEVEL, DiagLogLevel.ERROR);
assert.strictEqual(env.OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT, 40);
assert.strictEqual(env.OTEL_ATTRIBUTE_COUNT_LIMIT, 50);
assert.strictEqual(env.OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT, 100);
assert.strictEqual(env.OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT, 10);
assert.strictEqual(env.OTEL_SPAN_EVENT_COUNT_LIMIT, 20);
Expand Down
Expand Up @@ -59,6 +59,18 @@ export abstract class InstrumentationBase<T = any>
}
}

private _extractPackageVersion(baseDir: string): string | undefined {
try {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const version = require(path.join(baseDir, 'package.json')).version;
return typeof version === 'string' ? version : undefined;
} catch (error) {
diag.warn('Failed extracting version', baseDir);
}

return undefined;
}

private _onRequire<T>(
module: InstrumentationModuleDefinition<T>,
exports: T,
Expand All @@ -73,13 +85,11 @@ export abstract class InstrumentationBase<T = any>
return exports;
}

// eslint-disable-next-line @typescript-eslint/no-var-requires
const version = require(path.join(baseDir, 'package.json')).version;
const version = this._extractPackageVersion(baseDir);
module.moduleVersion = version;
if (module.name === name) {
// main module
if (
typeof version === 'string' &&
isSupported(module.supportedVersions, version, module.includePrerelease)
) {
if (typeof module.patch === 'function') {
Expand Down Expand Up @@ -167,7 +177,12 @@ export abstract class InstrumentationBase<T = any>
}
}

function isSupported(supportedVersions: string[], version: string, includePrerelease?: boolean): boolean {
function isSupported(supportedVersions: string[], version?: string, includePrerelease?: boolean): boolean {
if (typeof version === 'undefined') {
// If we don't have the version, accept the wildcard case only
return supportedVersions.includes('*');
}

return supportedVersions.some(supportedVersion => {
return satisfies(version, supportedVersion, { includePrerelease });
});
Expand Down
@@ -0,0 +1,166 @@
/*
* 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 * as sinon from 'sinon';
import { InstrumentationBase, InstrumentationModuleDefinition } from '../../src';

const MODULE_NAME = 'test-module';
const MODULE_FILE_NAME = 'test-module-file';
const MODULE_VERSION = '0.1.0';
const WILDCARD_VERSION = '*';
const MODULE_DIR = '/random/dir';

class TestInstrumentation extends InstrumentationBase {
constructor() {
super(MODULE_NAME, MODULE_VERSION);
}

init() {}
}

describe('InstrumentationBase', () => {
describe('_onRequire - module version is not available', () => {
// For all of these cases, there is no indication of the actual module version,
// so we require there to be a wildcard supported version.

let instrumentation: TestInstrumentation;
let modulePatchSpy: sinon.SinonSpy;

beforeEach(() => {
instrumentation = new TestInstrumentation();
// @ts-expect-error access internal property for testing
instrumentation._enabled = true;
modulePatchSpy = sinon.spy();
});

describe('when patching a module', () => {
describe('AND there is no wildcard supported version', () => {
it('should not patch module', () => {
const moduleExports = {};
const instrumentationModule = {
supportedVersions: [`^${MODULE_VERSION}`],
name: MODULE_NAME,
patch: modulePatchSpy as unknown,
} as InstrumentationModuleDefinition<unknown>;

// @ts-expect-error access internal property for testing
instrumentation._onRequire<unknown>(
instrumentationModule,
moduleExports,
MODULE_NAME,
MODULE_DIR
);

assert.strictEqual(instrumentationModule.moduleVersion, undefined);
assert.strictEqual(instrumentationModule.moduleExports, undefined);
sinon.assert.notCalled(modulePatchSpy);
});
});

describe('AND there is a wildcard supported version', () => {
it('should patch module', () => {
const moduleExports = {};
const instrumentationModule = {
supportedVersions: [`^${MODULE_VERSION}`, WILDCARD_VERSION],
name: MODULE_NAME,
patch: modulePatchSpy as unknown,
} as InstrumentationModuleDefinition<unknown>;

// @ts-expect-error access internal property for testing
instrumentation._onRequire<unknown>(
instrumentationModule,
moduleExports,
MODULE_NAME,
MODULE_DIR
);

assert.strictEqual(instrumentationModule.moduleVersion, undefined);
assert.strictEqual(instrumentationModule.moduleExports, moduleExports);
sinon.assert.calledOnceWithExactly(modulePatchSpy, moduleExports, undefined);
});
});
});

describe('when patching module files', () => {
let filePatchSpy: sinon.SinonSpy;

beforeEach(() => {
filePatchSpy = sinon.spy();
})

describe('AND there is no wildcard supported version', () => {
it('should not patch module file', () => {
const moduleExports = {};
const supportedVersions = [`^${MODULE_VERSION}`];
const instrumentationModule = {
supportedVersions,
name: MODULE_NAME,
patch: modulePatchSpy as unknown,
files: [{
name: MODULE_FILE_NAME,
supportedVersions,
patch: filePatchSpy as unknown
}]
} as InstrumentationModuleDefinition<unknown>;

// @ts-expect-error access internal property for testing
instrumentation._onRequire<unknown>(
instrumentationModule,
moduleExports,
MODULE_FILE_NAME,
MODULE_DIR
);

assert.strictEqual(instrumentationModule.moduleVersion, undefined);
assert.strictEqual(instrumentationModule.moduleExports, undefined);
sinon.assert.notCalled(modulePatchSpy);
sinon.assert.notCalled(filePatchSpy);
});
});

describe('AND there is a wildcard supported version', () => {
it('should patch module file', () => {
const moduleExports = {};
const supportedVersions = [`^${MODULE_VERSION}`, WILDCARD_VERSION];
const instrumentationModule = {
supportedVersions,
name: MODULE_NAME,
patch: modulePatchSpy as unknown,
files: [{
name: MODULE_FILE_NAME,
supportedVersions,
patch: filePatchSpy as unknown
}]
} as InstrumentationModuleDefinition<unknown>;

// @ts-expect-error access internal property for testing
instrumentation._onRequire<unknown>(
instrumentationModule,
moduleExports,
MODULE_FILE_NAME,
MODULE_DIR
);

assert.strictEqual(instrumentationModule.moduleVersion, undefined);
assert.strictEqual(instrumentationModule.files[0].moduleExports, moduleExports);
sinon.assert.notCalled(modulePatchSpy);
sinon.assert.calledOnceWithExactly(filePatchSpy, moduleExports, undefined);
});
});
});
});
});
9 changes: 8 additions & 1 deletion packages/opentelemetry-sdk-trace-base/src/Tracer.ts
Expand Up @@ -25,7 +25,7 @@ import {
import { Resource } from '@opentelemetry/resources';
import { BasicTracerProvider } from './BasicTracerProvider';
import { Span } from './Span';
import { SpanLimits, TracerConfig } from './types';
import { GeneralLimits, SpanLimits, TracerConfig } from './types';
import { mergeConfig } from './utility';
import { SpanProcessor } from './SpanProcessor';

Expand All @@ -34,6 +34,7 @@ import { SpanProcessor } from './SpanProcessor';
*/
export class Tracer implements api.Tracer {
private readonly _sampler: api.Sampler;
private readonly _generalLimits: GeneralLimits;
private readonly _spanLimits: SpanLimits;
private readonly _idGenerator: IdGenerator;
readonly resource: Resource;
Expand All @@ -49,6 +50,7 @@ export class Tracer implements api.Tracer {
) {
const localConfig = mergeConfig(config);
this._sampler = localConfig.sampler;
this._generalLimits = localConfig.generalLimits;
this._spanLimits = localConfig.spanLimits;
this._idGenerator = config.idGenerator || new RandomIdGenerator();
this.resource = _tracerProvider.resource;
Expand Down Expand Up @@ -212,6 +214,11 @@ export class Tracer implements api.Tracer {
return api.context.with(contextWithSpanSet, fn, undefined, span);
}

/** Returns the active {@link GeneralLimits}. */
getGeneralLimits(): GeneralLimits {
return this._generalLimits;
}

/** Returns the active {@link SpanLimits}. */
getSpanLimits(): SpanLimits {
return this._spanLimits;
Expand Down
4 changes: 4 additions & 0 deletions packages/opentelemetry-sdk-trace-base/src/config.ts
Expand Up @@ -38,6 +38,10 @@ const DEFAULT_RATIO = 1;
export const DEFAULT_CONFIG = {
sampler: buildSamplerFromEnv(env),
forceFlushTimeoutMillis: 30000,
generalLimits: {
attributeValueLengthLimit: getEnv().OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT,
attributeCountLimit: getEnv().OTEL_ATTRIBUTE_COUNT_LIMIT,
},
spanLimits: {
attributeValueLengthLimit: getEnv().OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT,
attributeCountLimit: getEnv().OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT,
Expand Down
11 changes: 11 additions & 0 deletions packages/opentelemetry-sdk-trace-base/src/types.ts
Expand Up @@ -29,6 +29,9 @@ export interface TracerConfig {
*/
sampler?: Sampler;

/** General Limits */
generalLimits?: GeneralLimits;

/** Span Limits */
spanLimits?: SpanLimits;

Expand Down Expand Up @@ -61,6 +64,14 @@ export interface SDKRegistrationConfig {
contextManager?: ContextManager | null;
}

/** Global configuration limits of trace service */
export interface GeneralLimits {
/** attributeValueLengthLimit is maximum allowed attribute value size */
attributeValueLengthLimit?: number;
/** attributeCountLimit is number of attributes per trace */
attributeCountLimit?: number;
}

/** Global configuration of trace service */
export interface SpanLimits {
/** attributeValueLengthLimit is maximum allowed attribute value size */
Expand Down
32 changes: 30 additions & 2 deletions packages/opentelemetry-sdk-trace-base/src/utility.ts
Expand Up @@ -14,15 +14,21 @@
* limitations under the License.
*/

import { DEFAULT_ATTRIBUTE_VALUE_LENGTH_LIMIT, DEFAULT_ATTRIBUTE_COUNT_LIMIT } from '@opentelemetry/core';

import { Sampler } from '@opentelemetry/api';
import { buildSamplerFromEnv, DEFAULT_CONFIG } from './config';
import { SpanLimits, TracerConfig } from './types';
import { SpanLimits, TracerConfig, GeneralLimits } from './types';

/**
* Function to merge Default configuration (as specified in './config') with
* user provided configurations.
*/
export function mergeConfig(userConfig: TracerConfig): TracerConfig & { sampler: Sampler; spanLimits: SpanLimits } {
export function mergeConfig(userConfig: TracerConfig): TracerConfig & {
sampler: Sampler;
spanLimits: SpanLimits;
generalLimits: GeneralLimits;
} {
const perInstanceDefaults: Partial<TracerConfig> = {
sampler: buildSamplerFromEnv(),
};
Expand All @@ -34,11 +40,33 @@ export function mergeConfig(userConfig: TracerConfig): TracerConfig & { sampler:
userConfig
);

target.generalLimits = Object.assign(
{},
DEFAULT_CONFIG.generalLimits,
userConfig.generalLimits || {}
);

target.spanLimits = Object.assign(
{},
DEFAULT_CONFIG.spanLimits,
userConfig.spanLimits || {}
);

/**
* When span attribute count limit is not defined, but general attribute count limit is defined
* Then, span attribute count limit will be same as general one
*/
if (target.spanLimits.attributeCountLimit === DEFAULT_ATTRIBUTE_COUNT_LIMIT && target.generalLimits.attributeCountLimit !== DEFAULT_ATTRIBUTE_COUNT_LIMIT) {
target.spanLimits.attributeCountLimit = target.generalLimits.attributeCountLimit;
}

/**
* When span attribute value length limit is not defined, but general attribute value length limit is defined
* Then, span attribute value length limit will be same as general one
*/
if (target.spanLimits.attributeValueLengthLimit === DEFAULT_ATTRIBUTE_VALUE_LENGTH_LIMIT && target.generalLimits.attributeValueLengthLimit !== DEFAULT_ATTRIBUTE_VALUE_LENGTH_LIMIT) {
target.spanLimits.attributeValueLengthLimit = target.generalLimits.attributeValueLengthLimit;
}

return target;
}

0 comments on commit 915da21

Please sign in to comment.