Skip to content

Commit 564dc8f

Browse files
JPeer264claude
andauthored
test(cloudflare): Port the vite-autoinstrument suites to span streaming
Removes the `traceLifecycle: 'static'` pin from the seventeen `suites/vite-autoinstrument` suites, and rewrites the assertions from transaction envelopes to span v2. Under streaming a whole request chain is one trace, so a suite reads the trace rather than one envelope per hop. Every suite now uses `collectStreamedSpans`, because each hop, a Durable Object, a `WorkerEntrypoint` or a Workflow step, runs in its own isolate and streams its own envelope. The proof of instrumentation is unchanged, only its shape is. A Durable Object still shows the `durable_object_storage_get` and `durable_object_storage_put` pair, but as children of its own segment span rather than as `spans` of its transaction. The chain is now asserted directly: a hop's segment span carries the previous hop's `span_id` as its `parent_span_id`. Every route in these suites is a raw URL, so the streamed segment name keeps the method only. `GET /greet` becomes `GET`, and the hop is identified by its `url.path` attribute. That matters for the entrypoint and combination suites, where the segment name used to tell the hops apart. The per-suite `expectDurableObjectTransaction`, `expectMainWorkerTransaction` and `expectPlainTransaction` helpers are gone. The assertions are inlined, and the suites that hit two endpoints loop over the two paths instead. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent bbe9a15 commit 564dc8f

39 files changed

Lines changed: 563 additions & 736 deletions

File tree

‎dev-packages/cloudflare-integration-tests/suites/vite-autoinstrument/combination-entrypoint-do-chained/instrument.server.ts‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,5 @@ import { defineCloudflareOptions } from '@sentry/cloudflare';
22

33
export default defineCloudflareOptions((env: { SENTRY_DSN: string }) => ({
44
dsn: env.SENTRY_DSN,
5-
traceLifecycle: 'static',
65
tracesSampleRate: 1.0,
76
}));
Lines changed: 40 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,49 @@
1-
import type { TransactionEvent } from '@sentry/core';
21
import { expect, it } from 'vitest';
32
import { createRunner } from '../../../runner';
4-
5-
// The Durable Object, reached from inside the entrypoint, emits an `http.server`
6-
// transaction whose only children are the two
7-
// `auto.db.cloudflare.durable_object` storage spans (`get` + `put`) — present
8-
// only when the class was auto-instrumented.
9-
function expectDurableObjectTransaction(transactionEvent: TransactionEvent): void {
10-
expect(transactionEvent.contexts?.trace?.op).toBe('http.server');
11-
expect(transactionEvent.contexts?.trace?.origin).toBe('auto.http.cloudflare');
12-
expect(transactionEvent.spans).toEqual([
13-
expect.objectContaining({
14-
op: 'db',
15-
description: 'durable_object_storage_get',
16-
origin: 'auto.db.cloudflare.durable_object',
17-
}),
18-
expect.objectContaining({
19-
op: 'db',
20-
description: 'durable_object_storage_put',
21-
origin: 'auto.db.cloudflare.durable_object',
22-
}),
23-
]);
24-
}
25-
26-
function expectPlainTransaction(name: string) {
27-
return (transactionEvent: TransactionEvent): void => {
28-
expect(transactionEvent.contexts?.trace?.op).toBe('http.server');
29-
expect(transactionEvent.contexts?.trace?.origin).toBe('auto.http.cloudflare');
30-
expect(transactionEvent.transaction).toBe(name);
31-
expect(transactionEvent.spans).toHaveLength(0);
32-
};
33-
}
3+
import { getSpanOp } from '../../../spanUtils';
344

