Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(opentelemetry-sdk-trace-base): implemented general limits of attributes #2430

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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;
rauno56 marked this conversation as resolved.
Show resolved Hide resolved

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
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 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we have this function? It is not part of the interface. If it is just for testing, you can access the private field by doing tracer["_generalLimits"] in the test.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can access the way you mentioned.

I just followed the pattern we already have like getSpanLimits(): SpanLimits { in line 223

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;
}
Expand Up @@ -85,6 +85,52 @@ describe('BasicTracerProvider', () => {
});
});

describe('generalLimits', () => {
describe('when not defined default values', () => {
it('should have tracer with default values', () => {
const tracer = new BasicTracerProvider({}).getTracer('default');
assert.deepStrictEqual(tracer.getGeneralLimits(), {
attributeValueLengthLimit: Infinity,
attributeCountLimit: 128,
});
});
});

describe('when "attributeCountLimit" is defined', () => {
it('should have tracer with defined value', () => {
const tracer = new BasicTracerProvider({
generalLimits: {
attributeCountLimit: 100,
},
}).getTracer('default');
const generalLimits = tracer.getGeneralLimits();
assert.strictEqual(generalLimits.attributeCountLimit, 100);
});
});

describe('when "attributeValueLengthLimit" is defined', () => {
it('should have tracer with defined value', () => {
const tracer = new BasicTracerProvider({
generalLimits: {
attributeValueLengthLimit: 10,
},
}).getTracer('default');
const generalLimits = tracer.getGeneralLimits();
assert.strictEqual(generalLimits.attributeValueLengthLimit, 10);
});

it('should have tracer with negative "attributeValueLengthLimit" value', () => {
const tracer = new BasicTracerProvider({
generalLimits: {
attributeValueLengthLimit: -10,
},
}).getTracer('default');
const generalLimits = tracer.getGeneralLimits();
assert.strictEqual(generalLimits.attributeValueLengthLimit, -10);
});
});
});

describe('spanLimits', () => {
describe('when not defined default values', () => {
it('should have tracer with default values', () => {
Expand Down Expand Up @@ -155,6 +201,38 @@ describe('BasicTracerProvider', () => {
assert.strictEqual(spanLimits.linkCountLimit, 10);
});
});

describe('when only generalLimits are defined', () => {
it('should have span limits as general limits', () => {
const tracer = new BasicTracerProvider({
generalLimits: {
attributeValueLengthLimit: 100,
attributeCountLimit: 200,
},
}).getTracer('default');
const spanLimits = tracer.getSpanLimits();
assert.strictEqual(spanLimits.attributeValueLengthLimit, 100);
assert.strictEqual(spanLimits.attributeCountLimit, 200);
});
});

describe('when both generalLimits and spanLimits defined', () => {
it('should have span limits as priority than general limits', () => {
const tracer = new BasicTracerProvider({
generalLimits: {
attributeValueLengthLimit: 100,
attributeCountLimit: 200,
},
spanLimits: {
attributeValueLengthLimit: 10,
attributeCountLimit: 20,
},
}).getTracer('default');
const spanLimits = tracer.getSpanLimits();
assert.strictEqual(spanLimits.attributeValueLengthLimit, 10);
assert.strictEqual(spanLimits.attributeCountLimit, 20);
});
});
});
});

Expand Down