Skip to content

Commit c8686ff

Browse files
authored
fix(core): Only start spans in trace if tracing is enabled (#8357)
In our (still internal) `trace` function, we don't yet check if `hasTracingEnabled` returns `true` before trying to start a span or a transaction. This leads to the following problematic UX path that was discovered [here](#5838 (reply in thread)): 1. Users don't set `tracesSampleRate` (or `tracesSampler`), causing some SDKs (NextJS, SvelteKit) to **not** add the `BrowserTracing` integration, which in turn means that tracing extension methods are not added/registered. 2. Users or, more likely, other parts of our SDK (e.g. SvelteKit's wrappers/handlers) call `trace` which will still try to start a span/txn. 3. Users will get a console warning that tracing extension methods were not installed and they should manually call `addExtensionMethods` which is completely misleading in this case. This fix makes `trace` check for `hasTracingEnabled()` in which case it will not start a span but just invoke the error callback if an error occurred.
1 parent 9d8464b commit c8686ff

File tree

2 files changed

+41
-3
lines changed

2 files changed

+41
-3
lines changed

Diff for: packages/core/src/tracing/trace.ts

+13-3
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@ import type { TransactionContext } from '@sentry/types';
22
import { isThenable } from '@sentry/utils';
33

44
import { getCurrentHub } from '../hub';
5+
import { hasTracingEnabled } from '../utils/hasTracingEnabled';
56
import type { Span } from './span';
67

78
/**
89
* Wraps a function with a transaction/span and finishes the span after the function is done.
910
*
10-
* Note that if you have not enabled tracing extensions via `addTracingExtensions`, this function
11-
* will not generate spans, and the `span` returned from the callback may be undefined.
11+
* Note that if you have not enabled tracing extensions via `addTracingExtensions`
12+
* or you didn't set `tracesSampleRate`, this function will not generate spans
13+
* and the `span` returned from the callback will be undefined.
1214
*
1315
* This function is meant to be used internally and may break at any time. Use at your own risk.
1416
*
@@ -31,7 +33,15 @@ export function trace<T>(
3133
const scope = hub.getScope();
3234

3335
const parentSpan = scope.getSpan();
34-
const activeSpan = parentSpan ? parentSpan.startChild(ctx) : hub.startTransaction(ctx);
36+
37+
function getActiveSpan(): Span | undefined {
38+
if (!hasTracingEnabled()) {
39+
return undefined;
40+
}
41+
return parentSpan ? parentSpan.startChild(ctx) : hub.startTransaction(ctx);
42+
}
43+
44+
const activeSpan = getActiveSpan();
3545
scope.setSpan(activeSpan);
3646

3747
function finishAndSetSpan(): void {

Diff for: packages/core/test/lib/tracing/trace.test.ts

+28
Original file line numberDiff line numberDiff line change
@@ -185,5 +185,33 @@ describe('trace', () => {
185185
}
186186
expect(onError).toHaveBeenCalledTimes(isError ? 1 : 0);
187187
});
188+
189+
it("doesn't create spans but calls onError if tracing is disabled", async () => {
190+
const options = getDefaultTestClientOptions({
191+
/* we don't set tracesSampleRate or tracesSampler */
192+
});
193+
client = new TestClient(options);
194+
hub = new Hub(client);
195+
makeMain(hub);
196+
197+
const startTxnSpy = jest.spyOn(hub, 'startTransaction');
198+
199+
const onError = jest.fn();
200+
try {
201+
await trace(
202+
{ name: 'GET users/[id]' },
203+
() => {
204+
return callback();
205+
},
206+
onError,
207+
);
208+
} catch (e) {
209+
expect(onError).toHaveBeenCalledTimes(1);
210+
expect(onError).toHaveBeenCalledWith(e);
211+
}
212+
expect(onError).toHaveBeenCalledTimes(isError ? 1 : 0);
213+
214+
expect(startTxnSpy).not.toHaveBeenCalled();
215+
});
188216
});
189217
});

0 commit comments

Comments
 (0)