Skip to content

Commit ab75c4b

Browse files
ArtAhmetajsatazor
authored andcommitted
fix(sdk-trace): process onStart called with a span having empty attributes
1 parent f5ef8de commit ab75c4b

File tree

4 files changed

+105
-8
lines changed

4 files changed

+105
-8
lines changed

packages/opentelemetry-exporter-zipkin/test/common/transform.test.ts

+34
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,40 @@ describe('transform', () => {
236236
version: '1',
237237
});
238238
});
239+
it('should map OpenTelemetry constructor attributes to a Zipkin tag', () => {
240+
const span = new Span(
241+
tracer,
242+
api.ROOT_CONTEXT,
243+
'my-span',
244+
spanContext,
245+
api.SpanKind.SERVER,
246+
parentId,
247+
[],
248+
undefined,
249+
undefined,
250+
{
251+
key1: 'value1',
252+
key2: 'value2',
253+
}
254+
);
255+
const tags: zipkinTypes.Tags = _toZipkinTags(
256+
span,
257+
defaultStatusCodeTagName,
258+
defaultStatusErrorTagName
259+
);
260+
261+
assert.deepStrictEqual(tags, {
262+
key1: 'value1',
263+
key2: 'value2',
264+
[SemanticResourceAttributes.SERVICE_NAME]: 'zipkin-test',
265+
'telemetry.sdk.language': language,
266+
'telemetry.sdk.name': 'opentelemetry',
267+
'telemetry.sdk.version': VERSION,
268+
cost: '112.12',
269+
service: 'ui',
270+
version: '1',
271+
});
272+
});
239273
it('should map OpenTelemetry SpanStatus.code to a Zipkin tag', () => {
240274
const span = new Span(
241275
tracer,

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

+7-1
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,8 @@ export class Span implements APISpan, ReadableSpan {
100100
parentSpanId?: string,
101101
links: Link[] = [],
102102
startTime?: TimeInput,
103-
_deprecatedClock?: unknown // keeping this argument even though it is unused to ensure backwards compatibility
103+
_deprecatedClock?: unknown, // keeping this argument even though it is unused to ensure backwards compatibility
104+
initAttributes?: SpanAttributes
104105
) {
105106
this.name = spanName;
106107
this._spanContext = spanContext;
@@ -119,6 +120,11 @@ export class Span implements APISpan, ReadableSpan {
119120
this.resource = parentTracer.resource;
120121
this.instrumentationLibrary = parentTracer.instrumentationLibrary;
121122
this._spanLimits = parentTracer.getSpanLimits();
123+
124+
if (initAttributes != null) {
125+
this.setAttributes(initAttributes);
126+
}
127+
122128
this._spanProcessor = parentTracer.getActiveSpanProcessor();
123129
this._spanProcessor.onStart(this, context);
124130
this._attributeValueLengthLimit =

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

+9-7
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,12 @@ export class Tracer implements api.Tracer {
132132
return nonRecordingSpan;
133133
}
134134

135+
// Set initial span attributes. The attributes object may have been mutated
136+
// by the sampler, so we sanitize the merged attributes before setting them.
137+
const initAttributes = sanitizeAttributes(
138+
Object.assign(attributes, samplingResult.attributes)
139+
);
140+
135141
const span = new Span(
136142
this,
137143
context,
@@ -140,14 +146,10 @@ export class Tracer implements api.Tracer {
140146
spanKind,
141147
parentSpanId,
142148
links,
143-
options.startTime
144-
);
145-
// Set initial span attributes. The attributes object may have been mutated
146-
// by the sampler, so we sanitize the merged attributes before setting them.
147-
const initAttributes = sanitizeAttributes(
148-
Object.assign(attributes, samplingResult.attributes)
149+
options.startTime,
150+
undefined,
151+
initAttributes
149152
);
150-
span.setAttributes(initAttributes);
151153
return span;
152154
}
153155

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

+55
Original file line numberDiff line numberDiff line change
@@ -1054,6 +1054,25 @@ describe('Span', () => {
10541054
assert.ok(started);
10551055
});
10561056

1057+
it('should call onStart synchronously when span is started with the initial attributes', () => {
1058+
let attributes;
1059+
const processor: SpanProcessor = {
1060+
onStart: (span) => {
1061+
attributes = { ...span.attributes };
1062+
},
1063+
forceFlush: () => Promise.resolve(),
1064+
onEnd() {},
1065+
shutdown: () => Promise.resolve(),
1066+
};
1067+
1068+
const provider = new BasicTracerProvider();
1069+
1070+
provider.addSpanProcessor(processor);
1071+
1072+
provider.getTracer('default').startSpan('test', { attributes: { foo: 'bar' }});
1073+
assert.deepStrictEqual(attributes, { foo: 'bar' });
1074+
});
1075+
10571076
it('should call onEnd synchronously when span is ended', () => {
10581077
let ended = false;
10591078
const processor: SpanProcessor = {
@@ -1222,5 +1241,41 @@ describe('Span', () => {
12221241
});
12231242
});
12241243
});
1244+
1245+
describe('when initial attributes are specified', () => {
1246+
it('should not store attributes if none are passed', () => {
1247+
const span = new Span(
1248+
tracer,
1249+
ROOT_CONTEXT,
1250+
name,
1251+
spanContext,
1252+
SpanKind.CLIENT
1253+
);
1254+
assert.deepStrictEqual(span.attributes, {});
1255+
});
1256+
1257+
it('should store the passed attributes', () => {
1258+
const span = new Span(
1259+
tracer,
1260+
ROOT_CONTEXT,
1261+
name,
1262+
spanContext,
1263+
SpanKind.CLIENT,
1264+
"",
1265+
[],
1266+
undefined,
1267+
undefined,
1268+
{
1269+
key1: 'value1',
1270+
key2: 'value2',
1271+
}
1272+
);
1273+
1274+
assert.deepStrictEqual(span.attributes, {
1275+
key1: 'value1',
1276+
key2: 'value2',
1277+
});
1278+
});
1279+
});
12251280
});
12261281
});

0 commit comments

Comments
 (0)