feat: instrument Durable Objects and Workflows with Sentry (lazy import, isolate-local init)#1663
Conversation
Agent-Logs-Url: https://github.com/jaypatrick/adblock-compiler/sessions/a5170972-b60b-406b-8cf7-6b5481dae338 Co-authored-by: jaypatrick <1800595+jaypatrick@users.noreply.github.com>
…blic) Agent-Logs-Url: https://github.com/jaypatrick/adblock-compiler/sessions/a5170972-b60b-406b-8cf7-6b5481dae338 Co-authored-by: jaypatrick <1800595+jaypatrick@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Extends existing Sentry integration in the Cloudflare Worker to also instrument Durable Object classes and Cloudflare Workflow entrypoints, so errors/traces from those runtime entrypoints are captured.
Changes:
- Wrap the 3 Durable Objects with
Sentry.instrumentDurableObjectWithSentryand export the wrapped constructors. - Wrap the 4 Workflows with
Sentry.instrumentWorkflowWithSentryand export the wrapped constructors. - Rename the original exported classes to unexported
*Baseimplementations to keep the wrapped export name stable.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| worker/rate-limiter-do.ts | Renames DO class to RateLimiterDOBase and exports Sentry-instrumented RateLimiterDO. |
| worker/compilation-coordinator.ts | Renames DO class to CompilationCoordinatorBase and exports Sentry-instrumented CompilationCoordinator. |
| worker/ws-hibernation-do.ts | Renames DO class to WsHibernationDOBase and exports Sentry-instrumented WsHibernationDO. |
| worker/workflows/CompilationWorkflow.ts | Renames workflow class to CompilationWorkflowBase and exports Sentry-instrumented CompilationWorkflow. |
| worker/workflows/BatchCompilationWorkflow.ts | Renames workflow class to BatchCompilationWorkflowBase and exports Sentry-instrumented BatchCompilationWorkflow. |
| worker/workflows/CacheWarmingWorkflow.ts | Renames workflow class to CacheWarmingWorkflowBase and exports Sentry-instrumented CacheWarmingWorkflow. |
| worker/workflows/HealthMonitoringWorkflow.ts | Renames workflow class to HealthMonitoringWorkflowBase and exports Sentry-instrumented HealthMonitoringWorkflow. |
|
@copilot Fix CI |
…rrors
The Sentry SDK requires DO classes to satisfy `new(state, env: Env) =>
DurableObject<Env, {}>` (the runtime cloudflare:workers branded class),
but our `implements DurableObject` classes lack `ctx`, `env`, and
`[Rpc.__DURABLE_OBJECT_BRAND]`. Use a double cast:
- Inner: `XyzBase as unknown as new(state, env: Env) => DurableObject<Env, {}>` satisfies Sentry's parameter constraint
- Outer: `as unknown as typeof XyzBase` restores original class type so tests see
non-optional `fetch`/`alarm`/WebSocket methods and `_env: unknown` constructor
(allowing `{}` in test instantiation)
Fixes 3 TypeCheck errors + 123 Test type errors in CI.
Agent-Logs-Url: https://github.com/jaypatrick/adblock-compiler/sessions/50e6d538-3e45-4ef9-b5f5-f466c5308a64
Co-authored-by: jaypatrick <1800595+jaypatrick@users.noreply.github.com>
Applied in 37d1ebf. All 7 files (3 DOs + 4 Workflows) now use a lazy
|
|
@copilot apply changes based on the comments in this thread |
…th Sentry.init() per-isolate and waitUntil in webSocketError Agent-Logs-Url: https://github.com/jaypatrick/adblock-compiler/sessions/44660623-833c-40c1-a91d-4314b4a2420e Co-authored-by: jaypatrick <1800595+jaypatrick@users.noreply.github.com>
…tting per code review Agent-Logs-Url: https://github.com/jaypatrick/adblock-compiler/sessions/44660623-833c-40c1-a91d-4314b4a2420e Co-authored-by: jaypatrick <1800595+jaypatrick@users.noreply.github.com>
Applied in 43a4d45. Changes from the review thread:
|
|
@copilot Fix CI |
…sentry-isolate-init.ts; fix double blank line in ws-hibernation-do.ts Agent-Logs-Url: https://github.com/jaypatrick/adblock-compiler/sessions/4c73bb50-efb0-4f19-91d8-8de5a64879d8 Co-authored-by: jaypatrick <1800595+jaypatrick@users.noreply.github.com>
Fixed in 97e07dc. Two issues resolved:
|
Agent-Logs-Url: https://github.com/jaypatrick/adblock-compiler/sessions/5ca913d3-e1be-4411-8221-2f610ae6ddaf Co-authored-by: jaypatrick <1800595+jaypatrick@users.noreply.github.com>
* Initial plan * feat: add docs for PRs #1663/#1664 + automated schema sync pipeline Agent-Logs-Url: https://github.com/jaypatrick/adblock-compiler/sessions/5ca913d3-e1be-4411-8221-2f610ae6ddaf Co-authored-by: jaypatrick <1800595+jaypatrick@users.noreply.github.com> * refactor: simplify upload step control flow in sync-api-assets Agent-Logs-Url: https://github.com/jaypatrick/adblock-compiler/sessions/5ca913d3-e1be-4411-8221-2f610ae6ddaf Co-authored-by: jaypatrick <1800595+jaypatrick@users.noreply.github.com> * fix: regenerate canonical schema/postman artifacts, fix sync-api-assets string literals Agent-Logs-Url: https://github.com/jaypatrick/adblock-compiler/sessions/eed80bfb-95aa-42ab-ab14-449dca99d3b4 Co-authored-by: jaypatrick <1800595+jaypatrick@users.noreply.github.com> * fix: restore postman-collection.json to canonical pre-PR state (re-adds SSE stream test) Agent-Logs-Url: https://github.com/jaypatrick/adblock-compiler/sessions/cd693534-0d79-46b1-ac7b-6256200d301d Co-authored-by: jaypatrick <1800595+jaypatrick@users.noreply.github.com> * fix: apply all PR review feedback from review thread #4175868887 Agent-Logs-Url: https://github.com/jaypatrick/adblock-compiler/sessions/7bd8b89b-8b07-43b3-9224-bf37ca088a84 Co-authored-by: jaypatrick <1800595+jaypatrick@users.noreply.github.com> * docs: clarify destination_address is optional and add units comment to max_batch_timeout Agent-Logs-Url: https://github.com/jaypatrick/adblock-compiler/sessions/7bd8b89b-8b07-43b3-9224-bf37ca088a84 Co-authored-by: jaypatrick <1800595+jaypatrick@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Jayson Knight <jayson.knight@jaysonknight.com> Co-authored-by: jaypatrick <1800595+jaypatrick@users.noreply.github.com>
Adds Sentry error capture to all three Durable Objects and all four Cloudflare Workflow entrypoints using a shared
captureExceptionInIsolate(env, error)helper in a newworker/services/sentry-isolate-init.tsmodule. The@sentry/cloudflareSDK is only dynamically imported whenSENTRY_DSNis present at runtime, and aCloudflareClientis initialized once per isolate before each capture — zero overhead and no test-environment brittleness when Sentry is disabled.Description
@sentry/cloudflarewas already wired for the main Worker fetch/queue/scheduled handlers viawithSentryWorker. DOs and Workflows were uninstrumented — exceptions from those entrypoints were silently dropped by Sentry, because DOs and Workflows each run in their own isolates, separate from the main worker's Sentry context.Rather than wrapping classes with
instrumentDurableObjectWithSentry/instrumentWorkflowWithSentry(which requires satisfying the Cloudflare runtime's brandedDurableObject<Env, {}>constraint and forces a static SDK import), each file now callscaptureExceptionInIsolate(env, error)from a shared service module. The shared helper lazy-loads the SDK only whenSENTRY_DSNis set, initializes aCloudflareClientonce per isolate vianew Sentry.CloudflareClient()+Sentry.setCurrentClient()(matching the DSN/release/environment config fromsentry-init.ts), and forwards tocaptureException(). Import failures are logged as warnings and the cache is reset so the next invocation can retry, rather than permanently silencing Sentry for the isolate lifetime.Changes
New
worker/services/sentry-isolate-init.ts— sharedcaptureExceptionInIsolate(env, error)helper that:@sentry/cloudflareonly whenSENTRY_DSNis presentCloudflareClientonce per isolate usingnew Sentry.CloudflareClient()+Sentry.setCurrentClient()+client.init()withdsn,release, andenvironmentmatching the main worker config (avoidsSentry.init()which is not exported from@sentry/cloudflare's main index)captureException(error)to forward the exception to SentryDurable Objects —
captureExceptionInIsolateadded in key error paths:worker/rate-limiter-do.ts—captureExceptionInIsolateinalarm()catch blockworker/compilation-coordinator.ts—captureExceptionInIsolateinfetch()catch blockworker/ws-hibernation-do.ts—captureExceptionInIsolateinwebSocketError()viathis.state.waitUntil(...)so WebSocket teardown is not blocked on async module loadWorkflows —
captureExceptionInIsolateadded inrun()catch blocks:worker/workflows/CompilationWorkflow.tsworker/workflows/BatchCompilationWorkflow.tsworker/workflows/CacheWarmingWorkflow.tsworker/workflows/HealthMonitoringWorkflow.tsAll classes remain
export class Foo(not renamed or wrapped), soworker/worker.ts,worker/workflows/index.ts, and all existing tests require no changes.Testing
rate-limiter-do.test.ts,compilation-coordinator.test.ts,ws-hibernation-do.test.ts) instantiate the exported classes directly and pass{}as env; no test changes neededDurableObject<Env, {}>brand-constraint type errors andban-typeslint errors;CloudflareClient+setCurrentClientpattern resolves theTS2339error from the missinginitexportZero Trust Architecture Checklist
Worker / Backend
*) on write/authenticated endpoints — N/A[vars]) —SENTRY_DSNalready a Worker Secret.prepare().bind()(no string interpolation) — N/AFrontend / Angular
CanActivateFnauth guards — N/AlocalStorage) — N/AAPI Shield / Vulnerability Scanner
operationIdinopenapi.yaml— N/Asecurity:annotation — N/A404— N/Acloudflare-schema.yamlregenerated ifopenapi.yamlchanged — N/AOriginal prompt
Goal
Integrate the Sentry Cloudflare SDK's
instrumentDurableObjectWithSentryandinstrumentWorkflowWithSentryhelpers into the existing Durable Object and Workflow classes in theworker/directory.The SDK is already installed (
@sentry/cloudflare10.43.0) and the basewithSentrywrapper on the main Worker fetch handler is already in place inworker/worker.ts+worker/services/sentry-init.ts. This task is only about wrapping the Durable Objects and Workflow entrypoints.API reference (from Sentry docs)
Durable Objects
Workflows
Key notes:
env— useenv.SENTRY_DSNandenv.SENTRY_RELEASEjust like the main worker wrapper does.worker.ts. The base class stays in its own file, unexported (or exported as the base name for testing purposes).tracesSampleRate: 0.1andenvironment: env.ENVIRONMENT ?? 'production'to match the existing worker wrapper pattern.env.SENTRY_DSNis set — theinstrumentDurableObjectWithSentryandinstrumentWorkflowWithSentryfunctions handle this gracefully (they are no-ops when DSN is absent), so you can always wrap unconditionally.Durable Objects to wrap
There are three Durable Object classes exported from
worker/worker.ts:1.
RateLimiterDO—worker/rate-limiter-do.tsCurrently:
export class RateLimiterDO implements DurableObject { ... }Change to:
RateLimiterDOBase(keepimplements DurableObject)worker/worker.ts, theRateLimiterDOre-export at the bottom stays as-is (it now exports the wrapped version automatically since it comes from the same file).2.
CompilationCoordinator—worker/compilation-coordinator.tsCurrently:
export class CompilationCoordinator implements DurableObject { ... }Change to:
CompilationCoordinatorBase3.
WsHibernationDO—worker/ws-hibernation-do.tsApply the same pattern: rename to
WsHibernationDOBase, wrap withinstrumentDurableObjectWithSentry.Workflows to wrap
There are four Workflow classes in
worker/workflows/:CompilationWorkflow—worker/workflows/CompilationWorkflow.tsBatchCompilationWorkflow—worker/workflows/BatchCompilationWorkflow.tsCacheWarmingWorkflow—worker/workflows/CacheWarmingWorkflow.tsHealthMonitoringWorkflow—worker/workflows/HealthMonitoringWorkflow.tsFor each workflow file:
Paramstype it uses.<Name>Base(e.g.CompilationWorkflowBase).instrumentWorkflowWithSentrywrapper and re-export the wrapped constant as the original name (e.g.export const CompilationWorkflow = Sentry.instrumentWorkflowWithSentry(..., CompilationWorkflowBase)).Paramstype comes fromworker/types.ts— use the correct one for each workflow (e.g.CompilationParams,BatchCompilationParams,CacheWarmingParams,HealthMonitoringParams).The config callback for workflows:
worker/workflows/index.ts— no changes neededThe index re-exports by name, s...
This pull request was created from Copilot chat.