Skip to content

Commit a1f4a3e

Browse files
author
Luca Forstner
authored
feat(core): Add withActiveSpan (#10195)
1 parent 2974df6 commit a1f4a3e

File tree

2 files changed

+68
-1
lines changed

2 files changed

+68
-1
lines changed

packages/core/src/exports.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import type {
1717
SessionContext,
1818
Severity,
1919
SeverityLevel,
20+
Span,
2021
TransactionContext,
2122
User,
2223
} from '@sentry/types';
@@ -229,6 +230,21 @@ export function withIsolationScope<T>(callback: (isolationScope: Scope) => T): T
229230
});
230231
}
231232

233+
/**
234+
* Forks the current scope and sets the provided span as active span in the context of the provided callback.
235+
*
236+
* @param span Spans started in the context of the provided callback will be children of this span.
237+
* @param callback Execution context in which the provided span will be active. Is passed the newly forked scope.
238+
* @returns the value returned from the provided callback function.
239+
*/
240+
export function withActiveSpan<T>(span: Span, callback: (scope: Scope) => T): T {
241+
return withScope(scope => {
242+
// eslint-disable-next-line deprecation/deprecation
243+
scope.setSpan(span);
244+
return callback(scope);
245+
});
246+
}
247+
232248
/**
233249
* Starts a new `Transaction` and returns it. This is the entry point to manual tracing instrumentation.
234250
*

packages/core/test/lib/scope.test.ts

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,20 @@
11
import type { Attachment, Breadcrumb, Client, Event } from '@sentry/types';
2-
import { applyScopeDataToEvent, getCurrentScope, getIsolationScope, withIsolationScope } from '../../src';
2+
import {
3+
Hub,
4+
addTracingExtensions,
5+
applyScopeDataToEvent,
6+
getActiveSpan,
7+
getCurrentScope,
8+
getIsolationScope,
9+
makeMain,
10+
startInactiveSpan,
11+
startSpan,
12+
withIsolationScope,
13+
} from '../../src';
14+
15+
import { withActiveSpan } from '../../src/exports';
316
import { Scope, getGlobalScope, setGlobalScope } from '../../src/scope';
17+
import { TestClient, getDefaultTestClientOptions } from '../mocks/client';
418

519
describe('Scope', () => {
620
beforeEach(() => {
@@ -503,3 +517,40 @@ describe('isolation scope', () => {
503517
});
504518
});
505519
});
520+
521+
describe('withActiveSpan()', () => {
522+
beforeAll(() => {
523+
addTracingExtensions();
524+
});
525+
526+
beforeEach(() => {
527+
const options = getDefaultTestClientOptions({ enableTracing: true });
528+
const client = new TestClient(options);
529+
const scope = new Scope();
530+
const hub = new Hub(client, scope);
531+
makeMain(hub);
532+
});
533+
534+
it('should set the active span within the callback', () => {
535+
expect.assertions(2);
536+
const inactiveSpan = startInactiveSpan({ name: 'inactive-span' });
537+
538+
expect(getActiveSpan()).not.toBe(inactiveSpan);
539+
540+
withActiveSpan(inactiveSpan!, () => {
541+
expect(getActiveSpan()).toBe(inactiveSpan);
542+
});
543+
});
544+
545+
it('should create child spans when calling startSpan within the callback', done => {
546+
expect.assertions(1);
547+
const inactiveSpan = startInactiveSpan({ name: 'inactive-span' });
548+
549+
withActiveSpan(inactiveSpan!, () => {
550+
startSpan({ name: 'child-span' }, childSpan => {
551+
expect(childSpan?.parentSpanId).toBe(inactiveSpan?.spanContext().spanId);
552+
done();
553+
});
554+
});
555+
});
556+
});

0 commit comments

Comments
 (0)