diff --git a/packages/core/src/tracing/idleSpan.ts b/packages/core/src/tracing/idleSpan.ts index 0d4e2aa65eb9..18d056b6b64f 100644 --- a/packages/core/src/tracing/idleSpan.ts +++ b/packages/core/src/tracing/idleSpan.ts @@ -240,7 +240,7 @@ export function startIdleSpan(startSpanOptions: StartSpanOptions, options: Parti */ function _restartChildSpanTimeout(endTimestamp?: number): void { _cancelChildSpanTimeout(); - _idleTimeoutID = setTimeout(() => { + _childSpanTimeoutID = setTimeout(() => { if (!_finished && _autoFinishAllowed) { _finishReason = FINISH_REASON_HEARTBEAT_FAILED; span.end(endTimestamp); diff --git a/packages/core/test/lib/tracing/idleSpan.test.ts b/packages/core/test/lib/tracing/idleSpan.test.ts index 4dbe2236f42d..ea5a8b7b8158 100644 --- a/packages/core/test/lib/tracing/idleSpan.test.ts +++ b/packages/core/test/lib/tracing/idleSpan.test.ts @@ -806,6 +806,29 @@ describe('startIdleSpan', () => { expect(spanToJSON(idleSpan).end_timestamp).toBeDefined(); }); + it('measures the idle timeout from the last child end, not from the auto-finish signal', () => { + const idleSpan = startIdleSpan({ name: 'idle span' }, { disableAutoFinish: true, finalTimeout: 99_999 }); + const idleSpanId = idleSpan.spanContext().spanId; + + const child = startInactiveSpan({ name: 'inner' }); + + vi.advanceTimersByTime(500); + getClient()!.emit('idleSpanEnableAutoFinish', idleSpan); + + vi.advanceTimersByTime(700); + child!.end(); + + vi.advanceTimersByTime(TRACING_DEFAULTS.idleTimeout - 199); + expect(spanToJSON(idleSpan).end_timestamp).toBeUndefined(); + + const lateChild = startInactiveSpan({ name: 'late' }); + expect(spanToJSON(lateChild!).parent_span_id).toBe(idleSpanId); + + lateChild!.end(); + vi.advanceTimersByTime(TRACING_DEFAULTS.idleTimeout); + expect(spanToJSON(idleSpan).end_timestamp).toBeDefined(); + }); + it('times out at final timeout if disableAutoFinish=true', () => { const idleSpan = startIdleSpan({ name: 'idle span' }, { disableAutoFinish: true }); expect(idleSpan).toBeDefined();