Skip to content

Commit 644d697

Browse files
authored
feat(core): Add span.isRecording() instead of span.sampled (#10034)
To align this with OTEL API.
1 parent 89ca41d commit 644d697

File tree

12 files changed

+54
-11
lines changed

12 files changed

+54
-11
lines changed

MIGRATION.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ In v8, the Span class is heavily reworked. The following properties & methods ar
5757
* `span.setName(newName)`: Use `span.updateName(newName)` instead.
5858
* `span.toTraceparent()`: use `spanToTraceHeader(span)` util instead.
5959
* `span.getTraceContext()`: Use `spanToTraceContext(span)` utility function instead.
60+
* `span.sampled`: Use `span.isRecording()` instead.
6061

6162
## Deprecate `pushScope` & `popScope` in favor of `withScope`
6263

packages/astro/test/server/meta.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { vi } from 'vitest';
44
import { getTracingMetaTags, isValidBaggageString } from '../../src/server/meta';
55

66
const mockedSpan = {
7-
sampled: true,
7+
isRecording: () => true,
88
traceId: '12345678901234567890123456789012',
99
spanId: '1234567890123456',
1010
transaction: {
@@ -70,7 +70,7 @@ describe('getTracingMetaTags', () => {
7070
const tags = getTracingMetaTags(
7171
// @ts-expect-error - only passing a partial span object
7272
{
73-
sampled: true,
73+
isRecording: () => true,
7474
traceId: '12345678901234567890123456789012',
7575
spanId: '1234567890123456',
7676
transaction: undefined,
@@ -93,7 +93,7 @@ describe('getTracingMetaTags', () => {
9393
const tags = getTracingMetaTags(
9494
// @ts-expect-error - only passing a partial span object
9595
{
96-
sampled: true,
96+
isRecording: () => true,
9797
traceId: '12345678901234567890123456789012',
9898
spanId: '1234567890123456',
9999
transaction: undefined,

packages/browser/src/profiling/utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -515,7 +515,7 @@ export function shouldProfileTransaction(transaction: Transaction): boolean {
515515
return false;
516516
}
517517

518-
if (!transaction.sampled) {
518+
if (!transaction.isRecording()) {
519519
if (DEBUG_BUILD) {
520520
logger.log('[Profiling] Discarding profile because transaction was not sampled.');
521521
}

packages/browser/test/unit/profiling/hubextensions.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ describe('BrowserProfilingIntegration', () => {
6767
// @ts-expect-error force api to be undefined
6868
global.window.Profiler = undefined;
6969
// set sampled to true so that profiling does not early return
70-
const mockTransaction = { sampled: true } as Transaction;
70+
const mockTransaction = { isRecording: () => true } as Transaction;
7171
expect(() => onProfilingStartRouteTransaction(mockTransaction)).not.toThrow();
7272
});
7373
it('does not throw if constructor throws', () => {
@@ -80,8 +80,8 @@ describe('BrowserProfilingIntegration', () => {
8080
}
8181
}
8282

83-
// set sampled to true so that profiling does not early return
84-
const mockTransaction = { sampled: true } as Transaction;
83+
// set isRecording to true so that profiling does not early return
84+
const mockTransaction = { isRecording: () => true } as Transaction;
8585

8686
// @ts-expect-error override with our own constructor
8787
global.window.Profiler = Profiler;

packages/bun/test/integrations/bunserver.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ describe('Bun Serve Integration', () => {
8080
client.on('finishTransaction', transaction => {
8181
expect(transaction.traceId).toBe(TRACE_ID);
8282
expect(transaction.parentSpanId).toBe(PARENT_SPAN_ID);
83-
expect(transaction.sampled).toBe(true);
83+
expect(transaction.isRecording()).toBe(true);
8484

8585
expect(transaction.metadata?.dynamicSamplingContext).toStrictEqual({ version: '1.0', environment: 'production' });
8686
});

packages/core/src/tracing/hubextensions.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ function _startTransaction(
5555
The transaction will not be sampled. Please use the ${configInstrumenter} instrumentation to start transactions.`,
5656
);
5757

58+
// eslint-disable-next-line deprecation/deprecation
5859
transactionContext.sampled = false;
5960
}
6061

@@ -64,7 +65,7 @@ The transaction will not be sampled. Please use the ${configInstrumenter} instru
6465
transactionContext,
6566
...customSamplingContext,
6667
});
67-
if (transaction.sampled) {
68+
if (transaction.isRecording()) {
6869
transaction.initSpanRecorder(options._experiments && (options._experiments.maxSpans as number));
6970
}
7071
if (client && client.emit) {
@@ -94,7 +95,7 @@ export function startIdleTransaction(
9495
transactionContext,
9596
...customSamplingContext,
9697
});
97-
if (transaction.sampled) {
98+
if (transaction.isRecording()) {
9899
transaction.initSpanRecorder(options._experiments && (options._experiments.maxSpans as number));
99100
}
100101
if (client && client.emit) {

packages/core/src/tracing/sampling.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,16 @@ export function sampleTransaction<T extends Transaction>(
2121
): T {
2222
// nothing to do if tracing is not enabled
2323
if (!hasTracingEnabled(options)) {
24+
// eslint-disable-next-line deprecation/deprecation
2425
transaction.sampled = false;
2526
return transaction;
2627
}
2728

2829
// if the user has forced a sampling decision by passing a `sampled` value in their transaction context, go with that
30+
// eslint-disable-next-line deprecation/deprecation
2931
if (transaction.sampled !== undefined) {
3032
transaction.setMetadata({
33+
// eslint-disable-next-line deprecation/deprecation
3134
sampleRate: Number(transaction.sampled),
3235
});
3336
return transaction;
@@ -60,6 +63,7 @@ export function sampleTransaction<T extends Transaction>(
6063
// only valid values are booleans or numbers between 0 and 1.)
6164
if (!isValidSampleRate(sampleRate)) {
6265
DEBUG_BUILD && logger.warn('[Tracing] Discarding transaction because of invalid sample rate.');
66+
// eslint-disable-next-line deprecation/deprecation
6367
transaction.sampled = false;
6468
return transaction;
6569
}
@@ -74,15 +78,18 @@ export function sampleTransaction<T extends Transaction>(
7478
: 'a negative sampling decision was inherited or tracesSampleRate is set to 0'
7579
}`,
7680
);
81+
// eslint-disable-next-line deprecation/deprecation
7782
transaction.sampled = false;
7883
return transaction;
7984
}
8085

8186
// Now we roll the dice. Math.random is inclusive of 0, but not of 1, so strict < is safe here. In case sampleRate is
8287
// a boolean, the < comparison will cause it to be automatically cast to 1 if it's true and 0 if it's false.
88+
// eslint-disable-next-line deprecation/deprecation
8389
transaction.sampled = Math.random() < (sampleRate as number | boolean);
8490

8591
// if we're not going to keep it, we're done
92+
// eslint-disable-next-line deprecation/deprecation
8693
if (!transaction.sampled) {
8794
DEBUG_BUILD &&
8895
logger.log(

packages/core/src/tracing/span.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ export class Span implements SpanInterface {
154154
}
155155
// We want to include booleans as well here
156156
if ('sampled' in spanContext) {
157+
// eslint-disable-next-line deprecation/deprecation
157158
this.sampled = spanContext.sampled;
158159
}
159160
if (spanContext.op) {
@@ -193,6 +194,7 @@ export class Span implements SpanInterface {
193194
const childSpan = new Span({
194195
...spanContext,
195196
parentSpanId: this.spanId,
197+
// eslint-disable-next-line deprecation/deprecation
196198
sampled: this.sampled,
197199
traceId: this.traceId,
198200
});
@@ -351,6 +353,7 @@ export class Span implements SpanInterface {
351353
this.endTimestamp = spanContext.endTimestamp;
352354
this.op = spanContext.op;
353355
this.parentSpanId = spanContext.parentSpanId;
356+
// eslint-disable-next-line deprecation/deprecation
354357
this.sampled = spanContext.sampled;
355358
this.spanId = spanContext.spanId || this.spanId;
356359
this.startTimestamp = spanContext.startTimestamp || this.startTimestamp;
@@ -400,6 +403,11 @@ export class Span implements SpanInterface {
400403
});
401404
}
402405

406+
/** @inheritdoc */
407+
public isRecording(): boolean {
408+
return !this.endTimestamp && !!this.sampled;
409+
}
410+
403411
/**
404412
* Get the merged data for this span.
405413
* For now, this combines `data` and `attributes` together,

packages/core/src/utils/spanUtils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export function spanToTraceContext(span: Span): TraceContext {
2424
* Convert a Span to a Sentry trace header.
2525
*/
2626
export function spanToTraceHeader(span: Span): string {
27-
return generateSentryTraceHeader(span.traceId, span.spanId, span.sampled);
27+
return generateSentryTraceHeader(span.traceId, span.spanId, span.isRecording());
2828
}
2929

3030
/**

packages/core/test/lib/tracing/span.test.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,23 @@ describe('span', () => {
209209
});
210210
});
211211

212+
describe('isRecording', () => {
213+
it('returns true for sampled span', () => {
214+
const span = new Span({ sampled: true });
215+
expect(span.isRecording()).toEqual(true);
216+
});
217+
218+
it('returns false for sampled, finished span', () => {
219+
const span = new Span({ sampled: true, endTimestamp: Date.now() });
220+
expect(span.isRecording()).toEqual(false);
221+
});
222+
223+
it('returns false for unsampled span', () => {
224+
const span = new Span({ sampled: false });
225+
expect(span.isRecording()).toEqual(false);
226+
});
227+
});
228+
212229
// Ensure that attributes & data are merged together
213230
describe('_getData', () => {
214231
it('works without data & attributes', () => {

packages/tracing-internal/src/browser/browsertracing.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,7 @@ export class BrowserTracing implements Integration {
333333
this._latestRouteName = finalContext.name;
334334
this._latestRouteSource = finalContext.metadata && finalContext.metadata.source;
335335

336+
// eslint-disable-next-line deprecation/deprecation
336337
if (finalContext.sampled === false) {
337338
DEBUG_BUILD && logger.log(`[Tracing] Will not send ${finalContext.op} transaction because of beforeNavigate.`);
338339
}

packages/types/src/span.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ export interface SpanContext {
5858

5959
/**
6060
* Was this span chosen to be sent as part of the sample?
61+
*
62+
* @deprecated Use `isRecording()` instead.
6163
*/
6264
sampled?: boolean;
6365

@@ -268,4 +270,10 @@ export interface Span extends SpanContext {
268270
trace_id: string;
269271
origin?: SpanOrigin;
270272
};
273+
274+
/**
275+
* If this is span is actually recording data.
276+
* This will return false if tracing is disabled, this span was not sampled or if the span is already finished.
277+
*/
278+
isRecording(): boolean;
271279
}

0 commit comments

Comments
 (0)