355
// A single request fans out through the whole auto-wrapped chain: default
366
// handler (`/chain`) → self-bound `CounterEntrypoint` (`/work`) → `Counter`
37-
// Durable Object. All three transactions arrive only if the build-time transform
38-
// wrapped the default export, the entrypoint, and the DO — and it proves a DO
39-
// invoked from *within* an auto-instrumented entrypoint is itself instrumented.
7+
// Durable Object. All three segment spans arrive only if the build-time
8+
// transform wrapped the default export, the entrypoint, and the DO — and it
9+
// proves a DO invoked from *within* an auto-instrumented entrypoint is itself
10+
// instrumented.
11+
//
12+
// Every route here is a raw URL, so the streamed segment names keep the method
13+
// only and each hop is identified by its `url.path` attribute.
4014
it('auto-instruments a Durable Object invoked from within a WorkerEntrypoint', async ({ signal }) => {
41-
const runner = createRunner(__dirname)
42-
.unordered()
43-
.expect(envelope => expectPlainTransaction('GET /chain')(envelope[1]?.[0]?.[1] as TransactionEvent))
44-
.expect(envelope => expectPlainTransaction('GET /work')(envelope[1]?.[0]?.[1] as TransactionEvent))
45-
.expect(envelope => expectDurableObjectTransaction(envelope[1]?.[0]?.[1] as TransactionEvent))
46-
.start(signal);
15+
const runner = createRunner(__dirname).start(signal);
16+
17+
// Each hop streams from its own isolate, so the three segment spans of the trace arrive in
18+
// separate envelopes.
19+
const spansPromise = runner.collectStreamedSpans(
20+
spansOfTrace => spansOfTrace.filter(span => span.is_segment).length === 3,
21+
);
4722

4823
await runner.makeRequest('get', '/chain');
49-
await runner.completed();
24+
25+
const spans = await spansPromise;
26+
const chainSpan = spans.find(span => span.is_segment && span.attributes['url.path']?.value === '/chain');
27+
const entrypointSpan = spans.find(span => span.is_segment && span.attributes['url.path']?.value === '/work');
28+
const durableObjectSpan = spans.find(span => span.is_segment && span.attributes['url.path']?.value === '/increment');
29+
30+
expect(getSpanOp(chainSpan!)).toBe('http.server');
31+
expect(chainSpan?.attributes['sentry.origin']).toEqual({ type: 'string', value: 'auto.http.cloudflare' });
32+
33+
expect(getSpanOp(entrypointSpan!)).toBe('http.server');
34+
expect(entrypointSpan?.parent_span_id).toBe(chainSpan?.span_id);
35+
36+
expect(getSpanOp(durableObjectSpan!)).toBe('http.server');
37+
expect(durableObjectSpan?.parent_span_id).toBe(entrypointSpan?.span_id);
38+
39+
// The `auto.db.cloudflare.durable_object` storage pair (`get` + `put`) is the fingerprint of an
40+
// instrumented Durable Object.
41+
expect(
42+
spans
43+
.filter(span => span.parent_span_id === durableObjectSpan?.span_id)
44+
.map(span => ({ name: span.name, op: getSpanOp(span), origin: span.attributes['sentry.origin']?.value })),
45+
).toEqual([
46+
{ name: 'durable_object_storage_get', op: 'db', origin: 'auto.db.cloudflare.durable_object' },
47+
{ name: 'durable_object_storage_put', op: 'db', origin: 'auto.db.cloudflare.durable_object' },
48+
]);
5049
});

