getsentry / getsentry/sentry-javascript

@sentry/cloudflare: Durable Object RPC methods drop metrics, logs and errors unless the caller propagates a trace

クローズ
#24,443 コメント 2 件 リアクション 0 件 担当者 0 名 GitHub で見る
Cloudflare Workers
主要言語
TypeScript
スター
8.7k
フォーク
1.8k
平均マージ
1日 17時間
マージ済み PR(30日)
523

説明

### 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.

コントリビューションガイド

コントリビューションガイドを開く

調査の方向性

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.

索引モデルが issue の本文から書いたものです。

評価

技術スタック
typescript
領域
observability
issue の種類
バグ
難易度
4/5
見積もり時間
3〜5日
活発さ
活発
明瞭さ
おおむね明確
初心者へのやさしさ
63/100

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。