Skip to content

Commit 835e9c5

Browse files
Merge 97e619b into 5bb9fcc
2 parents 5bb9fcc + 97e619b commit 835e9c5

File tree

7 files changed

+302
-126
lines changed

7 files changed

+302
-126
lines changed

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

+2
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ const ENVIRONMENT_NUMBERS_KEYS = [
2828
'OTEL_BSP_MAX_EXPORT_BATCH_SIZE',
2929
'OTEL_BSP_MAX_QUEUE_SIZE',
3030
'OTEL_BSP_SCHEDULE_DELAY',
31+
'OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT',
3132
'OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT',
3233
'OTEL_SPAN_EVENT_COUNT_LIMIT',
3334
'OTEL_SPAN_LINK_COUNT_LIMIT',
@@ -117,6 +118,7 @@ export const DEFAULT_ENVIRONMENT: Required<ENVIRONMENT> = {
117118
OTEL_PROPAGATORS: ['tracecontext', 'baggage'],
118119
OTEL_RESOURCE_ATTRIBUTES: '',
119120
OTEL_SERVICE_NAME: '',
121+
OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT: Infinity,
120122
OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT: 128,
121123
OTEL_SPAN_EVENT_COUNT_LIMIT: 128,
122124
OTEL_SPAN_LINK_COUNT_LIMIT: 128,

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

+2
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ describe('environment', () => {
8484
OTEL_LOG_LEVEL: 'ERROR',
8585
OTEL_NO_PATCH_MODULES: 'a,b,c',
8686
OTEL_RESOURCE_ATTRIBUTES: '<attrs>',
87+
OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT: 100,
8788
OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT: 10,
8889
OTEL_SPAN_EVENT_COUNT_LIMIT: 20,
8990
OTEL_SPAN_LINK_COUNT_LIMIT: 30,
@@ -93,6 +94,7 @@ describe('environment', () => {
9394
const env = getEnv();
9495
assert.deepStrictEqual(env.OTEL_NO_PATCH_MODULES, ['a', 'b', 'c']);
9596
assert.strictEqual(env.OTEL_LOG_LEVEL, DiagLogLevel.ERROR);
97+
assert.strictEqual(env.OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT, 100);
9698
assert.strictEqual(env.OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT, 10);
9799
assert.strictEqual(env.OTEL_SPAN_EVENT_COUNT_LIMIT, 20);
98100
assert.strictEqual(env.OTEL_SPAN_LINK_COUNT_LIMIT, 30);

packages/opentelemetry-sdk-trace-base/src/Span.ts

+48-1
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ export class Span implements api.Span, ReadableSpan {
5757
private _duration: api.HrTime = [-1, -1];
5858
private readonly _spanProcessor: SpanProcessor;
5959
private readonly _spanLimits: SpanLimits;
60+
private readonly _attributeValueLengthLimit: number;
6061

6162
/** Constructs a new Span instance. */
6263
constructor(
@@ -80,6 +81,7 @@ export class Span implements api.Span, ReadableSpan {
8081
this._spanLimits = parentTracer.getSpanLimits();
8182
this._spanProcessor = parentTracer.getActiveSpanProcessor();
8283
this._spanProcessor.onStart(this, context);
84+
this._attributeValueLengthLimit = this._spanLimits.attributeValueLengthLimit || 0;
8385
}
8486

8587
spanContext(): api.SpanContext {
@@ -105,7 +107,7 @@ export class Span implements api.Span, ReadableSpan {
105107
) {
106108
return this;
107109
}
108-
this.attributes[key] = value;
110+
this.attributes[key] = this._truncateToSize(value);
109111
return this;
110112
}
111113

@@ -235,4 +237,49 @@ export class Span implements api.Span, ReadableSpan {
235237
}
236238
return this._ended;
237239
}
240+
241+
// Utility function to truncate given value within size
242+
// for value type of string, will truncate to given limit
243+
// for type of non-string, will return same value
244+
private _truncateToLimitUtil(value: string, limit: number): string {
245+
if (value.length <= limit) {
246+
return value;
247+
}
248+
return value.substr(0, limit);
249+
}
250+
251+
/**
252+
* If the given attribute value is of type string and has more characters than given {@code attributeValueLengthLimit} then
253+
* return string with trucated to {@code attributeValueLengthLimit} characters
254+
*
255+
* If the given attribute value is array of strings then
256+
* return new array of strings with each element truncated to {@code attributeValueLengthLimit} characters
257+
*
258+
* Otherwise return same Attribute {@code value}
259+
*
260+
* @param value Attribute value
261+
* @returns truncated attribute value if required, otherwise same value
262+
*/
263+
private _truncateToSize(value: SpanAttributeValue): SpanAttributeValue {
264+
const limit = this._attributeValueLengthLimit;
265+
// Check limit
266+
if (limit <= 0) {
267+
// Negative values are invalid, so do not truncate
268+
api.diag.warn(`Attribute value limit must be positive, got ${limit}`);
269+
return value;
270+
}
271+
272+
// String
273+
if (typeof value === 'string') {
274+
return this._truncateToLimitUtil(value, limit);
275+
}
276+
277+
// Array of strings
278+
if (Array.isArray(value)) {
279+
return (value as []).map(val => typeof val === 'string' ? this._truncateToLimitUtil(val, limit) : val);
280+
}
281+
282+
// Other types, no need to apply value length limit
283+
return value;
284+
}
238285
}

packages/opentelemetry-sdk-trace-base/src/config.ts

+1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ export const DEFAULT_CONFIG = {
3838
sampler: buildSamplerFromEnv(env),
3939
forceFlushTimeoutMillis: 30000,
4040
spanLimits: {
41+
attributeValueLengthLimit: getEnv().OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT,
4142
attributeCountLimit: getEnv().OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT,
4243
linkCountLimit: getEnv().OTEL_SPAN_LINK_COUNT_LIMIT,
4344
eventCountLimit: getEnv().OTEL_SPAN_EVENT_COUNT_LIMIT,

packages/opentelemetry-sdk-trace-base/src/types.ts

+2
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ export interface SDKRegistrationConfig {
6363

6464
/** Global configuration of trace service */
6565
export interface SpanLimits {
66+
/** attributeValueLengthLimit is maximum allowed attribute value size */
67+
attributeValueLengthLimit?: number;
6668
/** attributeCountLimit is number of attributes per span */
6769
attributeCountLimit?: number;
6870
/** linkCountLimit is number of links per span */

packages/opentelemetry-sdk-trace-base/test/common/BasicTracerProvider.test.ts

+77-54
Original file line numberDiff line numberDiff line change
@@ -64,74 +64,97 @@ describe('BasicTracerProvider', () => {
6464
});
6565

6666
describe('constructor', () => {
67-
it('should construct an instance without any options', () => {
68-
const provider = new BasicTracerProvider();
69-
assert.ok(provider instanceof BasicTracerProvider);
70-
});
67+
describe('when options not defined', () => {
68+
it('should construct an instance', () => {
69+
const tracer = new BasicTracerProvider();
70+
assert.ok(tracer instanceof BasicTracerProvider);
71+
});
7172

72-
it('should construct an instance with sampler', () => {
73-
const provider = new BasicTracerProvider({
74-
sampler: new AlwaysOnSampler(),
73+
it('should use noop span processor by default', () => {
74+
const tracer = new BasicTracerProvider();
75+
assert.ok(tracer.activeSpanProcessor instanceof NoopSpanProcessor);
7576
});
76-
assert.ok(provider instanceof BasicTracerProvider);
7777
});
7878

79-
it('should construct an instance with default span limits', () => {
80-
const tracer = new BasicTracerProvider({}).getTracer('default');
81-
assert.deepStrictEqual(tracer.getSpanLimits(), {
82-
attributeCountLimit: 128,
83-
eventCountLimit: 128,
84-
linkCountLimit: 128,
79+
describe('when "sampler" option defined', () => {
80+
it('should have an instance with sampler', () => {
81+
const tracer = new BasicTracerProvider({
82+
sampler: new AlwaysOnSampler(),
83+
});
84+
assert.ok(tracer instanceof BasicTracerProvider);
8585
});
8686
});
8787

88-
it('should construct an instance with customized attributeCountLimit span limits', () => {
89-
const tracer = new BasicTracerProvider({
90-
spanLimits: {
91-
attributeCountLimit: 100,
92-
},
93-
}).getTracer('default');
94-
assert.deepStrictEqual(tracer.getSpanLimits(), {
95-
attributeCountLimit: 100,
96-
eventCountLimit: 128,
97-
linkCountLimit: 128,
88+
describe('spanLimits', () => {
89+
describe('when not defined default values', () => {
90+
it('should have tracer with default values', () => {
91+
const tracer = new BasicTracerProvider({}).getTracer('default');
92+
assert.deepStrictEqual(tracer.getSpanLimits(), {
93+
attributeValueLengthLimit: Infinity,
94+
attributeCountLimit: 128,
95+
eventCountLimit: 128,
96+
linkCountLimit: 128,
97+
});
98+
});
9899
});
99-
});
100100

101-
it('should construct an instance with customized eventCountLimit span limits', () => {
102-
const tracer = new BasicTracerProvider({
103-
spanLimits: {
104-
eventCountLimit: 300,
105-
},
106-
}).getTracer('default');
107-
assert.deepStrictEqual(tracer.getSpanLimits(), {
108-
attributeCountLimit: 128,
109-
eventCountLimit: 300,
110-
linkCountLimit: 128,
101+
describe('when "attributeCountLimit" is defined', () => {
102+
it('should have tracer with defined value', () => {
103+
const tracer = new BasicTracerProvider({
104+
spanLimits: {
105+
attributeCountLimit: 100,
106+
},
107+
}).getTracer('default');
108+
const spanLimits = tracer.getSpanLimits();
109+
assert.strictEqual(spanLimits.attributeCountLimit, 100);
110+
});
111111
});
112-
});
113112

114-
it('should construct an instance with customized linkCountLimit span limits', () => {
115-
const tracer = new BasicTracerProvider({
116-
spanLimits: {
117-
linkCountLimit: 10,
118-
},
119-
}).getTracer('default');
120-
assert.deepStrictEqual(tracer.getSpanLimits(), {
121-
attributeCountLimit: 128,
122-
eventCountLimit: 128,
123-
linkCountLimit: 10,
113+
describe('when "attributeValueLengthLimit" is defined', () => {
114+
it('should have tracer with defined value', () => {
115+
const tracer = new BasicTracerProvider({
116+
spanLimits: {
117+
attributeValueLengthLimit: 10,
118+
},
119+
}).getTracer('default');
120+
const spanLimits = tracer.getSpanLimits();
121+
assert.strictEqual(spanLimits.attributeValueLengthLimit, 10);
122+
});
123+
124+
it('should have tracer with negative "attributeValueLengthLimit" value', () => {
125+
const tracer = new BasicTracerProvider({
126+
spanLimits: {
127+
attributeValueLengthLimit: -10,
128+
},
129+
}).getTracer('default');
130+
const spanLimits = tracer.getSpanLimits();
131+
assert.strictEqual(spanLimits.attributeValueLengthLimit, -10);
132+
});
124133
});
125-
});
126134

127-
it('should construct an instance of BasicTracerProvider', () => {
128-
const tracer = new BasicTracerProvider();
129-
assert.ok(tracer instanceof BasicTracerProvider);
130-
});
135+
describe('when "eventCountLimit" is defined', () => {
136+
it('should have tracer with defined value', () => {
137+
const tracer = new BasicTracerProvider({
138+
spanLimits: {
139+
eventCountLimit: 300,
140+
},
141+
}).getTracer('default');
142+
const spanLimits = tracer.getSpanLimits();
143+
assert.strictEqual(spanLimits.eventCountLimit, 300);
144+
});
145+
});
131146

132-
it('should use noop span processor by default', () => {
133-
const tracer = new BasicTracerProvider();
134-
assert.ok(tracer.activeSpanProcessor instanceof NoopSpanProcessor);
147+
describe('when "linkCountLimit" is defined', () => {
148+
it('should have tracer with defined value', () => {
149+
const tracer = new BasicTracerProvider({
150+
spanLimits: {
151+
linkCountLimit: 10,
152+
},
153+
}).getTracer('default');
154+
const spanLimits = tracer.getSpanLimits();
155+
assert.strictEqual(spanLimits.linkCountLimit, 10);
156+
});
157+
});
135158
});
136159
});
137160

0 commit comments

Comments
 (0)