diff --git a/docs/migration/v11-end-state.md b/docs/migration/v11-end-state.md index 7c93add6619a..50120422c5f9 100644 --- a/docs/migration/v11-end-state.md +++ b/docs/migration/v11-end-state.md @@ -1093,7 +1093,9 @@ The same applies when looking the integration up by name, e.g. via `client.getIn `SentryError`, and `User`. You may need to narrow types explicitly where you previously relied on `any`. - Attribute typing and serialization were unified across the SDK. -- The `SentrySpanArguments` interface and related dead code in `SentrySpan` were cleaned up. +- The `endTimestamp` property was removed from the `SentrySpanArguments` interface. It was never part of + `StartSpanOptions`, so it could only be passed by ignoring TypeScript, in which case the span ended itself + during construction. Call `span.end(timestamp)` instead. - `BrowserOptions` now supports the `TransportOptions` generic. - (Cloudflare) The `env` types and the generics on `withSentry` and `instrumentDurableObjectWithSentry` were reworked for better type safety. If you were not passing explicit generic type parameters, no changes are needed. diff --git a/packages/core/src/tracing/sentrySpan.ts b/packages/core/src/tracing/sentrySpan.ts index b9eb44216fa5..09f7fdd540dc 100644 --- a/packages/core/src/tracing/sentrySpan.ts +++ b/packages/core/src/tracing/sentrySpan.ts @@ -121,18 +121,10 @@ export class SentrySpan implements Span { if ('sampled' in spanContext) { this._sampled = spanContext.sampled; } - if (spanContext.endTimestamp) { - this._endTime = spanContext.endTimestamp; - } this._events = []; this._isStandaloneSpan = spanContext.isStandalone; - - // If the span is already ended, ensure we finalize the span immediately - if (this._endTime) { - this._onSpanEnded(); - } } /** @inheritDoc */ @@ -257,12 +249,7 @@ export class SentrySpan implements Span { /** @inheritdoc */ public end(endTimestamp?: SpanTimeInput): void { - // If already ended, skip the end-of-span processing, but still seal a tracer-provider span. The - // seal at the bottom of this method is skipped on this early return, and `_endTime` may have been - // set before this first `end()` call (e.g. via the constructor's `endTimestamp`), which would - // otherwise leave the span mutable after `end()`. End-of-span processing already ran in that case. if (this._endTime) { - this._frozen = spanIsTracerProviderSpan(this); return; } diff --git a/packages/core/src/tracing/trace.ts b/packages/core/src/tracing/trace.ts index f10583f301b7..7558b3a7a42b 100644 --- a/packages/core/src/tracing/trace.ts +++ b/packages/core/src/tracing/trace.ts @@ -608,11 +608,6 @@ function _startChildSpan( } client.emit('spanStart', childSpan); - // If it has an endTimestamp, it's already ended - if (spanArguments.endTimestamp) { - client.emit('spanEnd', childSpan); - client.emit('afterSpanEnd', childSpan); - } return childSpan; } diff --git a/packages/core/src/types/span.ts b/packages/core/src/types/span.ts index 020953009832..b33c377cb6fc 100644 --- a/packages/core/src/types/span.ts +++ b/packages/core/src/types/span.ts @@ -179,7 +179,6 @@ export interface SpanContextData { /** * Interface holding all properties that can be set on a Span on creation. - * This is only used for the legacy span/transaction creation and will go away in v8. */ export interface SentrySpanArguments { /** @@ -222,11 +221,6 @@ export interface SentrySpanArguments { */ startTimestamp?: number | undefined; - /** - * Timestamp in seconds (epoch time) indicating when the span ended. - */ - endTimestamp?: number | undefined; - /** * Links to associate with the new span. Setting links here is preferred over addLink() * as certain context information is only available during span creation. diff --git a/packages/core/test/lib/tracing/sentrySpan.test.ts b/packages/core/test/lib/tracing/sentrySpan.test.ts index 40f7a1344f9c..27b1a3447048 100644 --- a/packages/core/test/lib/tracing/sentrySpan.test.ts +++ b/packages/core/test/lib/tracing/sentrySpan.test.ts @@ -197,21 +197,20 @@ describe('SentrySpan', () => { expect(json.links).toHaveLength(1); }); - it('seals a tracer-provider span that ended via the constructor endTimestamp', () => { - // `_endTime` is set in the constructor, so `end()` early-returns before reaching the seal at the - // bottom of its body. The span must still be sealed once `end()` is invoked. + it('keeps a tracer-provider span sealed across repeated `end()` calls', () => { const span = new SentrySpan({ name: 'original', startTimestamp: 1, - endTimestamp: 2, attributes: { key: 'before' }, }); markSpanAsTracerProviderSpan(span); - span.end(); + span.end(2); + span.end(3); span.setAttribute('key', 'after'); expect(spanToJSON(span).data?.['key']).toBe('before'); + expect(spanToJSON(span).timestamp).toBe(2); }); }); @@ -255,20 +254,18 @@ describe('SentrySpan', () => { name: 'not-sampled', isStandalone: true, startTimestamp: 1, - endTimestamp: 2, sampled: false, }); - notSampledSpan.end(); + notSampledSpan.end(2); expect(mockSend).not.toHaveBeenCalled(); const sampledSpan = new SentrySpan({ name: 'is-sampled', isStandalone: true, startTimestamp: 1, - endTimestamp: 2, sampled: true, }); - sampledSpan.end(); + sampledSpan.end(2); expect(mockSend).toHaveBeenCalledTimes(1); }); @@ -289,10 +286,9 @@ describe('SentrySpan', () => { name: 'test', isStandalone: true, startTimestamp: 1, - endTimestamp: 2, sampled: true, }); - span.end(); + span.end(2); expect(mockSend).toHaveBeenCalled(); }); @@ -321,10 +317,9 @@ describe('SentrySpan', () => { name: 'test', isStandalone: true, startTimestamp: 1, - endTimestamp: 2, sampled: true, }); - span.end(); + span.end(2); expect(beforeSendSpan).toHaveBeenCalledTimes(1); expect(mockSend).toHaveBeenCalled(); @@ -359,10 +354,9 @@ describe('SentrySpan', () => { name: 'test', isStandalone: true, startTimestamp: 1, - endTimestamp: 2, sampled: true, }); - span.end(); + span.end(2); expect(seen[0]!['my.scope.attr']).toBeUndefined(); }); @@ -621,8 +615,9 @@ describe('SentrySpan', () => { it('skips if span is already ended', () => { const startTimestamp = timestampInSeconds() - 5; const endTimestamp = timestampInSeconds() - 1; - const span = new SentrySpan({ startTimestamp, endTimestamp }); + const span = new SentrySpan({ startTimestamp }); + span.end(endTimestamp); span.end(); expect(spanToJSON(span).timestamp).toBe(endTimestamp); @@ -636,7 +631,8 @@ describe('SentrySpan', () => { }); it('returns false for sampled, finished span', () => { - const span = new SentrySpan({ sampled: true, endTimestamp: Date.now() }); + const span = new SentrySpan({ sampled: true }); + span.end(); expect(span.isRecording()).toEqual(false); }); diff --git a/packages/core/test/lib/utils/spanUtils.test.ts b/packages/core/test/lib/utils/spanUtils.test.ts index 965fafcb5e4f..6ce870b1ee1f 100644 --- a/packages/core/test/lib/utils/spanUtils.test.ts +++ b/packages/core/test/lib/utils/spanUtils.test.ts @@ -341,12 +341,12 @@ describe('spanToJSON', () => { spanId: '5678', traceId: 'abcd', startTimestamp: 123, - endTimestamp: 456, attributes: { [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto', }, }); span.setStatus({ code: SPAN_STATUS_OK }); + span.end(456); expect(spanToJSON(span)).toEqual({ description: 'test name', @@ -449,7 +449,6 @@ describe('spanToJSON', () => { spanId: '5678', traceId: 'abcd', startTimestamp: 123, - endTimestamp: 456, attributes: { [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto', attr1: 'value1', @@ -471,6 +470,7 @@ describe('spanToJSON', () => { }); span.setStatus({ code: SPAN_STATUS_OK }); span.setAttribute('attr4', [1, 2, 3]); + span.end(456); expect(spanToStreamedSpanJSON(span)).toEqual({ name: 'test name',