Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const flushMarkerMatcher = (envelope: Envelope): void => {

it('instruments sync KV operations on Durable Object storage', async ({ signal }) => {
const runner = createRunner(__dirname)
.unordered()
.expect(envelope => {
const transactionEvent = envelope[1]?.[0]?.[1] as TransactionEvent | undefined;
const spans = transactionEvent?.spans ?? [];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
},
"dependencies": {
"@sentry/nextjs": "file:../../packed/sentry-nextjs-packed.tgz",
"@sentry/bun": "file:../../packed/sentry-bun-packed.tgz",
"@sentry/core": "file:../../packed/sentry-core-packed.tgz",
"import-in-the-middle": "^2",
"next": "16.2.3",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { bunHttpServerIntegration } from '@sentry/bun';
import * as Sentry from '@sentry/nextjs';

Sentry.init({
Expand All @@ -8,4 +9,8 @@ Sentry.init({
tracesSampleRate: 1.0,
dataCollection: { userInfo: true },
tracePropagationTargets: ['http://localhost:3030/propagation/test-outgoing-fetch/check'],
// Bun does not emit the `node:http` diagnostics channel the Node SDK uses to isolate incoming
// requests, so each request would otherwise share one trace. Next.js emits its own server spans,
// hence `spans: false` — this only isolates the request and resets its trace.
integrations: [bunHttpServerIntegration({ spans: false })],
});
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@ Sentry.pinoIntegration.untrackLogger(ignoredLogger);

ignoredLogger.info('this will not be tracked');

Sentry.withIsolationScope(() => {
// Each operation runs in its own trace, so logs emitted within them carry distinct trace ids.
Sentry.startNewTrace(() => {
Sentry.startSpan({ name: 'startup' }, () => {
logger.info({ user: 'user-id', something: { more: 3, complex: 'nope' } }, 'hello world');
});
});

setTimeout(() => {
Sentry.withIsolationScope(() => {
Sentry.startNewTrace(() => {
Sentry.startSpan({ name: 'later' }, () => {
const child = logger.child({ module: 'authentication' });
child.error(new Error('oh no'));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ test('bindScopeToEmitter preserves the active span for listeners firing in a dif
expect(childBound?.parent_span_id).toBe(parentSpanId);
expect(childBound?.trace_id).toBe(parentTraceId);

// The unbound emitter's listener ran without the parent active -> its own, separate trace.
// The unbound emitter's listener ran without the parent active -> its own root transaction,
// not nested under the parent span. It still shares the isolation scope's propagation context
// trace, matching the core SDK behavior for root spans.
expect(childUnbound?.spans).toEqual([]);
expect(childUnbound?.contexts?.trace?.trace_id).not.toBe(parentTraceId);
expect(childUnbound?.contexts?.trace?.parent_span_id).toBeUndefined();
});
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,24 @@ afterAll(() => {
});

test('sends manually started streamed parallel root spans in root context', async () => {
expect.assertions(7);
expect.assertions(6);

await createRunner(__dirname, 'scenario.ts')
.expect({ span: { items: [{ name: 'test_span_1' }] } })
.expect({
span: spanContainer => {
expect(spanContainer).toBeDefined();
const traceId = spanContainer.items[0]!.trace_id;
expect(traceId).toMatch(/^[0-9a-f]{32}$/);

// It ignores propagation context of the root context
expect(traceId).not.toBe('12345678901234567890123456789012');
expect(spanContainer.items[0]!.parent_span_id).toBeUndefined();
const span1 = spanContainer.items.find(item => item.name === 'test_span_1');
const span2 = spanContainer.items.find(item => item.name === 'test_span_2');
expect(span1).toBeDefined();
expect(span2).toBeDefined();

// Different trace ID than the first span
const trace1Id = spanContainer.items[0]!.attributes.spanIdTraceId?.value;
expect(trace1Id).toMatch(/^[0-9a-f]{32}$/);
// Both root spans continue the scope's propagation context, matching the core SDK behavior.
expect(span1!.trace_id).toBe('12345678901234567890123456789012');
expect(span1!.parent_span_id).toBe('1234567890123456');

expect(trace1Id).not.toBe(traceId);
// Same trace ID for both spans
expect(span2!.trace_id).toBe(span1!.trace_id);
},
})
.start()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,23 @@ afterAll(() => {
});

test('should send manually started parallel root spans in root context', async () => {
expect.assertions(7);
expect.assertions(6);

await createRunner(__dirname, 'scenario.ts')
.expect({ transaction: { transaction: 'test_span_1' } })
.expect({
transaction: transaction => {
expect(transaction).toBeDefined();
const traceId = transaction.contexts?.trace?.trace_id;
expect(traceId).toBeDefined();

// It ignores propagation context of the root context
expect(traceId).not.toBe('12345678901234567890123456789012');
expect(transaction.contexts?.trace?.parent_span_id).toBeUndefined();
// Both root spans continue the scope's propagation context, matching the core SDK behavior.
expect(traceId).toBe('12345678901234567890123456789012');
expect(transaction.contexts?.trace?.parent_span_id).toBe('1234567890123456');

// Different trace ID than the first span
// Same trace ID as the first span
const trace1Id = transaction.contexts?.trace?.data?.spanIdTraceId;
expect(trace1Id).toBeDefined();
expect(trace1Id).not.toBe(traceId);
expect(trace1Id).toBe('12345678901234567890123456789012');
expect(trace1Id).toBe(traceId);
},
})
.start()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,20 @@ test('sends manually started streamed parallel root spans outside of root contex
expect.assertions(6);

await createRunner(__dirname, 'scenario.ts')
.expect({ span: { items: [{ name: 'test_span_1' }] } })
.expect({
span: spanContainer => {
expect(spanContainer).toBeDefined();
const traceId = spanContainer.items[0]!.trace_id;
expect(traceId).toMatch(/^[0-9a-f]{32}$/);
expect(spanContainer.items[0]!.parent_span_id).toBeUndefined();

const trace1Id = spanContainer.items[0]!.attributes.spanIdTraceId?.value;
expect(trace1Id).toMatch(/^[0-9a-f]{32}$/);
const span1 = spanContainer.items.find(item => item.name === 'test_span_1');
const span2 = spanContainer.items.find(item => item.name === 'test_span_2');
expect(span1).toBeDefined();
expect(span2).toBeDefined();

// Different trace ID as the first span
expect(trace1Id).not.toBe(traceId);
expect(span1!.trace_id).toMatch(/^[0-9a-f]{32}$/);
expect(span1!.parent_span_id).toBeUndefined();

// Same trace ID for both spans - both root spans share the scope's propagation context
expect(span2!.trace_id).toBe(span1!.trace_id);
},
})
.start()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,22 @@ test('sends manually started streamed parallel root spans outside of root contex
expect.assertions(6);

await createRunner(__dirname, 'scenario.ts')
.expect({ span: { items: [{ name: 'test_span_1' }] } })
.expect({
span: spanContainer => {
expect(spanContainer).toBeDefined();
const traceId = spanContainer.items[0]!.trace_id;
expect(traceId).toMatch(/^[0-9a-f]{32}$/);
expect(spanContainer.items[0]!.parent_span_id).toBeUndefined();

const trace1Id = spanContainer.items[0]!.attributes.spanIdTraceId?.value;
expect(trace1Id).toMatch(/^[0-9a-f]{32}$/);
const span1 = spanContainer.items.find(item => item.name === 'test_span_1');
const span2 = spanContainer.items.find(item => item.name === 'test_span_2');
expect(span1).toBeDefined();
expect(span2).toBeDefined();

// Different trace ID as the first span
expect(trace1Id).not.toBe(traceId);
// Both root spans continue the scope's propagation context, including the parentSpanId,
// matching the core SDK behavior.
expect(span1!.trace_id).toBe('12345678901234567890123456789012');
expect(span1!.parent_span_id).toBe('1234567890123456');

// Same trace ID for both spans
expect(span2!.trace_id).toBe(span1!.trace_id);
},
})
.start()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@ test('should send manually started parallel root spans outside of root context w
transaction: transaction => {
expect(transaction).toBeDefined();
const traceId = transaction.contexts?.trace?.trace_id;
expect(traceId).toBeDefined();
expect(transaction.contexts?.trace?.parent_span_id).toBeUndefined();

const trace1Id = transaction.contexts?.trace?.data?.spanIdTraceId;
expect(trace1Id).toBeDefined();
// Both root spans continue the scope's propagation context, including the parentSpanId,
// matching the core SDK behavior.
expect(traceId).toBe('12345678901234567890123456789012');
expect(transaction.contexts?.trace?.parent_span_id).toBe('1234567890123456');

// Different trace ID as the first span
expect(trace1Id).not.toBe(traceId);
// Same trace ID as the first span
const trace1Id = transaction.contexts?.trace?.data?.spanIdTraceId;
expect(trace1Id).toBe(traceId);
},
})
.start()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ test('should send manually started parallel root spans outside of root context',
const trace1Id = transaction.contexts?.trace?.data?.spanIdTraceId;
expect(trace1Id).toBeDefined();

// Different trace ID as the first span
expect(trace1Id).not.toBe(traceId);
// Same trace ID as the first span - both root spans share the scope's propagation context
expect(trace1Id).toBe(traceId);
},
})
.start()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ describe('outgoing fetch', () => {
const [SERVER_URL, closeTestServer] = await createTestServer()
.get('/api/v0', headers => {
expect(headers['baggage']).toEqual(expect.any(String));
expect(headers['sentry-trace']).toEqual(expect.stringMatching(/^([a-f\d]{32})-([a-f\d]{16})$/));
expect(headers['sentry-trace']).not.toEqual('00000000000000000000000000000000-0000000000000000');
expect(headers['sentry-trace']).toEqual(expect.stringMatching(/^([a-f\d]{32})-([a-f\d]{16})-0$/));
expect(headers['sentry-trace']).not.toEqual('00000000000000000000000000000000-0000000000000000-0');
})
.get('/api/v1', headers => {
expect(headers['baggage']).toEqual(expect.any(String));
expect(headers['sentry-trace']).toEqual(expect.stringMatching(/^([a-f\d]{32})-([a-f\d]{16})$/));
expect(headers['sentry-trace']).not.toEqual('00000000000000000000000000000000-0000000000000000');
expect(headers['sentry-trace']).toEqual(expect.stringMatching(/^([a-f\d]{32})-([a-f\d]{16})-0$/));
expect(headers['sentry-trace']).not.toEqual('00000000000000000000000000000000-0000000000000000-0');
})
.get('/api/v2', headers => {
expect(headers['baggage']).toBeUndefined();
Expand Down
7 changes: 0 additions & 7 deletions packages/browser/src/tracing/browserTracingIntegration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import {
getClient,
getCurrentScope,
getDynamicSamplingContextFromSpan,
getIsolationScope,
getLocationHref,
GLOBAL_OBJ,
hasSpansEnabled,
Expand Down Expand Up @@ -554,12 +553,6 @@ export const browserTracingIntegration = ((options: Partial<BrowserTracingOption

maybeEndActiveSpan();

getIsolationScope().setPropagationContext({
traceId: generateTraceId(),
sampleRand: Math.random(),
propagationSpanId: hasSpansEnabled() ? undefined : generateSpanId(),
});

const scope = getCurrentScope();
scope.setPropagationContext({
traceId: generateTraceId(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -937,37 +937,24 @@ describe('browserTracingIntegration', () => {
setCurrentClient(client);
client.init();

const oldIsolationScopePropCtx = getIsolationScope().getPropagationContext();
const oldCurrentScopePropCtx = getCurrentScope().getPropagationContext();

startBrowserTracingNavigationSpan(client, { name: 'test navigation span' });

const newIsolationScopePropCtx = getIsolationScope().getPropagationContext();
const newCurrentScopePropCtx = getCurrentScope().getPropagationContext();

expect(oldCurrentScopePropCtx).toEqual({
traceId: expect.stringMatching(/[a-f0-9]{32}/),
propagationSpanId: expect.stringMatching(/[a-f0-9]{16}/),
sampleRand: expect.any(Number),
});
expect(oldIsolationScopePropCtx).toEqual({
traceId: expect.stringMatching(/[a-f0-9]{32}/),
sampleRand: expect.any(Number),
});

expect(newCurrentScopePropCtx).toEqual({
traceId: expect.stringMatching(/[a-f0-9]{32}/),
propagationSpanId: expect.stringMatching(/[a-f0-9]{16}/),
sampleRand: expect.any(Number),
});
expect(newIsolationScopePropCtx).toEqual({
traceId: expect.stringMatching(/[a-f0-9]{32}/),
propagationSpanId: expect.stringMatching(/[a-f0-9]{16}/),
sampleRand: expect.any(Number),
});

expect(newIsolationScopePropCtx.traceId).not.toEqual(oldIsolationScopePropCtx.traceId);
expect(newCurrentScopePropCtx.traceId).not.toEqual(oldCurrentScopePropCtx.traceId);
expect(newIsolationScopePropCtx.propagationSpanId).not.toEqual(oldIsolationScopePropCtx.propagationSpanId);
});

it("saves the span's positive sampling decision and its DSC on the propagationContext when the span finishes", () => {
Expand Down
1 change: 1 addition & 0 deletions packages/bun/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,5 +199,6 @@ export {
initWithoutDefaultIntegrations,
} from './sdk';
export { bunServerIntegration } from './integrations/bunserver';
export { bunHttpServerIntegration } from './integrations/bunHttpServer';
export { bunRuntimeMetricsIntegration, type BunRuntimeMetricsOptions } from './integrations/bunRuntimeMetrics';
export { makeFetchTransport } from './transports';
Loading
Loading