Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
feat(cloudflare): Consolidate startNewTrace and linkPreviousTrace
  • Loading branch information
JPeer264 committed Apr 13, 2026
commit 56a53f819d6efab8088618d5a59fe645b5da1a6f
2 changes: 1 addition & 1 deletion packages/cloudflare/src/durableobject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ export function instrumentDurableObjectWithSentry<
if (obj.alarm && typeof obj.alarm === 'function') {
// Alarms are independent invocations, so we start a new trace and link to the previous alarm
obj.alarm = wrapMethodWithSentry(
{ options, context, spanName: 'alarm', spanOp: 'function', startNewTrace: true, linkPreviousTrace: true },
{ options, context, spanName: 'alarm', spanOp: 'function', startNewTrace: true },
obj.alarm,
);
}
Expand Down
22 changes: 9 additions & 13 deletions packages/cloudflare/src/wrapMethodWithSentry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,14 @@ type MethodWrapperOptions = {
/**
* If true, starts a fresh trace instead of inheriting from a parent trace.
* Useful for scheduled/independent invocations like alarms.
* @default false
*/
startNewTrace?: boolean;
/**
* If true, stores the current span context and links to the previous invocation's span.
* Requires `startNewTrace` to be true. Uses Durable Object storage to persist the link.
* The link is set asynchronously via `span.addLinks()` in a `waitUntil` to avoid blocking.
*
* If true, it also stores the current span context and links to the previous invocation's span.
* Uses Durable Object storage to persist the link. The link is set asynchronously via `span.addLinks()`
* in a `waitUntil` to avoid blocking.
*
* @default false
*/
linkPreviousTrace?: boolean;
startNewTrace?: boolean;
};

// eslint-disable-next-line @typescript-eslint/no-explicit-any
Expand Down Expand Up @@ -68,7 +65,7 @@ export function wrapMethodWithSentry<T extends OriginalMethod>(
original =>
new Proxy(original, {
apply(target, thisArg, args: Parameters<T>) {
const { startNewTrace, linkPreviousTrace } = wrapperOptions;
const { startNewTrace } = wrapperOptions;

// For startNewTrace, always use withIsolationScope to ensure a fresh scope
// Otherwise, use existing client's scope or isolation scope
Expand Down Expand Up @@ -102,7 +99,7 @@ export function wrapMethodWithSentry<T extends OriginalMethod>(
const methodName = wrapperOptions.spanName || 'unknown';

const teardown = async (): Promise<void> => {
if (linkPreviousTrace && storage) {
if (startNewTrace && storage) {
await storeSpanContext(storage, methodName);
}
await flushAndDispose(clientToDispose);
Comment thread
sentry[bot] marked this conversation as resolved.
Expand Down Expand Up @@ -149,7 +146,6 @@ export function wrapMethodWithSentry<T extends OriginalMethod>(
}
}

const spanName = wrapperOptions.spanName || methodName;
const attributes = wrapperOptions.spanOp
? {
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: wrapperOptions.spanOp,
Expand All @@ -158,10 +154,10 @@ export function wrapMethodWithSentry<T extends OriginalMethod>(
: {};

const executeSpan = (): unknown => {
return startSpan({ name: spanName, attributes }, span => {
return startSpan({ name: methodName, attributes }, span => {
// When linking to previous trace, fetch the stored context and add links asynchronously
// This avoids blocking the response while fetching from storage
if (linkPreviousTrace && storage) {
if (startNewTrace && storage) {
waitUntil?.(
getStoredSpanContext(storage, methodName).then(storedContext => {
if (storedContext) {
Expand Down
21 changes: 8 additions & 13 deletions packages/cloudflare/test/wrapMethodWithSentry.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ describe('wrapMethodWithSentry', () => {
expect(handler).toHaveBeenCalled();
});

it('does not change sync/async behavior when linkPreviousTrace is true (links are set via waitUntil)', () => {
it('does not change sync/async behavior when startNewTrace is true (links are set via waitUntil)', () => {
const handler = vi.fn().mockReturnValue('sync-result');
const mockStorage = {
get: vi.fn().mockResolvedValue(undefined),
Expand All @@ -164,13 +164,12 @@ describe('wrapMethodWithSentry', () => {
context,
spanName: 'alarm',
startNewTrace: true,
linkPreviousTrace: true,
};

const wrapped = wrapMethodWithSentry(options, handler);
const result = wrapped();

// linkPreviousTrace does not make the result async - links are set via waitUntil
// startNewTrace does not make the result async - links are set via waitUntil
expect(result).not.toBeInstanceOf(Promise);
expect(result).toBe('sync-result');

Expand Down Expand Up @@ -345,8 +344,8 @@ describe('wrapMethodWithSentry', () => {
});
});

describe('linkPreviousTrace option', () => {
it('retrieves stored span context when linkPreviousTrace is true', async () => {
describe('span linking', () => {
it('retrieves stored span context when startNewTrace is true', async () => {
const storedContext = {
traceId: 'previous-trace-id-1234567890123456',
spanId: 'previous-span-id',
Expand All @@ -365,7 +364,6 @@ describe('wrapMethodWithSentry', () => {
options: {},
context,
startNewTrace: true,
linkPreviousTrace: true,
spanName: 'alarm',
};

Expand Down Expand Up @@ -399,7 +397,6 @@ describe('wrapMethodWithSentry', () => {
options: {},
context,
startNewTrace: true,
linkPreviousTrace: true,
spanName: 'alarm',
};

Expand All @@ -423,7 +420,7 @@ describe('wrapMethodWithSentry', () => {
);
});

it('stores span context after execution when linkPreviousTrace is true', async () => {
it('stores span context after execution when startNewTrace is true', async () => {
vi.mocked(sentryCore.getActiveSpan).mockReturnValue({
spanContext: vi.fn().mockReturnValue({
traceId: 'current-trace-id-123456789012345678',
Expand All @@ -445,7 +442,6 @@ describe('wrapMethodWithSentry', () => {
options: {},
context,
startNewTrace: true,
linkPreviousTrace: true,
spanName: 'alarm',
};

Expand All @@ -456,7 +452,7 @@ describe('wrapMethodWithSentry', () => {
expect(mockStorage.put).toHaveBeenCalledWith('__SENTRY_TRACE_LINK__alarm', expect.any(Object));
});

it('does not store span context when linkPreviousTrace is false', async () => {
it('does not store span context when startNewTrace is false', async () => {
vi.mocked(sentryCore.getActiveSpan).mockReturnValue({
spanContext: vi.fn().mockReturnValue({
traceId: 'current-trace-id-123456789012345678',
Expand All @@ -479,8 +475,7 @@ describe('wrapMethodWithSentry', () => {
const options = {
options: {},
context,
startNewTrace: true,
linkPreviousTrace: false,
startNewTrace: false,
spanName: 'alarm',
};

Expand All @@ -490,7 +485,7 @@ describe('wrapMethodWithSentry', () => {
// Wait for all waitUntil promises to resolve
await Promise.all(waitUntilPromises);

// Should NOT store span context when linkPreviousTrace is false
// Should NOT store span context when startNewTrace is false
expect(mockStorage.put).not.toHaveBeenCalledWith('__SENTRY_TRACE_LINK__alarm', expect.any(Object));
});
});
Expand Down