‎dev-packages/cloudflare-integration-tests/suites/vite-autoinstrument/combination-entrypoint-do-manual-mixed/index.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ class CounterImpl extends DurableObject<Env> {
3232
// untouched (no double-wrap) while still auto-wrapping the plain entrypoint and
3333
// default export in the same file.
3434
export const Counter = Sentry.instrumentDurableObjectWithSentry(
35-
(env: Env) => ({ dsn: env.SENTRY_DSN, traceLifecycle: 'static', tracesSampleRate: 1.0 }),
35+
(env: Env) => ({ dsn: env.SENTRY_DSN, tracesSampleRate: 1.0 }),
3636
CounterImpl,
3737
);
3838

‎dev-packages/cloudflare-integration-tests/suites/vite-autoinstrument/combination-entrypoint-do-manual-mixed/instrument.server.ts‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,5 @@ import { defineCloudflareOptions } from '@sentry/cloudflare';
22

33
export default defineCloudflareOptions((env: { SENTRY_DSN: string }) => ({
44
dsn: env.SENTRY_DSN,
5-
traceLifecycle: 'static',
65
tracesSampleRate: 1.0,
76
}));
Lines changed: 55 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,69 @@
1-
import type { TransactionEvent } from '@sentry/core';
21
import { expect, it } from 'vitest';
32
import { createRunner } from '../../../runner';
4-
5-
// A Durable Object invoked via `fetch` emits an `http.server` transaction whose
6-
// only children are the two `auto.db.cloudflare.durable_object` storage spans
7-
// (`get` + `put`). Here they come from the manual wrap — the assertion also
8-
// proves the transform did NOT double-wrap (a double-wrap would nest proxies or
9-
// break the build).
10-
function expectDurableObjectTransaction(transactionEvent: TransactionEvent): void {
11-
expect(transactionEvent.contexts?.trace?.op).toBe('http.server');
12-
expect(transactionEvent.contexts?.trace?.origin).toBe('auto.http.cloudflare');
13-
expect(transactionEvent.spans).toEqual([
14-
expect.objectContaining({
15-
op: 'db',
16-
description: 'durable_object_storage_get',
17-
origin: 'auto.db.cloudflare.durable_object',
18-
}),
19-
expect.objectContaining({
20-
op: 'db',
21-
description: 'durable_object_storage_put',
22-
origin: 'auto.db.cloudflare.durable_object',
23-
}),
24-
]);
25-
}
26-
27-
function expectPlainTransaction(name: string) {
28-
return (transactionEvent: TransactionEvent): void => {
29-
expect(transactionEvent.contexts?.trace?.op).toBe('http.server');
30-
expect(transactionEvent.contexts?.trace?.origin).toBe('auto.http.cloudflare');
31-
expect(transactionEvent.transaction).toBe(name);
32-
expect(transactionEvent.spans).toHaveLength(0);
33-
};
34-
}
3+
import { getSpanOp } from '../../../spanUtils';
354

365
// The Durable Object is manually wrapped with
376
// `Sentry.instrumentDurableObjectWithSentry`; the `GreeterEntrypoint` and the
387
// default export are plain. The transform must skip the manual DO (no
398
// double-wrap) yet still auto-wrap the entrypoint and default handler — so the
40-
// manual DO transaction (with storage spans) and both auto-wrapped transactions
41-
// all arrive exactly once.
9+
// manual DO span (with storage children) and both auto-wrapped spans all arrive
10+
// exactly once.
11+
//
12+
// Every route here is a raw URL, so the streamed segment names keep the method
13+
// only and each hop is identified by its `url.path` attribute.
4214
it('leaves a manually wrapped Durable Object untouched while auto-wrapping a sibling WorkerEntrypoint', async ({
4315
signal,
4416
}) => {
45-
const runner = createRunner(__dirname)
46-
.unordered()
47-
.expect(envelope => expectPlainTransaction('GET /call-entrypoint')(envelope[1]?.[0]?.[1] as TransactionEvent))
48-
.expect(envelope => expectPlainTransaction('GET /greet')(envelope[1]?.[0]?.[1] as TransactionEvent))
49-
.expect(envelope => expectPlainTransaction('GET /increment')(envelope[1]?.[0]?.[1] as TransactionEvent))
50-
.expect(envelope => expectDurableObjectTransaction(envelope[1]?.[0]?.[1] as TransactionEvent))
51-
.start(signal);
17+
const runner = createRunner(__dirname).start(signal);
18+
19+
// Each hop streams from its own isolate, so the segment spans of a trace arrive in separate
20+
// envelopes.
21+
const entrypointSpansPromise = runner.collectStreamedSpans(spansOfTrace => {
22+
const paths = spansOfTrace.filter(span => span.is_segment).map(span => span.attributes['url.path']?.value);
23+
return paths.includes('/call-entrypoint') && paths.includes('/greet');
24+
});
5225

5326
await runner.makeRequest('get', '/call-entrypoint');
27+
28+
const entrypointTraceSpans = await entrypointSpansPromise;
29+
const callEntrypointSpan = entrypointTraceSpans.find(
30+
span => span.is_segment && span.attributes['url.path']?.value === '/call-entrypoint',
31+
);
32+
const greetSpan = entrypointTraceSpans.find(
33+
span => span.is_segment && span.attributes['url.path']?.value === '/greet',
34+
);
35+
36+
expect(getSpanOp(callEntrypointSpan!)).toBe('http.server');
37+
expect(callEntrypointSpan?.attributes['sentry.origin']).toEqual({ type: 'string', value: 'auto.http.cloudflare' });
38+
39+
expect(getSpanOp(greetSpan!)).toBe('http.server');
40+
expect(greetSpan?.attributes['sentry.origin']).toEqual({ type: 'string', value: 'auto.http.cloudflare' });
41+
expect(greetSpan?.parent_span_id).toBe(callEntrypointSpan?.span_id);
42+
43+
const durableObjectSpansPromise = runner.collectStreamedSpans(
44+
spansOfTrace =>
45+
spansOfTrace.filter(span => span.is_segment).length === 2 &&
46+
spansOfTrace.some(span => span.attributes['url.path']?.value === '/increment'),
47+
);
48+
5449
await runner.makeRequest('get', '/increment');
55-
await runner.completed();
50+
51+
const durableObjectTraceSpans = await durableObjectSpansPromise;
52+
const workerSpan = durableObjectTraceSpans.find(span => span.is_segment && !span.parent_span_id);
53+
const durableObjectSpan = durableObjectTraceSpans.find(span => span.is_segment && span.parent_span_id);
54+
55+
expect(getSpanOp(workerSpan!)).toBe('http.server');
56+
expect(getSpanOp(durableObjectSpan!)).toBe('http.server');
57+
expect(durableObjectSpan?.parent_span_id).toBe(workerSpan?.span_id);
58+
59+
// The storage pair comes from the manual wrap here. Exactly two of them also rules out a
60+
// double-wrap, which would nest proxies or break the build.
61+
expect(
62+
durableObjectTraceSpans
63+
.filter(span => span.parent_span_id === durableObjectSpan?.span_id)
64+
.map(span => ({ name: span.name, op: getSpanOp(span), origin: span.attributes['sentry.origin']?.value })),
65+
).toEqual([
66+
{ name: 'durable_object_storage_get', op: 'db', origin: 'auto.db.cloudflare.durable_object' },
67+
{ name: 'durable_object_storage_put', op: 'db', origin: 'auto.db.cloudflare.durable_object' },
68+
]);
5669
});

‎dev-packages/cloudflare-integration-tests/suites/vite-autoinstrument/combination-entrypoint-do/instrument.server.ts‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,5 @@ import { defineCloudflareOptions } from '@sentry/cloudflare';
22

33
export default defineCloudflareOptions((env: { SENTRY_DSN: string }) => ({
44
dsn: env.SENTRY_DSN,
5-
traceLifecycle: 'static',
65
tracesSampleRate: 1.0,
76
}));
Lines changed: 56 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,67 @@
1-
import type { TransactionEvent } from '@sentry/core';
21
import { expect, it } from 'vitest';
32
import { createRunner } from '../../../runner';
4-
5-
// A Durable Object invoked via `fetch` emits an `http.server` transaction whose
6-
// only children are the two `auto.db.cloudflare.durable_object` storage spans
7-
// (`get` + `put`) — present only when the class was auto-instrumented.
8-
function expectDurableObjectTransaction(transactionEvent: TransactionEvent): void {
9-
expect(transactionEvent.contexts?.trace?.op).toBe('http.server');
10-
expect(transactionEvent.contexts?.trace?.origin).toBe('auto.http.cloudflare');
11-
expect(transactionEvent.spans).toEqual([
12-
expect.objectContaining({
13-
op: 'db',
14-
description: 'durable_object_storage_get',
15-
origin: 'auto.db.cloudflare.durable_object',
16-
}),
17-
expect.objectContaining({
18-
op: 'db',
19-
description: 'durable_object_storage_put',
20-
origin: 'auto.db.cloudflare.durable_object',
21-
}),
22-
]);
23-
}
24-
25-
// A plain `http.server` transaction with no child spans, identified by its
26-
// transaction name. Used for the two main-worker entries and the entrypoint —
27-
// asserting the name keeps each expectation disjoint under unordered matching.
28-
function expectPlainTransaction(name: string) {
29-
return (transactionEvent: TransactionEvent): void => {
30-
expect(transactionEvent.contexts?.trace?.op).toBe('http.server');
31-
expect(transactionEvent.contexts?.trace?.origin).toBe('auto.http.cloudflare');
32-
expect(transactionEvent.transaction).toBe(name);
33-
expect(transactionEvent.spans).toHaveLength(0);
34-
};
35-
}
3+
import { getSpanOp } from '../../../spanUtils';
364

375
// A single worker exports a plain `WorkerEntrypoint`, a plain `DurableObject`,
386
// and a plain default handler. The runner builds it with the Sentry Vite plugin
39-
// (auto-instrumentation on) and serves the output — so every transaction below
40-
// only arrives if the build-time transform wrapped all three: `withSentry` for
41-
// the default export, the self-bound `GreeterEntrypoint`, and `Counter` via
7+
// (auto-instrumentation on) and serves the output — so every span below only
8+
// arrives if the build-time transform wrapped all three: `withSentry` for the
9+
// default export, the self-bound `GreeterEntrypoint`, and `Counter` via
4210
// `instrumentDurableObjectWithSentry`.
11+
//
12+
// Every route here is a raw URL, so the streamed segment names keep the method
13+
// only and each hop is identified by its `url.path` attribute.
4314
it('auto-instruments a WorkerEntrypoint and a Durable Object exported from the same worker', async ({ signal }) => {
44-
const runner = createRunner(__dirname)
45-
.unordered()
46-
.expect(envelope => expectPlainTransaction('GET /call-entrypoint')(envelope[1]?.[0]?.[1] as TransactionEvent))
47-
.expect(envelope => expectPlainTransaction('GET /greet')(envelope[1]?.[0]?.[1] as TransactionEvent))
48-
.expect(envelope => expectPlainTransaction('GET /increment')(envelope[1]?.[0]?.[1] as TransactionEvent))
49-
.expect(envelope => expectDurableObjectTransaction(envelope[1]?.[0]?.[1] as TransactionEvent))
50-
.start(signal);
15+
const runner = createRunner(__dirname).start(signal);
16+
17+
// Each hop streams from its own isolate, so the segment spans of a trace arrive in separate
18+
// envelopes.
19+
const entrypointSpansPromise = runner.collectStreamedSpans(spansOfTrace => {
20+
const paths = spansOfTrace.filter(span => span.is_segment).map(span => span.attributes['url.path']?.value);
21+
return paths.includes('/call-entrypoint') && paths.includes('/greet');
22+
});
5123

5224
await runner.makeRequest('get', '/call-entrypoint');
25+
26+
const entrypointTraceSpans = await entrypointSpansPromise;
27+
const callEntrypointSpan = entrypointTraceSpans.find(
28+
span => span.is_segment && span.attributes['url.path']?.value === '/call-entrypoint',
29+
);
30+
const greetSpan = entrypointTraceSpans.find(
31+
span => span.is_segment && span.attributes['url.path']?.value === '/greet',
32+
);
33+
34+
expect(getSpanOp(callEntrypointSpan!)).toBe('http.server');
35+
expect(callEntrypointSpan?.attributes['sentry.origin']).toEqual({ type: 'string', value: 'auto.http.cloudflare' });
36+
37+
expect(getSpanOp(greetSpan!)).toBe('http.server');
38+
expect(greetSpan?.attributes['sentry.origin']).toEqual({ type: 'string', value: 'auto.http.cloudflare' });
39+
expect(greetSpan?.parent_span_id).toBe(callEntrypointSpan?.span_id);
40+
41+
const durableObjectSpansPromise = runner.collectStreamedSpans(
42+
spansOfTrace =>
43+
spansOfTrace.filter(span => span.is_segment).length === 2 &&
44+
spansOfTrace.some(span => span.attributes['url.path']?.value === '/increment'),
45+
);
46+
5347
await runner.makeRequest('get', '/increment');
54-
await runner.completed();
48+
49+
const durableObjectTraceSpans = await durableObjectSpansPromise;
50+
const workerSpan = durableObjectTraceSpans.find(span => span.is_segment && !span.parent_span_id);
51+
const durableObjectSpan = durableObjectTraceSpans.find(span => span.is_segment && span.parent_span_id);
52+
53+
expect(getSpanOp(workerSpan!)).toBe('http.server');
54+
expect(getSpanOp(durableObjectSpan!)).toBe('http.server');
55+
expect(durableObjectSpan?.parent_span_id).toBe(workerSpan?.span_id);
56+
57+
// The `auto.db.cloudflare.durable_object` storage pair (`get` + `put`) is the fingerprint of an
58+
// instrumented Durable Object.
59+
expect(
60+
durableObjectTraceSpans
61+
.filter(span => span.parent_span_id === durableObjectSpan?.span_id)
62+
.map(span => ({ name: span.name, op: getSpanOp(span), origin: span.attributes['sentry.origin']?.value })),
63+
).toEqual([
64+
{ name: 'durable_object_storage_get', op: 'db', origin: 'auto.db.cloudflare.durable_object' },
65+
{ name: 'durable_object_storage_put', op: 'db', origin: 'auto.db.cloudflare.durable_object' },
66+
]);
5567
});

‎dev-packages/cloudflare-integration-tests/suites/vite-autoinstrument/default-export/instrument.server.ts‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,5 @@ import { defineCloudflareOptions } from '@sentry/cloudflare';
22

33
export default defineCloudflareOptions((env: { SENTRY_DSN: string }) => ({
44
dsn: env.SENTRY_DSN,
5-
traceLifecycle: 'static',
65
tracesSampleRate: 1.0,
76
}));

‎dev-packages/cloudflare-integration-tests/suites/vite-autoinstrument/default-export/test.ts‎

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
1-
import type { TransactionEvent } from '@sentry/core';
21
import { expect, it } from 'vitest';
32
import { createRunner } from '../../../runner';
3+
import { getSpanOp, getSpansFromEnvelope } from '../../../spanUtils';
44

55
// The worker entry is a plain, unwrapped `export default {...}`. The runner
66
// detects `vite.config.mts`, runs `vite build`, and serves the generated output
7-
// — so this transaction only arrives if the build-time transform wrapped the
8-
// default export with `withSentry`.
7+
// — so this span only arrives if the build-time transform wrapped the default
8+
// export with `withSentry`.
99
it('auto-instruments a plain default-export handler', async ({ signal }) => {
1010
const runner = createRunner(__dirname)
1111
.expect(envelope => {
12-
const transactionEvent = envelope[1]?.[0]?.[1] as TransactionEvent;
13-
expect(transactionEvent.transaction).toBe('GET /hello');
14-
expect(transactionEvent.contexts?.trace?.op).toBe('http.server');
15-
expect(transactionEvent.contexts?.trace?.origin).toBe('auto.http.cloudflare');
12+
const segmentSpan = getSpansFromEnvelope(envelope).find(span => span.is_segment);
13+
14+
// `/hello` is a raw URL, so the streamed segment name keeps the method only.
15+
expect(segmentSpan?.name).toBe('GET');
16+
expect(segmentSpan?.attributes['url.path']).toEqual({ type: 'string', value: '/hello' });
17+
expect(getSpanOp(segmentSpan!)).toBe('http.server');
18+
expect(segmentSpan?.attributes['sentry.origin']).toEqual({ type: 'string', value: 'auto.http.cloudflare' });
1619
})
1720
.start(signal);
1821

‎dev-packages/cloudflare-integration-tests/suites/vite-autoinstrument/durableobject-manual-wrap/index.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class CounterImpl extends DurableObject<Env> {
1919
// leave it untouched — no second wrap — while still wrapping the plain default
2020
// export below.
2121
export const Counter = Sentry.instrumentDurableObjectWithSentry(
22-
(env: Env) => ({ dsn: env.SENTRY_DSN, traceLifecycle: 'static', tracesSampleRate: 1.0 }),
22+
(env: Env) => ({ dsn: env.SENTRY_DSN, tracesSampleRate: 1.0 }),
2323
CounterImpl,
2424
);
2525

0 commit comments

Comments
 (0)