getsentry / getsentry/sentry-javascript
@sentry/cloudflare: Durable Object RPC methods drop metrics, logs and errors unless the caller propagates a trace
- Vorherrschende Sprache
- TypeScript
- Sterne
- 8.7k
- Forks
- 1.8k
- Ø Merge
- 1 T. 17 Std.
- Gemergte PRs (30 T.)
- 523
Beschreibung
### Environment
- `@sentry/cloudflare`: 10.74.0 (same logic on `develop` / `11.0.0-beta.2`)
- DO wrapped with `instrumentDurableObjectWithSentry`, `enableRpcTracePropagation: true` (the only
supported way to instrument RPC methods in v11, now that `instrumentPrototypeMethods` is removed)
### Description
`createRpcPrototypeWrapper` only calls `wrapMethodWithSentry` when the last argument carries
`__sentry_rpc_meta__`. Otherwise the original method runs with no client bound. Then:
- `Sentry.metrics.count/gauge/distribution` return early (no client);
- `Sentry.captureException` and `Sentry.logger.*` are dropped;
- no `rpc` span is recorded, and a thrown error is not captured.
Callers send the metadata only if they are Sentry-instrumented, have an active trace, **and** got the stub
through `get`/`getByName` on the instrumented env (see #24442). Common cases that lose everything:
- calls from uninstrumented workers or mixed deployments;
- calls from code running outside the request's async context;
- any call through `jurisdiction()`.
`WorkerEntrypoint` already covers this case: `instrumentWorkerEntrypoint` uses a capture-only wrapper
when the metadata is missing. Durable Objects have no equivalent. `fetch`, `alarm` and `webSocket*` are
always wrapped, so only RPC is affected. That makes the gap easy to miss: our production data had
14 days of DO `alarm` spans and zero DO `rpc` spans.
### Reproduction
```ts
class MyDO extends DurableObject {
async ping() {
Sentry.metrics.count('my_do.ping', 1);
throw new Error('boom');
}
}
export const MyDOInstrumented = Sentry.instrumentDurableObjectWithSentry(
env => ({ dsn: env.SENTRY_DSN, tracesSampleRate: 1, enableRpcTracePropagation: true }),
MyDO,
);
// From a plain (uninstrumented) worker:
await env.MY_DO.get(env.MY_DO.idFromName('x')).ping();
```
Neither the metric nor the error reaches Sentry.
### Expected
External RPC calls without metadata still run inside a client and isolation scope with flush, as
`WorkerEntrypoint` does. Internal `this.method()` calls stay unwrapped.
### Proposed fix
A PR with this change will follow.
Beitragsleitfaden
Rechercherichtung
Start at createRpcPrototypeWrapper and compare its metadata-missing path with instrumentWorkerEntrypoint's capture-only wrapper and wrapMethodWithSentry. Reproduce an external RPC call from the plain worker in the issue, then verify that calls without metadata run with a client and isolation scope, flush metrics and errors, and leave internal this.method() calls unwrapped.
Vom Indexierungsmodell aus dem Issue-Text verfasst.
Bewertung
- Tech-Stack
- typescript
- Bereich
- observability
- Issue-Typ
- Bug
- Schwierigkeit
- 4/5
- Geschätzter Aufwand
- 3-5 Tage
- Aktivitätsstatus
- Aktiv
- Klarheit
- Größtenteils klar
- Anfängerfreundlichkeit
- 63/100