Skip to content

Commit c970772

Browse files
authored
feat: Allow passing null to withActiveSpan (#10717)
1 parent 7362857 commit c970772

File tree

4 files changed

+63
-8
lines changed

4 files changed

+63
-8
lines changed

packages/core/src/exports.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -127,16 +127,18 @@ export function setUser(user: User | null): ReturnType<Hub['setUser']> {
127127
}
128128

129129
/**
130-
* Forks the current scope and sets the provided span as active span in the context of the provided callback.
130+
* Forks the current scope and sets the provided span as active span in the context of the provided callback. Can be
131+
* passed `null` to start an entirely new span tree.
131132
*
132-
* @param span Spans started in the context of the provided callback will be children of this span.
133+
* @param span Spans started in the context of the provided callback will be children of this span. If `null` is passed,
134+
* spans started within the callback will not be attached to a parent span.
133135
* @param callback Execution context in which the provided span will be active. Is passed the newly forked scope.
134136
* @returns the value returned from the provided callback function.
135137
*/
136-
export function withActiveSpan<T>(span: Span, callback: (scope: ScopeInterface) => T): T {
138+
export function withActiveSpan<T>(span: Span | null, callback: (scope: ScopeInterface) => T): T {
137139
return withScope(scope => {
138140
// eslint-disable-next-line deprecation/deprecation
139-
scope.setSpan(span);
141+
scope.setSpan(span || undefined);
140142
return callback(scope);
141143
});
142144
}

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -582,4 +582,13 @@ describe('withActiveSpan()', () => {
582582
});
583583
});
584584
});
585+
586+
it('when `null` is passed, no span should be active within the callback', () => {
587+
expect.assertions(1);
588+
startSpan({ name: 'parent-span' }, () => {
589+
withActiveSpan(null, () => {
590+
expect(getActiveSpan()).toBeUndefined();
591+
});
592+
});
593+
});
585594
});

packages/node-experimental/test/sdk/api.test.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,46 @@ describe('withActiveSpan()', () => {
5353
expect.anything(),
5454
);
5555
});
56+
57+
it('when `null` is passed, no span should be active within the callback', () => {
58+
expect.assertions(1);
59+
startSpan({ name: 'parent-span' }, () => {
60+
withActiveSpan(null, () => {
61+
expect(getActiveSpan()).toBeUndefined();
62+
});
63+
});
64+
});
65+
66+
it('when `null` is passed, should start a new trace for new spans', async () => {
67+
const beforeSendTransaction = jest.fn(() => null);
68+
mockSdkInit({ enableTracing: true, beforeSendTransaction });
69+
const client = getClient();
70+
71+
startSpan({ name: 'parent-span' }, () => {
72+
withActiveSpan(null, () => {
73+
startSpan({ name: 'child-span' }, () => {});
74+
});
75+
});
76+
77+
await client.flush();
78+
79+
expect(beforeSendTransaction).toHaveBeenCalledTimes(2);
80+
81+
// The child span should be a child of the inactive span
82+
expect(beforeSendTransaction).toHaveBeenCalledWith(
83+
expect.objectContaining({
84+
transaction: 'parent-span',
85+
spans: expect.not.arrayContaining([expect.objectContaining({ description: 'child-span' })]),
86+
}),
87+
expect.anything(),
88+
);
89+
90+
// The floating span should be a separate transaction
91+
expect(beforeSendTransaction).toHaveBeenCalledWith(
92+
expect.objectContaining({
93+
transaction: 'child-span',
94+
}),
95+
expect.anything(),
96+
);
97+
});
5698
});

packages/opentelemetry/src/trace.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,14 +102,16 @@ export function startInactiveSpan(spanContext: OpenTelemetrySpanContext): Span {
102102
}
103103

104104
/**
105-
* Forks the current scope and sets the provided span as active span in the context of the provided callback.
105+
* Forks the current scope and sets the provided span as active span in the context of the provided callback. Can be
106+
* passed `null` to start an entirely new span tree.
106107
*
107-
* @param span Spans started in the context of the provided callback will be children of this span.
108+
* @param span Spans started in the context of the provided callback will be children of this span. If `null` is passed,
109+
* spans started within the callback will not be attached to a parent span.
108110
* @param callback Execution context in which the provided span will be active. Is passed the newly forked scope.
109111
* @returns the value returned from the provided callback function.
110112
*/
111-
export function withActiveSpan<T>(span: Span, callback: (scope: Scope) => T): T {
112-
const newContextWithActiveSpan = trace.setSpan(context.active(), span);
113+
export function withActiveSpan<T>(span: Span | null, callback: (scope: Scope) => T): T {
114+
const newContextWithActiveSpan = span ? trace.setSpan(context.active(), span) : trace.deleteSpan(context.active());
113115
return context.with(newContextWithActiveSpan, () => callback(getCurrentScope()));
114116
}
115117

0 commit comments

Comments
 (0)