diff --git a/packages/opentelemetry-sdk-trace-base/src/Span.ts b/packages/opentelemetry-sdk-trace-base/src/Span.ts index 2d267bbb09..ea3526b29b 100644 --- a/packages/opentelemetry-sdk-trace-base/src/Span.ts +++ b/packages/opentelemetry-sdk-trace-base/src/Span.ts @@ -284,7 +284,7 @@ export class Span implements APISpan, ReadableSpan { attributesOrStartTime = undefined; } - const attributes = sanitizeAttributes(attributesOrStartTime); + const attributes: SpanAttributes = {}; if (typeof exception === 'string') { attributes[SemanticAttributes.EXCEPTION_MESSAGE] = exception; } else if (exception) { @@ -301,6 +301,9 @@ export class Span implements APISpan, ReadableSpan { attributes[SemanticAttributes.EXCEPTION_STACKTRACE] = exception.stack; } } + if (attributesOrStartTime) { + Object.assign(attributes, sanitizeAttributes(attributesOrStartTime)); + } // these are minimum requirements from spec if ( diff --git a/packages/opentelemetry-sdk-trace-base/test/common/Span.test.ts b/packages/opentelemetry-sdk-trace-base/test/common/Span.test.ts index 0567af8ba5..873865a0dc 100644 --- a/packages/opentelemetry-sdk-trace-base/test/common/Span.test.ts +++ b/packages/opentelemetry-sdk-trace-base/test/common/Span.test.ts @@ -1233,16 +1233,47 @@ describe('Span', () => { // @ts-expect-error writing readonly property. performance time origin is mocked to return ms value of [1,1] span['_performanceOffset'] = 0; assert.strictEqual(span.events.length, 0); - span.recordException('boom', { + const exception = { code: 'Error', message: 'boom', stack: 'bar' }; + span.recordException(exception, { ...validAttributes, ...invalidAttributes, } as unknown as SpanAttributes); const event = span.events[0]; assert.deepStrictEqual(event.attributes, { + [SemanticAttributes.EXCEPTION_TYPE]: 'Error', [SemanticAttributes.EXCEPTION_MESSAGE]: 'boom', + [SemanticAttributes.EXCEPTION_STACKTRACE]: 'bar', ...validAttributes, }); }); + + it('should prioritize the provided attributes over generated', () => { + const span = new Span( + tracer, + ROOT_CONTEXT, + name, + spanContext, + SpanKind.CLIENT + ); + // @ts-expect-error writing readonly property. performance time origin is mocked to return ms value of [1,1] + span['_performanceOffset'] = 0; + assert.strictEqual(span.events.length, 0); + const exception = { code: 'Error', message: 'boom', stack: 'bar' }; + span.recordException(exception, { + [SemanticAttributes.EXCEPTION_TYPE]: 'OverrideError', + [SemanticAttributes.EXCEPTION_MESSAGE]: 'override-boom', + [SemanticAttributes.EXCEPTION_STACKTRACE]: 'override-bar', + ...validAttributes, + ...invalidAttributes, + } as unknown as SpanAttributes); + const event = span.events[0]; + assert.deepStrictEqual(event.attributes, { + ...validAttributes, + [SemanticAttributes.EXCEPTION_TYPE]: 'OverrideError', + [SemanticAttributes.EXCEPTION_MESSAGE]: 'override-boom', + [SemanticAttributes.EXCEPTION_STACKTRACE]: 'override-bar', + }); + }); }); describe('when exception code is numeric', () => {