Skip to content
Open
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
6 changes: 6 additions & 0 deletions dev-packages/cloudflare-integration-tests/runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,17 @@ export function createRunner(...paths: string[]) {
// By default, we ignore session & sessions
const ignored: Set<EnvelopeItemType> = new Set(['session', 'sessions', 'client_report']);
let serverUrl: string | undefined;
const extraWranglerArgs: string[] = [];

return {
withServerUrl: function (url: string) {
serverUrl = url;
return this;
},
withWranglerArgs: function (...args: string[]) {
extraWranglerArgs.push(...args);
return this;
},
expect: function (expected: Expected) {
expectedEnvelopes.push(expected);
return this;
Expand Down Expand Up @@ -237,6 +242,7 @@ export function createRunner(...paths: string[]) {
`SENTRY_DSN:http://public@localhost:${mockServerPort}/1337`,
'--var',
`SERVER_URL:${serverUrl}`,
...extraWranglerArgs,
],
{ stdio, signal },
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import * as Sentry from '@sentry/cloudflare';
import { DurableObject } from 'cloudflare:workers';

interface Env {
SENTRY_DSN: string;
MY_DURABLE_OBJECT: DurableObjectNamespace;
MY_QUEUE: Queue;
}

class MyDurableObjectBase extends DurableObject<Env> {
async fetch(request: Request) {

Check warning on line 11 in dev-packages/cloudflare-integration-tests/suites/tracing/propagation/worker-do/index.ts

View workflow job for this annotation

GitHub Actions / Lint

eslint(no-unused-vars)

Parameter 'request' is declared but never used. Unused parameters should start with a '_'.
return new Response('DO is fine');
}
}

export const MyDurableObject = Sentry.instrumentDurableObjectWithSentry(
(env: Env) => ({
dsn: env.SENTRY_DSN,
tracesSampleRate: 1.0,
}),
MyDurableObjectBase,
);

export default Sentry.withSentry(
(env: Env) => ({
dsn: env.SENTRY_DSN,
tracesSampleRate: 1.0,
}),
{
async fetch(request, env) {
const url = new URL(request.url);

if (url.pathname === '/queue/send') {
await env.MY_QUEUE.send({ action: 'test' });
return new Response('Queued');
}

const id = env.MY_DURABLE_OBJECT.idFromName('test');
const stub = env.MY_DURABLE_OBJECT.get(id);
const response = await stub.fetch(new Request('http://fake-host/hello'));
const text = await response.text();
return new Response(text);
},

async queue(batch, env, _ctx) {
const id = env.MY_DURABLE_OBJECT.idFromName('test');
const stub = env.MY_DURABLE_OBJECT.get(id);
for (const message of batch.messages) {
await stub.fetch(new Request('http://fake-host/hello'));
message.ack();
}
},

async scheduled(controller, env, _ctx) {
const id = env.MY_DURABLE_OBJECT.idFromName('test');
const stub = env.MY_DURABLE_OBJECT.get(id);
await stub.fetch(new Request('http://fake-host/hello'));
},
} satisfies ExportedHandler<Env>,
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
import { expect, it } from 'vitest';
import type { Event } from '@sentry/core';
import { createRunner } from '../../../../runner';

it('propagates trace from worker to durable object', async ({ signal }) => {
let workerTraceId: string | undefined;
let workerSpanId: string | undefined;
let doTraceId: string | undefined;
let doParentSpanId: string | undefined;

const runner = createRunner(__dirname)
.expect(envelope => {
const transactionEvent = envelope[1]?.[0]?.[1] as Event;

expect(transactionEvent).toEqual(
expect.objectContaining({
contexts: expect.objectContaining({
trace: expect.objectContaining({
op: 'http.server',
data: expect.objectContaining({
'sentry.origin': 'auto.http.cloudflare',
}),
origin: 'auto.http.cloudflare',
}),
}),
transaction: 'GET /hello',
}),
);
doTraceId = transactionEvent.contexts?.trace?.trace_id as string;
doParentSpanId = transactionEvent.contexts?.trace?.parent_span_id as string;
})
.expect(envelope => {
const transactionEvent = envelope[1]?.[0]?.[1] as Event;

expect(transactionEvent).toEqual(
expect.objectContaining({
contexts: expect.objectContaining({
trace: expect.objectContaining({
op: 'http.server',
data: expect.objectContaining({
'sentry.origin': 'auto.http.cloudflare',
}),
origin: 'auto.http.cloudflare',
}),
}),
transaction: 'GET /',
}),
);
workerTraceId = transactionEvent.contexts?.trace?.trace_id as string;
workerSpanId = transactionEvent.contexts?.trace?.span_id as string;
})
.unordered()
.start(signal);
await runner.makeRequest('get', '/');
await runner.completed();

expect(workerTraceId).toBeDefined();
expect(doTraceId).toBeDefined();
expect(workerTraceId).toBe(doTraceId);

expect(workerSpanId).toBeDefined();
expect(doParentSpanId).toBeDefined();
expect(doParentSpanId).toBe(workerSpanId);
});

it('propagates trace from queue handler to durable object', async ({ signal }) => {
let queueTraceId: string | undefined;
let queueSpanId: string | undefined;
let doTraceId: string | undefined;
let doParentSpanId: string | undefined;

const runner = createRunner(__dirname)
.expect(envelope => {
const transactionEvent = envelope[1]?.[0]?.[1] as Event;

expect(transactionEvent).toEqual(
expect.objectContaining({
contexts: expect.objectContaining({
trace: expect.objectContaining({
op: 'http.server',
data: expect.objectContaining({
'sentry.origin': 'auto.http.cloudflare',
}),
origin: 'auto.http.cloudflare',
}),
}),
transaction: 'GET /hello',
}),
);
doTraceId = transactionEvent.contexts?.trace?.trace_id as string;
doParentSpanId = transactionEvent.contexts?.trace?.parent_span_id as string;
})
.expect(envelope => {
const transactionEvent = envelope[1]?.[0]?.[1] as Event;

expect(transactionEvent).toEqual(
expect.objectContaining({
contexts: expect.objectContaining({
trace: expect.objectContaining({
op: 'queue.process',
data: expect.objectContaining({
'sentry.origin': 'auto.faas.cloudflare.queue',
}),
origin: 'auto.faas.cloudflare.queue',
}),
}),
transaction: 'process my-queue',
}),
);
queueTraceId = transactionEvent.contexts?.trace?.trace_id as string;
queueSpanId = transactionEvent.contexts?.trace?.span_id as string;
})
// Also expect the fetch transaction from the /queue/send request
.expect(envelope => {
const transactionEvent = envelope[1]?.[0]?.[1] as Event;

expect(transactionEvent).toEqual(
expect.objectContaining({
contexts: expect.objectContaining({
trace: expect.objectContaining({
op: 'http.server',
data: expect.objectContaining({
'sentry.origin': 'auto.http.cloudflare',
}),
origin: 'auto.http.cloudflare',
}),
}),
transaction: 'GET /queue/send',
}),
);
})
.unordered()
.start(signal);
// The fetch handler sends a message to the queue, which triggers the queue consumer
await runner.makeRequest('get', '/queue/send');
await runner.completed();

expect(queueTraceId).toBeDefined();
expect(doTraceId).toBeDefined();
expect(queueTraceId).toBe(doTraceId);

expect(queueSpanId).toBeDefined();
expect(doParentSpanId).toBeDefined();
expect(doParentSpanId).toBe(queueSpanId);
});

it('propagates trace from scheduled handler to durable object', async ({ signal }) => {
let scheduledTraceId: string | undefined;
let scheduledSpanId: string | undefined;
let doTraceId: string | undefined;
let doParentSpanId: string | undefined;

const runner = createRunner(__dirname)
.withWranglerArgs('--test-scheduled')
.expect(envelope => {
const transactionEvent = envelope[1]?.[0]?.[1] as Event;

expect(transactionEvent).toEqual(
expect.objectContaining({
contexts: expect.objectContaining({
trace: expect.objectContaining({
op: 'http.server',
data: expect.objectContaining({
'sentry.origin': 'auto.http.cloudflare',
}),
origin: 'auto.http.cloudflare',
}),
}),
transaction: 'GET /hello',
}),
);
doTraceId = transactionEvent.contexts?.trace?.trace_id as string;
doParentSpanId = transactionEvent.contexts?.trace?.parent_span_id as string;
})
.expect(envelope => {
const transactionEvent = envelope[1]?.[0]?.[1] as Event;

expect(transactionEvent).toEqual(
expect.objectContaining({
contexts: expect.objectContaining({
trace: expect.objectContaining({
op: 'faas.cron',
data: expect.objectContaining({
'sentry.origin': 'auto.faas.cloudflare.scheduled',
}),
origin: 'auto.faas.cloudflare.scheduled',
}),
}),
}),
);
scheduledTraceId = transactionEvent.contexts?.trace?.trace_id as string;
scheduledSpanId = transactionEvent.contexts?.trace?.span_id as string;
})
.unordered()
.start(signal);
await runner.makeRequest('get', '/__scheduled?cron=*+*+*+*+*');
await runner.completed();

expect(scheduledTraceId).toBeDefined();
expect(doTraceId).toBeDefined();
expect(scheduledTraceId).toBe(doTraceId);

expect(scheduledSpanId).toBeDefined();
expect(doParentSpanId).toBeDefined();
expect(doParentSpanId).toBe(scheduledSpanId);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"name": "cloudflare-durable-objects",
"main": "index.ts",
"compatibility_date": "2025-06-17",
"compatibility_flags": ["nodejs_als"],
"migrations": [
{
"new_sqlite_classes": ["MyDurableObject"],
"tag": "v1",
},
],
"durable_objects": {
"bindings": [
{
"class_name": "MyDurableObject",
"name": "MY_DURABLE_OBJECT",
},
],
},
"queues": {
"producers": [
{
"binding": "MY_QUEUE",
"queue": "my-queue",
},
],
"consumers": [
{
"queue": "my-queue",
},
],
},
"triggers": {
"crons": ["* * * * *"],
},
"vars": {
"SENTRY_DSN": "https://932e620ee3921c3b4a61c72558ad88ce@o447951.ingest.us.sentry.io/4509553159831552",
},
}
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import { expect, it } from 'vitest';
import type { Event } from '@sentry/core';
import { createRunner } from '../../../runner';
import { createRunner } from '../../../../runner';

it('propagates trace from worker to worker via service binding', async ({ signal }) => {
let workerTraceId: string | undefined;
let workerSpanId: string | undefined;
let subWorkerTraceId: string | undefined;
let subWorkerParentSpanId: string | undefined;

it('adds a trace to a worker via service binding', async ({ signal }) => {
const runner = createRunner(__dirname)
.expect(envelope => {
const transactionEvent = envelope[1]?.[0]?.[1] as Event;
Expand All @@ -20,6 +25,8 @@ it('adds a trace to a worker via service binding', async ({ signal }) => {
transaction: 'GET /',
}),
);
workerTraceId = transactionEvent.contexts?.trace?.trace_id as string;
workerSpanId = transactionEvent.contexts?.trace?.span_id as string;
})
.expect(envelope => {
const transactionEvent = envelope[1]?.[0]?.[1] as Event;
Expand All @@ -37,9 +44,19 @@ it('adds a trace to a worker via service binding', async ({ signal }) => {
transaction: 'GET /hello',
}),
);
subWorkerTraceId = transactionEvent.contexts?.trace?.trace_id as string;
subWorkerParentSpanId = transactionEvent.contexts?.trace?.parent_span_id as string;
})
.unordered()
.start(signal);
await runner.makeRequest('get', '/');
await runner.completed();

expect(workerTraceId).toBeDefined();
expect(subWorkerTraceId).toBeDefined();
expect(workerTraceId).toBe(subWorkerTraceId);

expect(workerSpanId).toBeDefined();
expect(subWorkerParentSpanId).toBeDefined();
expect(subWorkerParentSpanId).toBe(workerSpanId);
});
Loading
Loading