@sentry/cloudflare 11.0.0-alpha.2 → 11.0.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/cjs/dev/client.js +2 -1
- package/build/cjs/dev/client.js.map +1 -1
- package/build/cjs/dev/durableobject.js +3 -77
- package/build/cjs/dev/durableobject.js.map +1 -1
- package/build/cjs/dev/index.js +5 -4
- package/build/cjs/dev/index.js.map +1 -1
- package/build/cjs/dev/instrumentations/agents/instrumentAgentCallableRpc.js +3 -1
- package/build/cjs/dev/instrumentations/agents/instrumentAgentCallableRpc.js.map +1 -1
- package/build/cjs/dev/instrumentations/instrumentDurableObjectHandlers.js +123 -0
- package/build/cjs/dev/instrumentations/instrumentDurableObjectHandlers.js.map +1 -0
- package/build/cjs/dev/instrumentations/instrumentDurableObjectStorage.js +3 -1
- package/build/cjs/dev/instrumentations/instrumentDurableObjectStorage.js.map +1 -1
- package/build/cjs/dev/instrumentations/instrumentDurableObjectSyncKvStorage.js +3 -1
- package/build/cjs/dev/instrumentations/instrumentDurableObjectSyncKvStorage.js.map +1 -1
- package/build/cjs/dev/instrumentations/instrumentSqlStorage.js +6 -3
- package/build/cjs/dev/instrumentations/instrumentSqlStorage.js.map +1 -1
- package/build/cjs/dev/instrumentations/instrumentWorkerEntrypoint.js +2 -1
- package/build/cjs/dev/instrumentations/instrumentWorkerEntrypoint.js.map +1 -1
- package/build/cjs/dev/instrumentations/worker/instrumentD1.js +6 -3
- package/build/cjs/dev/instrumentations/worker/instrumentD1.js.map +1 -1
- package/build/cjs/dev/instrumentations/worker/instrumentEnv.js +0 -7
- package/build/cjs/dev/instrumentations/worker/instrumentEnv.js.map +1 -1
- package/build/cjs/dev/instrumentations/worker/instrumentQueueProducer.js +3 -2
- package/build/cjs/dev/instrumentations/worker/instrumentQueueProducer.js.map +1 -1
- package/build/cjs/dev/sdk.js +0 -1
- package/build/cjs/dev/sdk.js.map +1 -1
- package/build/cjs/dev/utils/isBinding.js +0 -4
- package/build/cjs/dev/utils/isBinding.js.map +1 -1
- package/build/cjs/dev/vendor/stacktrace.js +2 -1
- package/build/cjs/dev/vendor/stacktrace.js.map +1 -1
- package/build/cjs/dev/wrapRequestHandlerWithInit.js +2 -1
- package/build/cjs/dev/wrapRequestHandlerWithInit.js.map +1 -1
- package/build/cjs/prod/client.js +2 -1
- package/build/cjs/prod/client.js.map +1 -1
- package/build/cjs/prod/durableobject.js +3 -77
- package/build/cjs/prod/durableobject.js.map +1 -1
- package/build/cjs/prod/index.js +5 -4
- package/build/cjs/prod/index.js.map +1 -1
- package/build/cjs/prod/instrumentations/agents/instrumentAgentCallableRpc.js +3 -1
- package/build/cjs/prod/instrumentations/agents/instrumentAgentCallableRpc.js.map +1 -1
- package/build/cjs/prod/instrumentations/instrumentDurableObjectHandlers.js +123 -0
- package/build/cjs/prod/instrumentations/instrumentDurableObjectHandlers.js.map +1 -0
- package/build/cjs/prod/instrumentations/instrumentDurableObjectStorage.js +3 -1
- package/build/cjs/prod/instrumentations/instrumentDurableObjectStorage.js.map +1 -1
- package/build/cjs/prod/instrumentations/instrumentDurableObjectSyncKvStorage.js +3 -1
- package/build/cjs/prod/instrumentations/instrumentDurableObjectSyncKvStorage.js.map +1 -1
- package/build/cjs/prod/instrumentations/instrumentSqlStorage.js +6 -3
- package/build/cjs/prod/instrumentations/instrumentSqlStorage.js.map +1 -1
- package/build/cjs/prod/instrumentations/instrumentWorkerEntrypoint.js +2 -1
- package/build/cjs/prod/instrumentations/instrumentWorkerEntrypoint.js.map +1 -1
- package/build/cjs/prod/instrumentations/worker/instrumentD1.js +6 -3
- package/build/cjs/prod/instrumentations/worker/instrumentD1.js.map +1 -1
- package/build/cjs/prod/instrumentations/worker/instrumentEnv.js +0 -7
- package/build/cjs/prod/instrumentations/worker/instrumentEnv.js.map +1 -1
- package/build/cjs/prod/instrumentations/worker/instrumentQueueProducer.js +3 -2
- package/build/cjs/prod/instrumentations/worker/instrumentQueueProducer.js.map +1 -1
- package/build/cjs/prod/sdk.js +0 -1
- package/build/cjs/prod/sdk.js.map +1 -1
- package/build/cjs/prod/utils/isBinding.js +0 -4
- package/build/cjs/prod/utils/isBinding.js.map +1 -1
- package/build/cjs/prod/vendor/stacktrace.js +2 -1
- package/build/cjs/prod/vendor/stacktrace.js.map +1 -1
- package/build/cjs/prod/wrapRequestHandlerWithInit.js +2 -1
- package/build/cjs/prod/wrapRequestHandlerWithInit.js.map +1 -1
- package/build/esm/dev/client.js +2 -1
- package/build/esm/dev/client.js.map +1 -1
- package/build/esm/dev/durableobject.js +5 -79
- package/build/esm/dev/durableobject.js.map +1 -1
- package/build/esm/dev/index.js +3 -2
- package/build/esm/dev/index.js.map +1 -1
- package/build/esm/dev/instrumentations/agents/instrumentAgentCallableRpc.js +4 -2
- package/build/esm/dev/instrumentations/agents/instrumentAgentCallableRpc.js.map +1 -1
- package/build/esm/dev/instrumentations/instrumentDurableObjectHandlers.js +121 -0
- package/build/esm/dev/instrumentations/instrumentDurableObjectHandlers.js.map +1 -0
- package/build/esm/dev/instrumentations/instrumentDurableObjectStorage.js +3 -1
- package/build/esm/dev/instrumentations/instrumentDurableObjectStorage.js.map +1 -1
- package/build/esm/dev/instrumentations/instrumentDurableObjectSyncKvStorage.js +3 -1
- package/build/esm/dev/instrumentations/instrumentDurableObjectSyncKvStorage.js.map +1 -1
- package/build/esm/dev/instrumentations/instrumentSqlStorage.js +5 -2
- package/build/esm/dev/instrumentations/instrumentSqlStorage.js.map +1 -1
- package/build/esm/dev/instrumentations/instrumentWorkerEntrypoint.js +2 -1
- package/build/esm/dev/instrumentations/instrumentWorkerEntrypoint.js.map +1 -1
- package/build/esm/dev/instrumentations/worker/instrumentD1.js +6 -3
- package/build/esm/dev/instrumentations/worker/instrumentD1.js.map +1 -1
- package/build/esm/dev/instrumentations/worker/instrumentEnv.js +1 -8
- package/build/esm/dev/instrumentations/worker/instrumentEnv.js.map +1 -1
- package/build/esm/dev/instrumentations/worker/instrumentQueueProducer.js +4 -3
- package/build/esm/dev/instrumentations/worker/instrumentQueueProducer.js.map +1 -1
- package/build/esm/dev/package.json +1 -1
- package/build/esm/dev/sdk.js +0 -1
- package/build/esm/dev/sdk.js.map +1 -1
- package/build/esm/dev/utils/isBinding.js +1 -4
- package/build/esm/dev/utils/isBinding.js.map +1 -1
- package/build/esm/dev/vendor/stacktrace.js +2 -1
- package/build/esm/dev/vendor/stacktrace.js.map +1 -1
- package/build/esm/dev/wrapRequestHandlerWithInit.js +4 -3
- package/build/esm/dev/wrapRequestHandlerWithInit.js.map +1 -1
- package/build/esm/prod/client.js +2 -1
- package/build/esm/prod/client.js.map +1 -1
- package/build/esm/prod/durableobject.js +5 -79
- package/build/esm/prod/durableobject.js.map +1 -1
- package/build/esm/prod/index.js +3 -2
- package/build/esm/prod/index.js.map +1 -1
- package/build/esm/prod/instrumentations/agents/instrumentAgentCallableRpc.js +4 -2
- package/build/esm/prod/instrumentations/agents/instrumentAgentCallableRpc.js.map +1 -1
- package/build/esm/prod/instrumentations/instrumentDurableObjectHandlers.js +121 -0
- package/build/esm/prod/instrumentations/instrumentDurableObjectHandlers.js.map +1 -0
- package/build/esm/prod/instrumentations/instrumentDurableObjectStorage.js +3 -1
- package/build/esm/prod/instrumentations/instrumentDurableObjectStorage.js.map +1 -1
- package/build/esm/prod/instrumentations/instrumentDurableObjectSyncKvStorage.js +3 -1
- package/build/esm/prod/instrumentations/instrumentDurableObjectSyncKvStorage.js.map +1 -1
- package/build/esm/prod/instrumentations/instrumentSqlStorage.js +5 -2
- package/build/esm/prod/instrumentations/instrumentSqlStorage.js.map +1 -1
- package/build/esm/prod/instrumentations/instrumentWorkerEntrypoint.js +2 -1
- package/build/esm/prod/instrumentations/instrumentWorkerEntrypoint.js.map +1 -1
- package/build/esm/prod/instrumentations/worker/instrumentD1.js +6 -3
- package/build/esm/prod/instrumentations/worker/instrumentD1.js.map +1 -1
- package/build/esm/prod/instrumentations/worker/instrumentEnv.js +1 -8
- package/build/esm/prod/instrumentations/worker/instrumentEnv.js.map +1 -1
- package/build/esm/prod/instrumentations/worker/instrumentQueueProducer.js +4 -3
- package/build/esm/prod/instrumentations/worker/instrumentQueueProducer.js.map +1 -1
- package/build/esm/prod/package.json +1 -1
- package/build/esm/prod/sdk.js +0 -1
- package/build/esm/prod/sdk.js.map +1 -1
- package/build/esm/prod/utils/isBinding.js +1 -4
- package/build/esm/prod/utils/isBinding.js.map +1 -1
- package/build/esm/prod/vendor/stacktrace.js +2 -1
- package/build/esm/prod/vendor/stacktrace.js.map +1 -1
- package/build/esm/prod/wrapRequestHandlerWithInit.js +4 -3
- package/build/esm/prod/wrapRequestHandlerWithInit.js.map +1 -1
- package/build/types/client.d.ts +1 -1
- package/build/types/client.d.ts.map +1 -1
- package/build/types/durableobject.d.ts.map +1 -1
- package/build/types/index.d.ts +3 -2
- package/build/types/index.d.ts.map +1 -1
- package/build/types/instrumentations/agents/instrumentAgentCallableRpc.d.ts.map +1 -1
- package/build/types/instrumentations/instrumentDurableObjectHandlers.d.ts +28 -0
- package/build/types/instrumentations/instrumentDurableObjectHandlers.d.ts.map +1 -0
- package/build/types/instrumentations/instrumentDurableObjectStorage.d.ts.map +1 -1
- package/build/types/instrumentations/instrumentDurableObjectSyncKvStorage.d.ts.map +1 -1
- package/build/types/instrumentations/instrumentSqlStorage.d.ts.map +1 -1
- package/build/types/instrumentations/instrumentWorkerEntrypoint.d.ts.map +1 -1
- package/build/types/instrumentations/worker/instrumentD1.d.ts.map +1 -1
- package/build/types/instrumentations/worker/instrumentEnv.d.ts +0 -1
- package/build/types/instrumentations/worker/instrumentEnv.d.ts.map +1 -1
- package/build/types/instrumentations/worker/instrumentQueueProducer.d.ts.map +1 -1
- package/build/types/utils/isBinding.d.ts +1 -9
- package/build/types/utils/isBinding.d.ts.map +1 -1
- package/build/types/vendor/stacktrace.d.ts.map +1 -1
- package/build/types/wrapRequestHandlerWithInit.d.ts.map +1 -1
- package/package.json +4 -4
- package/build/cjs/dev/instrumentations/worker/instrumentRateLimit.js +0 -34
- package/build/cjs/dev/instrumentations/worker/instrumentRateLimit.js.map +0 -1
- package/build/cjs/prod/instrumentations/worker/instrumentRateLimit.js +0 -34
- package/build/cjs/prod/instrumentations/worker/instrumentRateLimit.js.map +0 -1
- package/build/esm/dev/instrumentations/worker/instrumentRateLimit.js +0 -32
- package/build/esm/dev/instrumentations/worker/instrumentRateLimit.js.map +0 -1
- package/build/esm/prod/instrumentations/worker/instrumentRateLimit.js +0 -32
- package/build/esm/prod/instrumentations/worker/instrumentRateLimit.js.map +0 -1
- package/build/types/instrumentations/worker/instrumentRateLimit.d.ts +0 -6
- package/build/types/instrumentations/worker/instrumentRateLimit.d.ts.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instrumentAgentCallableRpc.js","sources":["../../../../../src/instrumentations/agents/instrumentAgentCallableRpc.ts"],"sourcesContent":["import { debug,
|
|
1
|
+
{"version":3,"file":"instrumentAgentCallableRpc.js","sources":["../../../../../src/instrumentations/agents/instrumentAgentCallableRpc.ts"],"sourcesContent":["import { SENTRY_OP } from '@sentry/conventions/attributes';\nimport { RPC } from '@sentry/conventions/op';\nimport { debug, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, startSpan } from '@sentry/core';\nimport { DEBUG_BUILD } from '../../debug-build';\nimport { AGENT_SPAN_ORIGIN, type AgentInternals, getAgentAttributes, setAgentConversationId } from './types';\n\n/**\n * Wraps the Agent's `onMessage` handler to create a span for each `@callable()` RPC invocation.\n * RPC requests arrive as WebSocket messages, so this span nests under the active transaction for\n * the WebSocket message (on Cloudflare, the instrumented Durable Object `webSocketMessage` hook).\n *\n * Also sets the conversation id on the scope for the duration of the call: callable methods are the\n * unit of work for plain (non-chat) agents, which run LLM calls just like chat turns do. It is\n * awaited so the id is on the scope before the method body creates any span; the `agents`\n * `onMessage` own property this wraps is already `async`, so the promise return is nothing new to\n * the WebSocket dispatch upstream.\n */\nexport function instrumentAgentCallableRpc(obj: AgentInternals): void {\n const original = obj.onMessage;\n if (typeof original !== 'function') {\n DEBUG_BUILD && debug.log('[Sentry] Agent `onMessage` not found — callable RPC span instrumentation skipped.');\n return;\n }\n\n obj.onMessage = new Proxy(original, {\n apply(target, thisArg: AgentInternals, args: unknown[]): unknown {\n const method = extractCallableMethod(args[1]);\n\n if (!method) {\n return Reflect.apply(target, thisArg, args);\n }\n\n return startSpan(\n {\n name: method,\n attributes: {\n [SENTRY_OP]: RPC,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: AGENT_SPAN_ORIGIN,\n ...getAgentAttributes(thisArg),\n },\n },\n async () => {\n await setAgentConversationId(thisArg);\n return Reflect.apply(target, thisArg, args);\n },\n );\n },\n });\n}\n\n/** Extracts the RPC method name from a WebSocket message, mirroring the SDK's `isRPCRequest`. */\nfunction extractCallableMethod(message: unknown): string | undefined {\n const text =\n typeof message === 'string'\n ? message\n : message instanceof ArrayBuffer\n ? new TextDecoder().decode(message)\n : undefined;\n\n if (!text) {\n return undefined;\n }\n\n try {\n const parsed = JSON.parse(text) as { type?: unknown; method?: unknown; args?: unknown };\n if (parsed.type === 'rpc' && typeof parsed.method === 'string' && Array.isArray(parsed.args)) {\n return parsed.method;\n }\n } catch {\n // Not JSON, or not an RPC request — no span.\n }\n\n return undefined;\n}\n"],"names":[],"mappings":";;;;;;AAiBO,SAAS,2BAA2B,GAAA,EAA2B;AACpE,EAAA,MAAM,WAAW,GAAA,CAAI,SAAA;AACrB,EAAA,IAAI,OAAO,aAAa,UAAA,EAAY;AAClC,IAAA,WAAA,IAAe,KAAA,CAAM,IAAI,wFAAmF,CAAA;AAC5G,IAAA;AAAA,EACF;AAEA,EAAA,GAAA,CAAI,SAAA,GAAY,IAAI,KAAA,CAAM,QAAA,EAAU;AAAA,IAClC,KAAA,CAAM,MAAA,EAAQ,OAAA,EAAyB,IAAA,EAA0B;AAC/D,MAAA,MAAM,MAAA,GAAS,qBAAA,CAAsB,IAAA,CAAK,CAAC,CAAC,CAAA;AAE5C,MAAA,IAAI,CAAC,MAAA,EAAQ;AACX,QAAA,OAAO,OAAA,CAAQ,KAAA,CAAM,MAAA,EAAQ,OAAA,EAAS,IAAI,CAAA;AAAA,MAC5C;AAEA,MAAA,OAAO,SAAA;AAAA,QACL;AAAA,UACE,IAAA,EAAM,MAAA;AAAA,UACN,UAAA,EAAY;AAAA,YACV,CAAC,SAAS,GAAG,GAAA;AAAA,YACb,CAAC,gCAAgC,GAAG,iBAAA;AAAA,YACpC,GAAG,mBAAmB,OAAO;AAAA;AAC/B,SACF;AAAA,QACA,YAAY;AACV,UAAA,MAAM,uBAAuB,OAAO,CAAA;AACpC,UAAA,OAAO,OAAA,CAAQ,KAAA,CAAM,MAAA,EAAQ,OAAA,EAAS,IAAI,CAAA;AAAA,QAC5C;AAAA,OACF;AAAA,IACF;AAAA,GACD,CAAA;AACH;AAGA,SAAS,sBAAsB,OAAA,EAAsC;AACnE,EAAA,MAAM,IAAA,GACJ,OAAO,OAAA,KAAY,QAAA,GACf,OAAA,GACA,OAAA,YAAmB,WAAA,GACjB,IAAI,WAAA,EAAY,CAAE,MAAA,CAAO,OAAO,CAAA,GAChC,MAAA;AAER,EAAA,IAAI,CAAC,IAAA,EAAM;AACT,IAAA,OAAO,MAAA;AAAA,EACT;AAEA,EAAA,IAAI;AACF,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,KAAA,CAAM,IAAI,CAAA;AAC9B,IAAA,IAAI,MAAA,CAAO,IAAA,KAAS,KAAA,IAAS,OAAO,MAAA,CAAO,MAAA,KAAW,QAAA,IAAY,KAAA,CAAM,OAAA,CAAQ,MAAA,CAAO,IAAI,CAAA,EAAG;AAC5F,MAAA,OAAO,MAAA,CAAO,MAAA;AAAA,IAChB;AAAA,EACF,CAAA,CAAA,MAAQ;AAAA,EAER;AAEA,EAAA,OAAO,MAAA;AACT;;;;"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { FUNCTION, WEBSOCKET } from '@sentry/conventions/op';
|
|
2
|
+
import { debug, captureException } from '@sentry/core';
|
|
3
|
+
import { DEBUG_BUILD } from '../debug-build.js';
|
|
4
|
+
import { ensureInstrumented } from '../instrument.js';
|
|
5
|
+
import { init } from '../sdk.js';
|
|
6
|
+
import { wrapMethodWithSentry } from '../wrapMethodWithSentry.js';
|
|
7
|
+
import { wrapRequestHandlerWithInit } from '../wrapRequestHandlerWithInit.js';
|
|
8
|
+
|
|
9
|
+
function instrumentDurableObjectHandlers(obj, options, context) {
|
|
10
|
+
if (obj.fetch && typeof obj.fetch === "function") {
|
|
11
|
+
setInstanceHandler(
|
|
12
|
+
obj,
|
|
13
|
+
"fetch",
|
|
14
|
+
ensureInstrumented(
|
|
15
|
+
obj.fetch.bind(obj),
|
|
16
|
+
(original) => new Proxy(original, {
|
|
17
|
+
apply(target, thisArg, args) {
|
|
18
|
+
return wrapRequestHandlerWithInit(
|
|
19
|
+
{ options, request: args[0], context },
|
|
20
|
+
() => {
|
|
21
|
+
return Reflect.apply(target, thisArg, args);
|
|
22
|
+
},
|
|
23
|
+
init
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
})
|
|
27
|
+
)
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
if (obj.alarm && typeof obj.alarm === "function") {
|
|
31
|
+
setInstanceHandler(
|
|
32
|
+
obj,
|
|
33
|
+
"alarm",
|
|
34
|
+
wrapMethodWithSentry(
|
|
35
|
+
{
|
|
36
|
+
options,
|
|
37
|
+
context,
|
|
38
|
+
spanName: "alarm",
|
|
39
|
+
spanOp: FUNCTION,
|
|
40
|
+
startNewTrace: true,
|
|
41
|
+
origin: "auto.faas.cloudflare.durable_object"
|
|
42
|
+
},
|
|
43
|
+
obj.alarm.bind(obj)
|
|
44
|
+
)
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
if (obj.webSocketMessage && typeof obj.webSocketMessage === "function") {
|
|
48
|
+
setInstanceHandler(
|
|
49
|
+
obj,
|
|
50
|
+
"webSocketMessage",
|
|
51
|
+
wrapMethodWithSentry(
|
|
52
|
+
{
|
|
53
|
+
options,
|
|
54
|
+
context,
|
|
55
|
+
spanName: "webSocketMessage",
|
|
56
|
+
spanOp: WEBSOCKET,
|
|
57
|
+
origin: "auto.faas.cloudflare.durable_object"
|
|
58
|
+
},
|
|
59
|
+
obj.webSocketMessage.bind(obj)
|
|
60
|
+
)
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
if (obj.webSocketClose && typeof obj.webSocketClose === "function") {
|
|
64
|
+
setInstanceHandler(
|
|
65
|
+
obj,
|
|
66
|
+
"webSocketClose",
|
|
67
|
+
wrapMethodWithSentry(
|
|
68
|
+
{
|
|
69
|
+
options,
|
|
70
|
+
context,
|
|
71
|
+
spanName: "webSocketClose",
|
|
72
|
+
spanOp: WEBSOCKET,
|
|
73
|
+
origin: "auto.faas.cloudflare.durable_object"
|
|
74
|
+
},
|
|
75
|
+
obj.webSocketClose.bind(obj)
|
|
76
|
+
)
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
if (obj.webSocketError && typeof obj.webSocketError === "function") {
|
|
80
|
+
setInstanceHandler(
|
|
81
|
+
obj,
|
|
82
|
+
"webSocketError",
|
|
83
|
+
wrapMethodWithSentry(
|
|
84
|
+
{
|
|
85
|
+
options,
|
|
86
|
+
context,
|
|
87
|
+
spanName: "webSocketError",
|
|
88
|
+
spanOp: WEBSOCKET,
|
|
89
|
+
origin: "auto.faas.cloudflare.durable_object"
|
|
90
|
+
},
|
|
91
|
+
obj.webSocketError.bind(obj),
|
|
92
|
+
(_, error) => captureException(error, {
|
|
93
|
+
mechanism: {
|
|
94
|
+
type: "auto.faas.cloudflare.durable_object_websocket",
|
|
95
|
+
handled: false
|
|
96
|
+
}
|
|
97
|
+
})
|
|
98
|
+
)
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
function setInstanceHandler(obj, name, handler) {
|
|
103
|
+
const descriptor = Object.getOwnPropertyDescriptor(obj, name);
|
|
104
|
+
try {
|
|
105
|
+
if (descriptor?.writable === false) {
|
|
106
|
+
Object.defineProperty(obj, name, {
|
|
107
|
+
value: handler,
|
|
108
|
+
writable: true,
|
|
109
|
+
enumerable: descriptor.enumerable,
|
|
110
|
+
configurable: descriptor.configurable
|
|
111
|
+
});
|
|
112
|
+
} else {
|
|
113
|
+
obj[name] = handler;
|
|
114
|
+
}
|
|
115
|
+
} catch (error) {
|
|
116
|
+
DEBUG_BUILD && debug.warn(`Failed to instrument Durable Object handler "${name}"`, error);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export { instrumentDurableObjectHandlers };
|
|
121
|
+
//# sourceMappingURL=instrumentDurableObjectHandlers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instrumentDurableObjectHandlers.js","sources":["../../../../src/instrumentations/instrumentDurableObjectHandlers.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/unbound-method */\nimport { FUNCTION, WEBSOCKET } from '@sentry/conventions/op';\nimport { captureException, debug } from '@sentry/core';\nimport type { DurableObject } from 'cloudflare:workers';\nimport type { CloudflareOptions } from '../client';\nimport { DEBUG_BUILD } from '../debug-build';\nimport { ensureInstrumented } from '../instrument';\nimport { init } from '../sdk';\nimport { wrapMethodWithSentry } from '../wrapMethodWithSentry';\nimport { wrapRequestHandlerWithInit } from '../wrapRequestHandlerWithInit';\n\n/**\n * The instrumented context of the Durable Object being wrapped.\n *\n * Kept as `any` for the same reason as in `durableobject.ts`: a concrete `DurableObjectState` here\n * makes `tsc` relate its `SqlStorage` graph against the parameter union of `wrapMethodWithSentry`,\n * which hangs the type build.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype InstrumentedDurableObjectContext = any;\n\n/**\n * Instruments the built-in Durable Object handler methods on a constructed instance.\n *\n * These are the methods that are available on a Durable Object\n * ref: https://developers.cloudflare.com/durable-objects/api/base/\n * - obj.alarm\n * - obj.fetch\n * - obj.webSocketError\n * - obj.webSocketClose\n * - obj.webSocketMessage\n *\n * Any other public methods on the Durable Object instance are RPC calls.\n *\n * @internal\n */\nexport function instrumentDurableObjectHandlers<E, T extends DurableObject<E>>(\n obj: T,\n options: CloudflareOptions,\n context: InstrumentedDurableObjectContext,\n): void {\n // Bind each built-in handler to this instance before wrapping.\n // See https://github.com/getsentry/sentry-javascript/issues/22328\n if (obj.fetch && typeof obj.fetch === 'function') {\n setInstanceHandler(\n obj,\n 'fetch',\n ensureInstrumented(\n obj.fetch.bind(obj),\n original =>\n new Proxy(original, {\n apply(target, thisArg, args) {\n return wrapRequestHandlerWithInit(\n { options, request: args[0], context },\n () => {\n return Reflect.apply(target, thisArg, args);\n },\n init,\n );\n },\n }),\n ),\n );\n }\n\n if (obj.alarm && typeof obj.alarm === 'function') {\n // Alarms are independent invocations, so we start a new trace and link to the previous alarm\n setInstanceHandler(\n obj,\n 'alarm',\n wrapMethodWithSentry(\n {\n options,\n context,\n spanName: 'alarm',\n spanOp: FUNCTION,\n startNewTrace: true,\n origin: 'auto.faas.cloudflare.durable_object',\n },\n obj.alarm.bind(obj),\n ),\n );\n }\n\n if (obj.webSocketMessage && typeof obj.webSocketMessage === 'function') {\n setInstanceHandler(\n obj,\n 'webSocketMessage',\n wrapMethodWithSentry(\n {\n options,\n context,\n spanName: 'webSocketMessage',\n spanOp: WEBSOCKET,\n origin: 'auto.faas.cloudflare.durable_object',\n },\n obj.webSocketMessage.bind(obj),\n ),\n );\n }\n\n if (obj.webSocketClose && typeof obj.webSocketClose === 'function') {\n setInstanceHandler(\n obj,\n 'webSocketClose',\n wrapMethodWithSentry(\n {\n options,\n context,\n spanName: 'webSocketClose',\n spanOp: WEBSOCKET,\n origin: 'auto.faas.cloudflare.durable_object',\n },\n obj.webSocketClose.bind(obj),\n ),\n );\n }\n\n if (obj.webSocketError && typeof obj.webSocketError === 'function') {\n setInstanceHandler(\n obj,\n 'webSocketError',\n wrapMethodWithSentry(\n {\n options,\n context,\n spanName: 'webSocketError',\n spanOp: WEBSOCKET,\n origin: 'auto.faas.cloudflare.durable_object',\n },\n obj.webSocketError.bind(obj),\n (_, error) =>\n captureException(error, {\n mechanism: {\n type: 'auto.faas.cloudflare.durable_object_websocket',\n handled: false,\n },\n }),\n ),\n );\n }\n}\n\n/**\n * Installs an instrumented handler as an own property of the Durable Object instance.\n *\n * A plain assignment is not always possible. The `agents` package installs its handlers with\n * `Object.defineProperty(instance, name, { value, configurable: true })`, and `defineProperty`\n * leaves `writable` at `false`. Assigning to such a property throws a `TypeError` in strict mode,\n * so a read-only property is redefined instead. When the property can be neither assigned nor\n * redefined, the handler stays uninstrumented rather than breaking the object.\n */\nfunction setInstanceHandler(obj: object, name: string, handler: unknown): void {\n const descriptor = Object.getOwnPropertyDescriptor(obj, name);\n\n try {\n if (descriptor?.writable === false) {\n Object.defineProperty(obj, name, {\n value: handler,\n writable: true,\n enumerable: descriptor.enumerable,\n configurable: descriptor.configurable,\n });\n } else {\n (obj as Record<string, unknown>)[name] = handler;\n }\n } catch (error) {\n DEBUG_BUILD && debug.warn(`Failed to instrument Durable Object handler \"${name}\"`, error);\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { SENTRY_OP } from '@sentry/conventions/attributes';
|
|
2
|
+
import { DB } from '@sentry/conventions/op';
|
|
1
3
|
import { getClient, startSpan, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, isThenable } from '@sentry/core';
|
|
2
4
|
import { getStorageKeys, targetsCloudflareInternalKey } from '../utils/internalStorageKey.js';
|
|
3
5
|
import { storeSpanContext } from '../utils/traceLinks.js';
|
|
@@ -32,8 +34,8 @@ function instrumentDurableObjectStorage(storage, waitUntil) {
|
|
|
32
34
|
{
|
|
33
35
|
// Use underscore naming to match Cloudflare's native instrumentation (e.g., "durable_object_storage_get")
|
|
34
36
|
name: `durable_object_storage_${methodName}`,
|
|
35
|
-
op: "db",
|
|
36
37
|
attributes: {
|
|
38
|
+
[SENTRY_OP]: DB,
|
|
37
39
|
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.db.cloudflare.durable_object",
|
|
38
40
|
"db.system.name": "cloudflare.durable_object.storage",
|
|
39
41
|
"db.operation.name": methodName
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instrumentDurableObjectStorage.js","sources":["../../../../src/instrumentations/instrumentDurableObjectStorage.ts"],"sourcesContent":["import type { DurableObjectStorage, SyncKvStorage, SqlStorage } from '@cloudflare/workers-types';\nimport { getClient, isThenable, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, startSpan } from '@sentry/core';\nimport type { CloudflareClientOptions } from '../client';\nimport { getStorageKeys, targetsCloudflareInternalKey } from '../utils/internalStorageKey';\nimport { storeSpanContext } from '../utils/traceLinks';\nimport { instrumentDurableObjectSyncKvStorage } from './instrumentDurableObjectSyncKvStorage';\nimport { instrumentSqlStorage } from './instrumentSqlStorage';\n\nconst STORAGE_METHODS_TO_INSTRUMENT = ['get', 'put', 'delete', 'list', 'setAlarm', 'getAlarm', 'deleteAlarm'] as const;\n\ntype StorageMethod = (typeof STORAGE_METHODS_TO_INSTRUMENT)[number];\n\ntype WaitUntil = (promise: Promise<unknown>) => void;\n\n/**\n * Instruments DurableObjectStorage methods with Sentry spans.\n *\n * Wraps the following async methods:\n * - get, put, delete, list (KV API)\n * - setAlarm, getAlarm, deleteAlarm (Alarm API)\n *\n * When setAlarm is called, it also stores the current span context so that when\n * the alarm fires later, it can link back to the trace that called setAlarm.\n *\n * @param storage - The DurableObjectStorage instance to instrument\n * @param waitUntil - Optional waitUntil function to defer span context storage\n * @returns An instrumented DurableObjectStorage instance\n */\nexport function instrumentDurableObjectStorage(\n storage: DurableObjectStorage,\n waitUntil?: WaitUntil,\n): DurableObjectStorage {\n return new Proxy(storage, {\n get(target, prop, _receiver) {\n // Use `target` as the receiver instead of the proxy (`_receiver`).\n // Native workerd getters (e.g., `storage.sql`) validate `this` via\n // internal slots. Passing the proxy as receiver breaks that check,\n // causing \"Illegal invocation: function called with incorrect `this`\n // reference\" errors.\n const original = Reflect.get(target, prop, target);\n\n if (prop === 'kv' && original != null && 'get' in original && 'put' in original) {\n return instrumentDurableObjectSyncKvStorage(original as SyncKvStorage);\n }\n\n if (prop === 'sql' && original != null && 'databaseSize' in original && 'exec' in original) {\n return instrumentSqlStorage(original as SqlStorage);\n }\n\n if (typeof original !== 'function') {\n return original;\n }\n\n const methodName = prop as string;\n if (!STORAGE_METHODS_TO_INSTRUMENT.includes(methodName as StorageMethod)) {\n return (original as (...args: unknown[]) => unknown).bind(target);\n }\n\n return function (this: unknown, ...args: unknown[]) {\n // KV entries managed by the DO framework itself (agents/partyserver state) are bookkeeping\n // rather than user work — skip the span, mirroring how `cf_` SQL tables are treated.\n // oxlint-disable-next-line typescript/no-unnecessary-type-assertion -- rule false positive: the cast reaches the Cloudflare-only `durableObjectStorageSpanAllowlist`; tsc errors without it\n const allowlist = (getClient()?.getOptions() as CloudflareClientOptions | undefined)\n ?.durableObjectStorageSpanAllowlist;\n const keys = getStorageKeys(methodName, args);\n if (keys && keys.length > 0 && keys.every(key => targetsCloudflareInternalKey(key, allowlist))) {\n return (original as (...a: unknown[]) => unknown).apply(target, args);\n }\n\n return startSpan(\n {\n // Use underscore naming to match Cloudflare's native instrumentation (e.g., \"durable_object_storage_get\")\n name: `durable_object_storage_${methodName}`,\n
|
|
1
|
+
{"version":3,"file":"instrumentDurableObjectStorage.js","sources":["../../../../src/instrumentations/instrumentDurableObjectStorage.ts"],"sourcesContent":["import type { DurableObjectStorage, SyncKvStorage, SqlStorage } from '@cloudflare/workers-types';\nimport { SENTRY_OP } from '@sentry/conventions/attributes';\nimport { DB } from '@sentry/conventions/op';\nimport { getClient, isThenable, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, startSpan } from '@sentry/core';\nimport type { CloudflareClientOptions } from '../client';\nimport { getStorageKeys, targetsCloudflareInternalKey } from '../utils/internalStorageKey';\nimport { storeSpanContext } from '../utils/traceLinks';\nimport { instrumentDurableObjectSyncKvStorage } from './instrumentDurableObjectSyncKvStorage';\nimport { instrumentSqlStorage } from './instrumentSqlStorage';\n\nconst STORAGE_METHODS_TO_INSTRUMENT = ['get', 'put', 'delete', 'list', 'setAlarm', 'getAlarm', 'deleteAlarm'] as const;\n\ntype StorageMethod = (typeof STORAGE_METHODS_TO_INSTRUMENT)[number];\n\ntype WaitUntil = (promise: Promise<unknown>) => void;\n\n/**\n * Instruments DurableObjectStorage methods with Sentry spans.\n *\n * Wraps the following async methods:\n * - get, put, delete, list (KV API)\n * - setAlarm, getAlarm, deleteAlarm (Alarm API)\n *\n * When setAlarm is called, it also stores the current span context so that when\n * the alarm fires later, it can link back to the trace that called setAlarm.\n *\n * @param storage - The DurableObjectStorage instance to instrument\n * @param waitUntil - Optional waitUntil function to defer span context storage\n * @returns An instrumented DurableObjectStorage instance\n */\nexport function instrumentDurableObjectStorage(\n storage: DurableObjectStorage,\n waitUntil?: WaitUntil,\n): DurableObjectStorage {\n return new Proxy(storage, {\n get(target, prop, _receiver) {\n // Use `target` as the receiver instead of the proxy (`_receiver`).\n // Native workerd getters (e.g., `storage.sql`) validate `this` via\n // internal slots. Passing the proxy as receiver breaks that check,\n // causing \"Illegal invocation: function called with incorrect `this`\n // reference\" errors.\n const original = Reflect.get(target, prop, target);\n\n if (prop === 'kv' && original != null && 'get' in original && 'put' in original) {\n return instrumentDurableObjectSyncKvStorage(original as SyncKvStorage);\n }\n\n if (prop === 'sql' && original != null && 'databaseSize' in original && 'exec' in original) {\n return instrumentSqlStorage(original as SqlStorage);\n }\n\n if (typeof original !== 'function') {\n return original;\n }\n\n const methodName = prop as string;\n if (!STORAGE_METHODS_TO_INSTRUMENT.includes(methodName as StorageMethod)) {\n return (original as (...args: unknown[]) => unknown).bind(target);\n }\n\n return function (this: unknown, ...args: unknown[]) {\n // KV entries managed by the DO framework itself (agents/partyserver state) are bookkeeping\n // rather than user work — skip the span, mirroring how `cf_` SQL tables are treated.\n // oxlint-disable-next-line typescript/no-unnecessary-type-assertion -- rule false positive: the cast reaches the Cloudflare-only `durableObjectStorageSpanAllowlist`; tsc errors without it\n const allowlist = (getClient()?.getOptions() as CloudflareClientOptions | undefined)\n ?.durableObjectStorageSpanAllowlist;\n const keys = getStorageKeys(methodName, args);\n if (keys && keys.length > 0 && keys.every(key => targetsCloudflareInternalKey(key, allowlist))) {\n return (original as (...a: unknown[]) => unknown).apply(target, args);\n }\n\n return startSpan(\n {\n // Use underscore naming to match Cloudflare's native instrumentation (e.g., \"durable_object_storage_get\")\n name: `durable_object_storage_${methodName}`,\n attributes: {\n [SENTRY_OP]: DB,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.db.cloudflare.durable_object',\n 'db.system.name': 'cloudflare.durable_object.storage',\n 'db.operation.name': methodName,\n },\n },\n () => {\n const teardown = async (): Promise<void> => {\n // When setAlarm is called, store the current span context so that when the alarm\n // fires later, it can link back to the trace that called setAlarm.\n // We use the original (uninstrumented) storage (target) to avoid creating a span\n // for this internal operation. The storage is deferred via waitUntil to not block.\n if (methodName === 'setAlarm') {\n storeSpanContext(target, 'alarm');\n }\n };\n\n const result = (original as (...args: unknown[]) => unknown).apply(target, args);\n\n if (!isThenable(result)) {\n waitUntil?.(teardown());\n\n return result;\n }\n\n return result.then(\n res => {\n waitUntil?.(teardown());\n return res;\n },\n e => {\n throw e;\n },\n );\n },\n );\n };\n },\n });\n}\n"],"names":[],"mappings":";;;;;;;;AAUA,MAAM,6BAAA,GAAgC,CAAC,KAAA,EAAO,KAAA,EAAO,UAAU,MAAA,EAAQ,UAAA,EAAY,YAAY,aAAa,CAAA;AAoBrG,SAAS,8BAAA,CACd,SACA,SAAA,EACsB;AACtB,EAAA,OAAO,IAAI,MAAM,OAAA,EAAS;AAAA,IACxB,GAAA,CAAI,MAAA,EAAQ,IAAA,EAAM,SAAA,EAAW;AAM3B,MAAA,MAAM,QAAA,GAAW,OAAA,CAAQ,GAAA,CAAI,MAAA,EAAQ,MAAM,MAAM,CAAA;AAEjD,MAAA,IAAI,SAAS,IAAA,IAAQ,QAAA,IAAY,QAAQ,KAAA,IAAS,QAAA,IAAY,SAAS,QAAA,EAAU;AAC/E,QAAA,OAAO,qCAAqC,QAAyB,CAAA;AAAA,MACvE;AAEA,MAAA,IAAI,SAAS,KAAA,IAAS,QAAA,IAAY,QAAQ,cAAA,IAAkB,QAAA,IAAY,UAAU,QAAA,EAAU;AAC1F,QAAA,OAAO,qBAAqB,QAAsB,CAAA;AAAA,MACpD;AAEA,MAAA,IAAI,OAAO,aAAa,UAAA,EAAY;AAClC,QAAA,OAAO,QAAA;AAAA,MACT;AAEA,MAAA,MAAM,UAAA,GAAa,IAAA;AACnB,MAAA,IAAI,CAAC,6BAAA,CAA8B,QAAA,CAAS,UAA2B,CAAA,EAAG;AACxE,QAAA,OAAQ,QAAA,CAA6C,KAAK,MAAM,CAAA;AAAA,MAClE;AAEA,MAAA,OAAO,YAA4B,IAAA,EAAiB;AAIlD,QAAA,MAAM,SAAA,GAAa,SAAA,EAAU,EAAG,UAAA,EAAW,EACvC,iCAAA;AACJ,QAAA,MAAM,IAAA,GAAO,cAAA,CAAe,UAAA,EAAY,IAAI,CAAA;AAC5C,QAAA,IAAI,IAAA,IAAQ,IAAA,CAAK,MAAA,GAAS,CAAA,IAAK,IAAA,CAAK,KAAA,CAAM,CAAA,GAAA,KAAO,4BAAA,CAA6B,GAAA,EAAK,SAAS,CAAC,CAAA,EAAG;AAC9F,UAAA,OAAQ,QAAA,CAA0C,KAAA,CAAM,MAAA,EAAQ,IAAI,CAAA;AAAA,QACtE;AAEA,QAAA,OAAO,SAAA;AAAA,UACL;AAAA;AAAA,YAEE,IAAA,EAAM,0BAA0B,UAAU,CAAA,CAAA;AAAA,YAC1C,UAAA,EAAY;AAAA,cACV,CAAC,SAAS,GAAG,EAAA;AAAA,cACb,CAAC,gCAAgC,GAAG,mCAAA;AAAA,cACpC,gBAAA,EAAkB,mCAAA;AAAA,cAClB,mBAAA,EAAqB;AAAA;AACvB,WACF;AAAA,UACA,MAAM;AACJ,YAAA,MAAM,WAAW,YAA2B;AAK1C,cAAA,IAAI,eAAe,UAAA,EAAY;AAC7B,gBAAA,gBAAA,CAAiB,QAAQ,OAAO,CAAA;AAAA,cAClC;AAAA,YACF,CAAA;AAEA,YAAA,MAAM,MAAA,GAAU,QAAA,CAA6C,KAAA,CAAM,MAAA,EAAQ,IAAI,CAAA;AAE/E,YAAA,IAAI,CAAC,UAAA,CAAW,MAAM,CAAA,EAAG;AACvB,cAAA,SAAA,GAAY,UAAU,CAAA;AAEtB,cAAA,OAAO,MAAA;AAAA,YACT;AAEA,YAAA,OAAO,MAAA,CAAO,IAAA;AAAA,cACZ,CAAA,GAAA,KAAO;AACL,gBAAA,SAAA,GAAY,UAAU,CAAA;AACtB,gBAAA,OAAO,GAAA;AAAA,cACT,CAAA;AAAA,cACA,CAAA,CAAA,KAAK;AACH,gBAAA,MAAM,CAAA;AAAA,cACR;AAAA,aACF;AAAA,UACF;AAAA,SACF;AAAA,MACF,CAAA;AAAA,IACF;AAAA,GACD,CAAA;AACH;;;;"}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { SENTRY_OP } from '@sentry/conventions/attributes';
|
|
2
|
+
import { DB } from '@sentry/conventions/op';
|
|
1
3
|
import { startSpan, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/core';
|
|
2
4
|
|
|
3
5
|
const SYNC_KV_METHODS_TO_INSTRUMENT = ["get", "put", "delete", "list"];
|
|
@@ -16,8 +18,8 @@ function instrumentDurableObjectSyncKvStorage(syncKv) {
|
|
|
16
18
|
return startSpan(
|
|
17
19
|
{
|
|
18
20
|
name: `durable_object_storage_kv_${methodName}`,
|
|
19
|
-
op: "db",
|
|
20
21
|
attributes: {
|
|
22
|
+
[SENTRY_OP]: DB,
|
|
21
23
|
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.db.cloudflare.durable_object",
|
|
22
24
|
// keeping the value as close as possible to the Cloudflare Worker KV instrumentation
|
|
23
25
|
// https://github.com/cloudflare/workerd/blob/6b8b11787e2b2a800ab0edd0690bfab3857b0529/src/workerd/api/sync-kv.c%2B%2B#L19
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instrumentDurableObjectSyncKvStorage.js","sources":["../../../../src/instrumentations/instrumentDurableObjectSyncKvStorage.ts"],"sourcesContent":["import type { SyncKvStorage } from '@cloudflare/workers-types';\nimport { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, startSpan } from '@sentry/core';\n\nconst SYNC_KV_METHODS_TO_INSTRUMENT = ['get', 'put', 'delete', 'list'] as const;\n\ntype SyncKvMethod = (typeof SYNC_KV_METHODS_TO_INSTRUMENT)[number];\n\nexport function instrumentDurableObjectSyncKvStorage(syncKv: SyncKvStorage): SyncKvStorage {\n return new Proxy(syncKv, {\n get(target, prop, _receiver) {\n const original = Reflect.get(target, prop, target);\n\n if (typeof original !== 'function') {\n return original;\n }\n\n const methodName = prop as SyncKvMethod;\n\n if (!SYNC_KV_METHODS_TO_INSTRUMENT.includes(methodName)) {\n return (original as (...args: unknown[]) => unknown).bind(target);\n }\n\n return function (this: unknown, ...args: unknown[]) {\n return startSpan(\n {\n name: `durable_object_storage_kv_${methodName}`,\n
|
|
1
|
+
{"version":3,"file":"instrumentDurableObjectSyncKvStorage.js","sources":["../../../../src/instrumentations/instrumentDurableObjectSyncKvStorage.ts"],"sourcesContent":["import type { SyncKvStorage } from '@cloudflare/workers-types';\nimport { SENTRY_OP } from '@sentry/conventions/attributes';\nimport { DB } from '@sentry/conventions/op';\nimport { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, startSpan } from '@sentry/core';\n\nconst SYNC_KV_METHODS_TO_INSTRUMENT = ['get', 'put', 'delete', 'list'] as const;\n\ntype SyncKvMethod = (typeof SYNC_KV_METHODS_TO_INSTRUMENT)[number];\n\nexport function instrumentDurableObjectSyncKvStorage(syncKv: SyncKvStorage): SyncKvStorage {\n return new Proxy(syncKv, {\n get(target, prop, _receiver) {\n const original = Reflect.get(target, prop, target);\n\n if (typeof original !== 'function') {\n return original;\n }\n\n const methodName = prop as SyncKvMethod;\n\n if (!SYNC_KV_METHODS_TO_INSTRUMENT.includes(methodName)) {\n return (original as (...args: unknown[]) => unknown).bind(target);\n }\n\n return function (this: unknown, ...args: unknown[]) {\n return startSpan(\n {\n name: `durable_object_storage_kv_${methodName}`,\n attributes: {\n [SENTRY_OP]: DB,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.db.cloudflare.durable_object',\n // keeping the value as close as possible to the Cloudflare Worker KV instrumentation\n // https://github.com/cloudflare/workerd/blob/6b8b11787e2b2a800ab0edd0690bfab3857b0529/src/workerd/api/sync-kv.c%2B%2B#L19\n 'db.system.name': 'cloudflare-durable-object-sql',\n 'db.operation.name': methodName,\n },\n },\n () => {\n return (original as (...args: unknown[]) => unknown).apply(target, args);\n },\n );\n };\n },\n });\n}\n"],"names":[],"mappings":";;;;AAKA,MAAM,6BAAA,GAAgC,CAAC,KAAA,EAAO,KAAA,EAAO,UAAU,MAAM,CAAA;AAI9D,SAAS,qCAAqC,MAAA,EAAsC;AACzF,EAAA,OAAO,IAAI,MAAM,MAAA,EAAQ;AAAA,IACvB,GAAA,CAAI,MAAA,EAAQ,IAAA,EAAM,SAAA,EAAW;AAC3B,MAAA,MAAM,QAAA,GAAW,OAAA,CAAQ,GAAA,CAAI,MAAA,EAAQ,MAAM,MAAM,CAAA;AAEjD,MAAA,IAAI,OAAO,aAAa,UAAA,EAAY;AAClC,QAAA,OAAO,QAAA;AAAA,MACT;AAEA,MAAA,MAAM,UAAA,GAAa,IAAA;AAEnB,MAAA,IAAI,CAAC,6BAAA,CAA8B,QAAA,CAAS,UAAU,CAAA,EAAG;AACvD,QAAA,OAAQ,QAAA,CAA6C,KAAK,MAAM,CAAA;AAAA,MAClE;AAEA,MAAA,OAAO,YAA4B,IAAA,EAAiB;AAClD,QAAA,OAAO,SAAA;AAAA,UACL;AAAA,YACE,IAAA,EAAM,6BAA6B,UAAU,CAAA,CAAA;AAAA,YAC7C,UAAA,EAAY;AAAA,cACV,CAAC,SAAS,GAAG,EAAA;AAAA,cACb,CAAC,gCAAgC,GAAG,mCAAA;AAAA;AAAA;AAAA,cAGpC,gBAAA,EAAkB,+BAAA;AAAA,cAClB,mBAAA,EAAqB;AAAA;AACvB,WACF;AAAA,UACA,MAAM;AACJ,YAAA,OAAQ,QAAA,CAA6C,KAAA,CAAM,MAAA,EAAQ,IAAI,CAAA;AAAA,UACzE;AAAA,SACF;AAAA,MACF,CAAA;AAAA,IACF;AAAA,GACD,CAAA;AACH;;;;"}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { SENTRY_OP } from '@sentry/conventions/attributes';
|
|
2
|
+
import { DB_QUERY } from '@sentry/conventions/op';
|
|
3
|
+
import { getClient, startSpan, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/core';
|
|
4
|
+
import { _INTERNAL_sanitizeSqlQuery, _INTERNAL_getSqlQuerySummary } from '@sentry/core/server';
|
|
2
5
|
import { targetsCloudflareInternalTable } from '../utils/internalSqlQuery.js';
|
|
3
6
|
|
|
4
7
|
function instrumentSqlStorage(sql) {
|
|
@@ -18,9 +21,9 @@ function instrumentSqlStorage(sql) {
|
|
|
18
21
|
}
|
|
19
22
|
return startSpan(
|
|
20
23
|
{
|
|
21
|
-
op: "db.query",
|
|
22
24
|
name: querySummary || sanitizedQuery,
|
|
23
25
|
attributes: {
|
|
26
|
+
[SENTRY_OP]: DB_QUERY,
|
|
24
27
|
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.db.cloudflare.durable_object.sql",
|
|
25
28
|
"db.system.name": "cloudflare-durable-object-sql",
|
|
26
29
|
"db.operation.name": "exec",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instrumentSqlStorage.js","sources":["../../../../src/instrumentations/instrumentSqlStorage.ts"],"sourcesContent":["import type { SqlStorage } from '@cloudflare/workers-types';\nimport {
|
|
1
|
+
{"version":3,"file":"instrumentSqlStorage.js","sources":["../../../../src/instrumentations/instrumentSqlStorage.ts"],"sourcesContent":["import type { SqlStorage } from '@cloudflare/workers-types';\nimport { SENTRY_OP } from '@sentry/conventions/attributes';\nimport { DB_QUERY } from '@sentry/conventions/op';\nimport { getClient, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, startSpan } from '@sentry/core';\nimport { _INTERNAL_getSqlQuerySummary, _INTERNAL_sanitizeSqlQuery } from '@sentry/core/server';\nimport type { CloudflareClientOptions } from '../client';\nimport { targetsCloudflareInternalTable } from '../utils/internalSqlQuery';\n\n/**\n * Instruments the Durable Object SqlStorage `exec` method with Sentry spans.\n *\n * @param sql - The SqlStorage instance to instrument\n * @returns An instrumented SqlStorage instance\n */\nexport function instrumentSqlStorage(sql: SqlStorage): SqlStorage {\n return new Proxy(sql, {\n get(target, prop, _receiver) {\n const original = Reflect.get(target, prop, target);\n\n if (prop !== 'exec' || typeof original !== 'function') {\n return original;\n }\n\n return function (this: unknown, ...args: unknown[]) {\n const [query, ...bindings] = args as [string, ...unknown[]];\n\n const sanitizedQuery = _INTERNAL_sanitizeSqlQuery(query);\n const querySummary = _INTERNAL_getSqlQuerySummary(sanitizedQuery);\n\n // oxlint-disable-next-line typescript/no-unnecessary-type-assertion -- rule false positive: the cast reaches the Cloudflare-only `durableObjectSqlSpanAllowlist`; tsc errors without it\n const allowlist = (getClient()?.getOptions() as CloudflareClientOptions | undefined)\n ?.durableObjectSqlSpanAllowlist;\n\n if (targetsCloudflareInternalTable(querySummary, allowlist, sanitizedQuery)) {\n return (original as (...a: unknown[]) => ReturnType<SqlStorage['exec']>).apply(target, args);\n }\n\n return startSpan(\n {\n name: querySummary || sanitizedQuery,\n attributes: {\n [SENTRY_OP]: DB_QUERY,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.db.cloudflare.durable_object.sql',\n 'db.system.name': 'cloudflare-durable-object-sql',\n 'db.operation.name': 'exec',\n 'db.query.text': sanitizedQuery,\n 'db.query.summary': querySummary,\n 'cloudflare.durable_object.query.bindings': bindings.length,\n },\n },\n () => (original as (...a: unknown[]) => ReturnType<SqlStorage['exec']>).apply(target, args),\n );\n };\n },\n });\n}\n"],"names":[],"mappings":";;;;;;AAcO,SAAS,qBAAqB,GAAA,EAA6B;AAChE,EAAA,OAAO,IAAI,MAAM,GAAA,EAAK;AAAA,IACpB,GAAA,CAAI,MAAA,EAAQ,IAAA,EAAM,SAAA,EAAW;AAC3B,MAAA,MAAM,QAAA,GAAW,OAAA,CAAQ,GAAA,CAAI,MAAA,EAAQ,MAAM,MAAM,CAAA;AAEjD,MAAA,IAAI,IAAA,KAAS,MAAA,IAAU,OAAO,QAAA,KAAa,UAAA,EAAY;AACrD,QAAA,OAAO,QAAA;AAAA,MACT;AAEA,MAAA,OAAO,YAA4B,IAAA,EAAiB;AAClD,QAAA,MAAM,CAAC,KAAA,EAAO,GAAG,QAAQ,CAAA,GAAI,IAAA;AAE7B,QAAA,MAAM,cAAA,GAAiB,2BAA2B,KAAK,CAAA;AACvD,QAAA,MAAM,YAAA,GAAe,6BAA6B,cAAc,CAAA;AAGhE,QAAA,MAAM,SAAA,GAAa,SAAA,EAAU,EAAG,UAAA,EAAW,EACvC,6BAAA;AAEJ,QAAA,IAAI,8BAAA,CAA+B,YAAA,EAAc,SAAA,EAAW,cAAc,CAAA,EAAG;AAC3E,UAAA,OAAQ,QAAA,CAAiE,KAAA,CAAM,MAAA,EAAQ,IAAI,CAAA;AAAA,QAC7F;AAEA,QAAA,OAAO,SAAA;AAAA,UACL;AAAA,YACE,MAAM,YAAA,IAAgB,cAAA;AAAA,YACtB,UAAA,EAAY;AAAA,cACV,CAAC,SAAS,GAAG,QAAA;AAAA,cACb,CAAC,gCAAgC,GAAG,uCAAA;AAAA,cACpC,gBAAA,EAAkB,+BAAA;AAAA,cAClB,mBAAA,EAAqB,MAAA;AAAA,cACrB,eAAA,EAAiB,cAAA;AAAA,cACjB,kBAAA,EAAoB,YAAA;AAAA,cACpB,4CAA4C,QAAA,CAAS;AAAA;AACvD,WACF;AAAA,UACA,MAAO,QAAA,CAAiE,KAAA,CAAM,MAAA,EAAQ,IAAI;AAAA,SAC5F;AAAA,MACF,CAAA;AAAA,IACF;AAAA,GACD,CAAA;AACH;;;;"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { RPC } from '@sentry/conventions/op';
|
|
1
2
|
import { setAsyncLocalStorageAsyncContextStrategy } from '@sentry/server-utils/no-diagnostic-channels';
|
|
2
3
|
import { getFinalOptions } from '../options.js';
|
|
3
4
|
import { instrumentContext } from '../utils/instrumentContext.js';
|
|
@@ -54,7 +55,7 @@ function instrumentMethod(instance, proxy, prop, method, options, context) {
|
|
|
54
55
|
options,
|
|
55
56
|
context,
|
|
56
57
|
spanName: (rpcMeta) => rpcMeta ? prop : void 0,
|
|
57
|
-
spanOp:
|
|
58
|
+
spanOp: RPC,
|
|
58
59
|
origin: WORKER_ENTRYPOINT_ORIGIN
|
|
59
60
|
},
|
|
60
61
|
boundMethod,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instrumentWorkerEntrypoint.js","sources":["../../../../src/instrumentations/instrumentWorkerEntrypoint.ts"],"sourcesContent":["import type { RpcStub, WorkerEntrypoint } from 'cloudflare:workers';\nimport { setAsyncLocalStorageAsyncContextStrategy } from '@sentry/server-utils/no-diagnostic-channels';\nimport type { CloudflareOptions } from '../client';\nimport { getFinalOptions } from '../options';\nimport type { DefaultEnv, ResolveEnv, StrictCloudflareOptions } from '../types';\nimport { instrumentContext } from '../utils/instrumentContext';\nimport { type UncheckedMethod, wrapMethodWithSentry } from '../wrapMethodWithSentry';\nimport { instrumentEnv } from './worker/instrumentEnv';\nimport { instrumentWorkerEntrypointFetch } from './worker/instrumentFetch';\nimport { instrumentWorkerEntrypointQueue } from './worker/instrumentQueue';\nimport { instrumentWorkerEntrypointScheduled } from './worker/instrumentScheduled';\nimport { instrumentWorkerEntrypointTail } from './worker/instrumentTail';\n\nconst WORKER_ENTRYPOINT_ORIGIN = 'auto.faas.cloudflare.worker_entrypoint';\n\ntype ReservedMethod =\n | Exclude<Extract<keyof WorkerEntrypoint, string>, '__WORKER_ENTRYPOINT_BRAND'>\n | Extract<keyof ExportedHandler, string>\n | Exclude<Extract<keyof RpcStub<(...args: unknown[]) => unknown>, string>, '__RPC_STUB_BRAND'>\n | 'constructor';\n\nconst RESERVED_METHODS = new Set<string>([\n 'connect',\n 'constructor',\n 'dup',\n 'email',\n 'fetch',\n 'queue',\n 'scheduled',\n 'tail',\n 'tailStream',\n 'test',\n 'trace',\n] satisfies ReservedMethod[]);\n\ninterface CachedMethod {\n source: UncheckedMethod;\n wrapped: UncheckedMethod;\n}\n\nexport type WorkerEntrypointConstructor<Env = unknown, Props = {}> = new (\n ctx: ExecutionContext,\n // WorkerEntrypoint subclasses can define different `env` shapes, so this constructor must accept all of them.\n // oxlint-disable-next-line typescript/no-explicit-any\n env: any,\n) => InstanceType<typeof WorkerEntrypoint<Env, Props>>;\n\nfunction isPrototypeMethod(instance: object, prop: string, method: UncheckedMethod): boolean {\n let prototype = Object.getPrototypeOf(instance);\n\n while (prototype && prototype !== Object.prototype) {\n const descriptor = Object.getOwnPropertyDescriptor(prototype, prop);\n if (descriptor) {\n return descriptor.value === method;\n }\n prototype = Object.getPrototypeOf(prototype);\n }\n\n return false;\n}\n\nfunction bindToInstance(instance: object, proxy: object, method: UncheckedMethod): UncheckedMethod {\n return new Proxy(method, {\n apply(target, thisArg, args) {\n return Reflect.apply(target, thisArg === proxy ? instance : thisArg, args);\n },\n });\n}\n\nfunction instrumentMethod(\n instance: object,\n proxy: object,\n prop: PropertyKey,\n method: UncheckedMethod,\n options: CloudflareOptions,\n context: ExecutionContext,\n): UncheckedMethod {\n const ownDescriptor = Object.getOwnPropertyDescriptor(instance, prop);\n if (ownDescriptor && 'value' in ownDescriptor && !ownDescriptor.configurable && !ownDescriptor.writable) {\n return method;\n }\n\n const boundMethod = bindToInstance(instance, proxy, method);\n\n if (typeof prop !== 'string' || RESERVED_METHODS.has(prop) || !isPrototypeMethod(instance, prop, method)) {\n return boundMethod;\n }\n\n return wrapMethodWithSentry(\n {\n options,\n context,\n spanName: rpcMeta => (rpcMeta ? prop : undefined),\n spanOp: 'rpc',\n origin: WORKER_ENTRYPOINT_ORIGIN,\n },\n boundMethod,\n undefined,\n true,\n );\n}\n\n/**\n * Instruments a WorkerEntrypoint class to capture errors and performance data.\n *\n * Instruments the following methods (same as `withSentry` for ExportedHandler):\n * - fetch (HTTP requests)\n * - scheduled (cron triggers)\n * - queue (queue consumers)\n * - email (email handlers)\n * - tail (tail workers)\n *\n * as well as any other public RPC methods on the WorkerEntrypoint instance.\n *\n * @param optionsCallback Function that returns the options for the SDK initialization.\n * @param WorkerEntrypointClass The WorkerEntrypoint class to instrument.\n * @returns The instrumented WorkerEntrypoint class.\n *\n * @example\n * ```ts\n * class MyWorkerBase extends WorkerEntrypoint<Env> {\n * async fetch(request: Request): Promise<Response> {\n * return new Response('Hello World!');\n * }\n *\n * async myRpcMethod(arg: string): Promise<string> {\n * return `Hello ${arg}!`;\n * }\n * }\n *\n * export default instrumentWorkerEntrypoint(\n * env => ({\n * dsn: env.SENTRY_DSN,\n * tracesSampleRate: 1.0,\n * }),\n * MyWorkerBase,\n * );\n * ```\n */\nexport function instrumentWorkerEntrypoint<\n Env = DefaultEnv,\n Props = {},\n // oxlint-disable-next-line typescript/no-explicit-any\n T extends WorkerEntrypoint<any, any> = WorkerEntrypoint<Env, Props>,\n // oxlint-disable-next-line typescript/no-explicit-any\n C extends new (ctx: ExecutionContext, env: any) => T = new (ctx: ExecutionContext, env: any) => T,\n O = unknown,\n>(optionsCallback: (env: ResolveEnv<C, Env>) => StrictCloudflareOptions<O> | undefined, WorkerEntrypointClass: C): C {\n // Set up AsyncLocalStorage strategy ONCE at instrumentation time, not per-request\n // This is critical - calling this per-request would create a new AsyncLocalStorage\n // each time, breaking scope isolation for concurrent requests\n setAsyncLocalStorageAsyncContextStrategy();\n\n return new Proxy(WorkerEntrypointClass, {\n construct(target, [ctx, env]) {\n const context = instrumentContext(ctx);\n const options = getFinalOptions(optionsCallback(env), env);\n const instrumentedEnv = instrumentEnv(env, options);\n const obj = new target(context, instrumentedEnv);\n\n // Override this.ctx to ensure the instrumented context is used\n // This is necessary because the base WorkerEntrypoint class sets this.ctx\n // from the constructor parameter, but we want users accessing this.ctx\n // to get the instrumented version\n if ('ctx' in obj) {\n Object.defineProperty(obj, 'ctx', {\n value: context,\n writable: true,\n enumerable: true,\n configurable: true,\n });\n }\n\n if ('env' in obj) {\n Object.defineProperty(obj, 'env', {\n value: instrumentedEnv,\n writable: true,\n enumerable: true,\n configurable: true,\n });\n }\n\n Object.defineProperty(obj, '__SENTRY_CONTEXT__', {\n value: context,\n enumerable: false,\n writable: false,\n configurable: false,\n });\n\n Object.defineProperty(obj, '__SENTRY_OPTIONS__', {\n value: options,\n enumerable: false,\n writable: false,\n configurable: false,\n });\n\n const entrypoint = obj as WorkerEntrypoint;\n instrumentWorkerEntrypointFetch(entrypoint, options, context);\n instrumentWorkerEntrypointScheduled(entrypoint, options, context);\n instrumentWorkerEntrypointQueue(entrypoint, options, context);\n instrumentWorkerEntrypointTail(entrypoint, options, context);\n\n // workerd resolves RPC methods through property access.\n const methodCache = new Map<PropertyKey, CachedMethod>();\n\n const proxy: typeof obj = new Proxy(obj, {\n get(instance, prop) {\n const value = Reflect.get(instance, prop, instance);\n\n if (typeof value !== 'function') {\n return value;\n }\n\n const method = value as UncheckedMethod;\n\n const cached = methodCache.get(prop);\n if (cached?.source === method) {\n return cached.wrapped;\n }\n\n const wrapped = instrumentMethod(instance, proxy, prop, method, options, context);\n methodCache.set(prop, { source: method, wrapped });\n return wrapped;\n },\n });\n\n return proxy;\n },\n });\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"instrumentWorkerEntrypoint.js","sources":["../../../../src/instrumentations/instrumentWorkerEntrypoint.ts"],"sourcesContent":["import type { RpcStub, WorkerEntrypoint } from 'cloudflare:workers';\nimport { RPC } from '@sentry/conventions/op';\nimport { setAsyncLocalStorageAsyncContextStrategy } from '@sentry/server-utils/no-diagnostic-channels';\nimport type { CloudflareOptions } from '../client';\nimport { getFinalOptions } from '../options';\nimport type { DefaultEnv, ResolveEnv, StrictCloudflareOptions } from '../types';\nimport { instrumentContext } from '../utils/instrumentContext';\nimport { type UncheckedMethod, wrapMethodWithSentry } from '../wrapMethodWithSentry';\nimport { instrumentEnv } from './worker/instrumentEnv';\nimport { instrumentWorkerEntrypointFetch } from './worker/instrumentFetch';\nimport { instrumentWorkerEntrypointQueue } from './worker/instrumentQueue';\nimport { instrumentWorkerEntrypointScheduled } from './worker/instrumentScheduled';\nimport { instrumentWorkerEntrypointTail } from './worker/instrumentTail';\n\nconst WORKER_ENTRYPOINT_ORIGIN = 'auto.faas.cloudflare.worker_entrypoint';\n\ntype ReservedMethod =\n | Exclude<Extract<keyof WorkerEntrypoint, string>, '__WORKER_ENTRYPOINT_BRAND'>\n | Extract<keyof ExportedHandler, string>\n | Exclude<Extract<keyof RpcStub<(...args: unknown[]) => unknown>, string>, '__RPC_STUB_BRAND'>\n | 'constructor';\n\nconst RESERVED_METHODS = new Set<string>([\n 'connect',\n 'constructor',\n 'dup',\n 'email',\n 'fetch',\n 'queue',\n 'scheduled',\n 'tail',\n 'tailStream',\n 'test',\n 'trace',\n] satisfies ReservedMethod[]);\n\ninterface CachedMethod {\n source: UncheckedMethod;\n wrapped: UncheckedMethod;\n}\n\nexport type WorkerEntrypointConstructor<Env = unknown, Props = {}> = new (\n ctx: ExecutionContext,\n // WorkerEntrypoint subclasses can define different `env` shapes, so this constructor must accept all of them.\n // oxlint-disable-next-line typescript/no-explicit-any\n env: any,\n) => InstanceType<typeof WorkerEntrypoint<Env, Props>>;\n\nfunction isPrototypeMethod(instance: object, prop: string, method: UncheckedMethod): boolean {\n let prototype = Object.getPrototypeOf(instance);\n\n while (prototype && prototype !== Object.prototype) {\n const descriptor = Object.getOwnPropertyDescriptor(prototype, prop);\n if (descriptor) {\n return descriptor.value === method;\n }\n prototype = Object.getPrototypeOf(prototype);\n }\n\n return false;\n}\n\nfunction bindToInstance(instance: object, proxy: object, method: UncheckedMethod): UncheckedMethod {\n return new Proxy(method, {\n apply(target, thisArg, args) {\n return Reflect.apply(target, thisArg === proxy ? instance : thisArg, args);\n },\n });\n}\n\nfunction instrumentMethod(\n instance: object,\n proxy: object,\n prop: PropertyKey,\n method: UncheckedMethod,\n options: CloudflareOptions,\n context: ExecutionContext,\n): UncheckedMethod {\n const ownDescriptor = Object.getOwnPropertyDescriptor(instance, prop);\n if (ownDescriptor && 'value' in ownDescriptor && !ownDescriptor.configurable && !ownDescriptor.writable) {\n return method;\n }\n\n const boundMethod = bindToInstance(instance, proxy, method);\n\n if (typeof prop !== 'string' || RESERVED_METHODS.has(prop) || !isPrototypeMethod(instance, prop, method)) {\n return boundMethod;\n }\n\n return wrapMethodWithSentry(\n {\n options,\n context,\n spanName: rpcMeta => (rpcMeta ? prop : undefined),\n spanOp: RPC,\n origin: WORKER_ENTRYPOINT_ORIGIN,\n },\n boundMethod,\n undefined,\n true,\n );\n}\n\n/**\n * Instruments a WorkerEntrypoint class to capture errors and performance data.\n *\n * Instruments the following methods (same as `withSentry` for ExportedHandler):\n * - fetch (HTTP requests)\n * - scheduled (cron triggers)\n * - queue (queue consumers)\n * - email (email handlers)\n * - tail (tail workers)\n *\n * as well as any other public RPC methods on the WorkerEntrypoint instance.\n *\n * @param optionsCallback Function that returns the options for the SDK initialization.\n * @param WorkerEntrypointClass The WorkerEntrypoint class to instrument.\n * @returns The instrumented WorkerEntrypoint class.\n *\n * @example\n * ```ts\n * class MyWorkerBase extends WorkerEntrypoint<Env> {\n * async fetch(request: Request): Promise<Response> {\n * return new Response('Hello World!');\n * }\n *\n * async myRpcMethod(arg: string): Promise<string> {\n * return `Hello ${arg}!`;\n * }\n * }\n *\n * export default instrumentWorkerEntrypoint(\n * env => ({\n * dsn: env.SENTRY_DSN,\n * tracesSampleRate: 1.0,\n * }),\n * MyWorkerBase,\n * );\n * ```\n */\nexport function instrumentWorkerEntrypoint<\n Env = DefaultEnv,\n Props = {},\n // oxlint-disable-next-line typescript/no-explicit-any\n T extends WorkerEntrypoint<any, any> = WorkerEntrypoint<Env, Props>,\n // oxlint-disable-next-line typescript/no-explicit-any\n C extends new (ctx: ExecutionContext, env: any) => T = new (ctx: ExecutionContext, env: any) => T,\n O = unknown,\n>(optionsCallback: (env: ResolveEnv<C, Env>) => StrictCloudflareOptions<O> | undefined, WorkerEntrypointClass: C): C {\n // Set up AsyncLocalStorage strategy ONCE at instrumentation time, not per-request\n // This is critical - calling this per-request would create a new AsyncLocalStorage\n // each time, breaking scope isolation for concurrent requests\n setAsyncLocalStorageAsyncContextStrategy();\n\n return new Proxy(WorkerEntrypointClass, {\n construct(target, [ctx, env]) {\n const context = instrumentContext(ctx);\n const options = getFinalOptions(optionsCallback(env), env);\n const instrumentedEnv = instrumentEnv(env, options);\n const obj = new target(context, instrumentedEnv);\n\n // Override this.ctx to ensure the instrumented context is used\n // This is necessary because the base WorkerEntrypoint class sets this.ctx\n // from the constructor parameter, but we want users accessing this.ctx\n // to get the instrumented version\n if ('ctx' in obj) {\n Object.defineProperty(obj, 'ctx', {\n value: context,\n writable: true,\n enumerable: true,\n configurable: true,\n });\n }\n\n if ('env' in obj) {\n Object.defineProperty(obj, 'env', {\n value: instrumentedEnv,\n writable: true,\n enumerable: true,\n configurable: true,\n });\n }\n\n Object.defineProperty(obj, '__SENTRY_CONTEXT__', {\n value: context,\n enumerable: false,\n writable: false,\n configurable: false,\n });\n\n Object.defineProperty(obj, '__SENTRY_OPTIONS__', {\n value: options,\n enumerable: false,\n writable: false,\n configurable: false,\n });\n\n const entrypoint = obj as WorkerEntrypoint;\n instrumentWorkerEntrypointFetch(entrypoint, options, context);\n instrumentWorkerEntrypointScheduled(entrypoint, options, context);\n instrumentWorkerEntrypointQueue(entrypoint, options, context);\n instrumentWorkerEntrypointTail(entrypoint, options, context);\n\n // workerd resolves RPC methods through property access.\n const methodCache = new Map<PropertyKey, CachedMethod>();\n\n const proxy: typeof obj = new Proxy(obj, {\n get(instance, prop) {\n const value = Reflect.get(instance, prop, instance);\n\n if (typeof value !== 'function') {\n return value;\n }\n\n const method = value as UncheckedMethod;\n\n const cached = methodCache.get(prop);\n if (cached?.source === method) {\n return cached.wrapped;\n }\n\n const wrapped = instrumentMethod(instance, proxy, prop, method, options, context);\n methodCache.set(prop, { source: method, wrapped });\n return wrapped;\n },\n });\n\n return proxy;\n },\n });\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { SENTRY_OP } from '@sentry/conventions/attributes';
|
|
2
|
+
import { DB_QUERY } from '@sentry/conventions/op';
|
|
3
|
+
import { startSpan, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, getClient, hasSpanStreamingEnabled, addBreadcrumb, SPAN_STATUS_ERROR } from '@sentry/core';
|
|
4
|
+
import { _INTERNAL_getSqlQuerySummary, _INTERNAL_sanitizeSqlQuery } from '@sentry/core/server';
|
|
2
5
|
import { ensureInstrumented } from '../../instrument.js';
|
|
3
6
|
|
|
4
7
|
const patchedStatement = /* @__PURE__ */ new WeakSet();
|
|
@@ -83,9 +86,9 @@ function createStartSpanOptions(query, type) {
|
|
|
83
86
|
const client = getClient();
|
|
84
87
|
const name = client && hasSpanStreamingEnabled(client) ? querySummary || "cloudflare-d1" : query;
|
|
85
88
|
return {
|
|
86
|
-
op: "db.query",
|
|
87
89
|
name,
|
|
88
90
|
attributes: {
|
|
91
|
+
[SENTRY_OP]: DB_QUERY,
|
|
89
92
|
"db.system.name": "cloudflare-d1",
|
|
90
93
|
"db.operation.name": type,
|
|
91
94
|
"db.query.text": query,
|
|
@@ -109,9 +112,9 @@ function instrumentBatch(batch) {
|
|
|
109
112
|
const queryText = statements.map((statement) => statement.statement ?? "").filter(Boolean).join("\n");
|
|
110
113
|
return startSpan(
|
|
111
114
|
{
|
|
112
|
-
op: "db.query",
|
|
113
115
|
name: "D1 batch",
|
|
114
116
|
attributes: {
|
|
117
|
+
[SENTRY_OP]: DB_QUERY,
|
|
115
118
|
"db.system.name": "cloudflare-d1",
|
|
116
119
|
"db.operation.name": "batch",
|
|
117
120
|
"db.query.text": queryText || void 0,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instrumentD1.js","sources":["../../../../../src/instrumentations/worker/instrumentD1.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/unbound-method */\nimport type { D1Database, D1DatabaseSession, D1PreparedStatement, D1Response } from '@cloudflare/workers-types';\nimport type { Span, SpanAttributes, StartSpanOptions } from '@sentry/core';\nimport {\n _INTERNAL_getSqlQuerySummary,\n _INTERNAL_sanitizeSqlQuery,\n addBreadcrumb,\n getClient,\n hasSpanStreamingEnabled,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SPAN_STATUS_ERROR,\n startSpan,\n} from '@sentry/core';\nimport { ensureInstrumented } from '../../instrument';\n\n// Patching is based on internal Cloudflare D1 API\n// https://github.com/cloudflare/workerd/blob/cd5279e7b305003f1d9c851e73efa9d67e4b68b2/src/cloudflare/internal/d1-api.ts\n\nconst patchedStatement = new WeakSet<D1PreparedStatement>();\n\n/**\n * Patches the query methods of a Cloudflare D1 prepared statement with Sentry.\n */\nfunction instrumentD1PreparedStatementQueries(statement: D1PreparedStatement, query: string): D1PreparedStatement {\n if (patchedStatement.has(statement)) {\n return statement;\n }\n\n // eslint-disable-next-line @typescript-eslint/unbound-method\n statement.first = new Proxy(statement.first, {\n apply(target, thisArg, args: Parameters<typeof statement.first>) {\n return startSpan(createStartSpanOptions(query, 'first'), async () => {\n const res = await Reflect.apply(target, thisArg, args);\n createD1Breadcrumb(query, 'first');\n return res;\n });\n },\n });\n\n // eslint-disable-next-line @typescript-eslint/unbound-method\n statement.run = new Proxy(statement.run, {\n apply(target, thisArg, args: Parameters<typeof statement.run>) {\n return startSpan(createStartSpanOptions(query, 'run'), async span => {\n const d1Response = await Reflect.apply(target, thisArg, args);\n applyD1ReturnObjectToSpan(span, d1Response);\n createD1Breadcrumb(query, 'run', d1Response);\n return d1Response;\n });\n },\n });\n\n // eslint-disable-next-line @typescript-eslint/unbound-method\n statement.all = new Proxy(statement.all, {\n apply(target, thisArg, args: Parameters<typeof statement.all>) {\n return startSpan(createStartSpanOptions(query, 'all'), async span => {\n const d1Result = await Reflect.apply(target, thisArg, args);\n applyD1ReturnObjectToSpan(span, d1Result);\n createD1Breadcrumb(query, 'all', d1Result);\n return d1Result;\n });\n },\n });\n\n // eslint-disable-next-line @typescript-eslint/unbound-method\n statement.raw = new Proxy(statement.raw, {\n apply(target, thisArg, args: Parameters<typeof statement.raw>) {\n return startSpan(createStartSpanOptions(query, 'raw'), async () => {\n const res = await Reflect.apply(target, thisArg, args);\n createD1Breadcrumb(query, 'raw');\n return res;\n });\n },\n });\n\n patchedStatement.add(statement);\n\n return statement;\n}\n\n/**\n * Instruments a Cloudflare D1 prepared statement with Sentry.\n *\n * This is meant to be used as a top-level call, under the hood it calls `instrumentD1PreparedStatementQueries`\n * to patch the query methods. The reason for this abstraction is to ensure that the `bind` method is also patched.\n */\nfunction instrumentD1PreparedStatement(statement: D1PreparedStatement, query: string): D1PreparedStatement {\n // statement.bind() returns a new instance of D1PreparedStatement, so we have to patch it as well.\n // eslint-disable-next-line @typescript-eslint/unbound-method\n statement.bind = new Proxy(statement.bind, {\n apply(target, thisArg, args: Parameters<typeof statement.bind>) {\n return instrumentD1PreparedStatementQueries(Reflect.apply(target, thisArg, args), query);\n },\n });\n\n return instrumentD1PreparedStatementQueries(statement, query);\n}\n\n/**\n * Add D1Response meta information to a span.\n *\n * See: https://developers.cloudflare.com/d1/build-with-d1/d1-client-api/#return-object\n */\nfunction applyD1ReturnObjectToSpan(span: Span, d1Result: D1Response): void {\n if (!d1Result.success) {\n span.setStatus({ code: SPAN_STATUS_ERROR });\n }\n\n span.setAttributes(getAttributesFromD1Response(d1Result));\n}\n\nfunction getAttributesFromD1Response(d1Result: D1Response): SpanAttributes {\n return {\n 'cloudflare.d1.duration': d1Result.meta.duration,\n 'cloudflare.d1.rows_read': d1Result.meta.rows_read,\n 'cloudflare.d1.rows_written': d1Result.meta.rows_written,\n };\n}\n\ntype D1QueryType = 'first' | 'run' | 'all' | 'raw' | 'batch' | 'exec';\n\nfunction createD1Breadcrumb(query: string, type: D1QueryType, d1Result?: D1Response): void {\n addBreadcrumb({\n category: 'query',\n message: query,\n data: {\n ...(d1Result ? getAttributesFromD1Response(d1Result) : {}),\n 'db.operation.name': type,\n },\n });\n}\n\nfunction createStartSpanOptions(query: string, type: D1QueryType): StartSpanOptions {\n const querySummary = query ? _INTERNAL_getSqlQuerySummary(_INTERNAL_sanitizeSqlQuery(query)) : undefined;\n\n const client = getClient();\n const name = client && hasSpanStreamingEnabled(client) ? querySummary || 'cloudflare-d1' : query;\n\n return {\n op: 'db.query',\n name,\n attributes: {\n 'db.system.name': 'cloudflare-d1',\n 'db.operation.name': type,\n 'db.query.text': query,\n 'db.query.summary': querySummary,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.db.cloudflare.d1',\n },\n };\n}\n\nfunction instrumentPrepare(\n prepare: D1Database['prepare'] | D1DatabaseSession['prepare'],\n): D1Database['prepare'] | D1DatabaseSession['prepare'] {\n return new Proxy(prepare, {\n apply(target, thisArg, args: Parameters<typeof prepare>) {\n const [query] = args;\n return instrumentD1PreparedStatement(Reflect.apply(target, thisArg, args), query);\n },\n });\n}\n\nfunction instrumentBatch(\n batch: D1Database['batch'] | D1DatabaseSession['batch'],\n): D1Database['batch'] | D1DatabaseSession['batch'] {\n return new Proxy(batch, {\n apply(target, thisArg, args: Parameters<typeof batch>) {\n const statements = args[0];\n // D1PreparedStatement exposes a `statement` property at runtime, but it's not in @cloudflare/workers-types.\n // https://github.com/cloudflare/workerd/blob/dc12d7650b4f5d4f9ba6a47aa45fad769cdf8db4/src/cloudflare/internal/d1-api.ts#L210\n const queryText = statements\n .map(statement => (statement as unknown as { statement?: string }).statement ?? '')\n .filter(Boolean)\n .join('\\n');\n\n return startSpan(\n {\n op: 'db.query',\n name: 'D1 batch',\n attributes: {\n 'db.system.name': 'cloudflare-d1',\n 'db.operation.name': 'batch',\n 'db.query.text': queryText || undefined,\n 'db.operation.batch.size': statements.length,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.db.cloudflare.d1',\n },\n },\n async () => {\n const res = await Reflect.apply(target, thisArg, args);\n createD1Breadcrumb('D1 batch', 'batch');\n return res;\n },\n );\n },\n });\n}\n\nfunction instrumentD1Session(session: D1DatabaseSession): D1DatabaseSession {\n session.prepare = instrumentPrepare(session.prepare);\n session.batch = instrumentBatch(session.batch);\n return session;\n}\n\nfunction _instrumentD1(db: D1Database): D1Database {\n db.prepare = instrumentPrepare(db.prepare);\n db.batch = instrumentBatch(db.batch);\n\n db.exec = new Proxy(db.exec, {\n apply(target, thisArg, args: Parameters<typeof db.exec>) {\n const [query] = args;\n return startSpan(createStartSpanOptions(query, 'exec'), async () => {\n const res = await Reflect.apply(target, thisArg, args);\n createD1Breadcrumb(query, 'exec');\n return res;\n });\n },\n });\n\n if ('withSession' in db && typeof db.withSession === 'function') {\n db.withSession = new Proxy(db.withSession, {\n apply(target, thisArg, args: [unknown]) {\n return instrumentD1Session(Reflect.apply(target, thisArg, args) as D1DatabaseSession);\n },\n });\n }\n\n return db;\n}\n\nexport function instrumentD1(db: D1Database): D1Database {\n return ensureInstrumented(db, _instrumentD1);\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"instrumentD1.js","sources":["../../../../../src/instrumentations/worker/instrumentD1.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/unbound-method */\nimport type { D1Database, D1DatabaseSession, D1PreparedStatement, D1Response } from '@cloudflare/workers-types';\nimport { SENTRY_OP } from '@sentry/conventions/attributes';\nimport { DB_QUERY } from '@sentry/conventions/op';\nimport type { Span, SpanAttributes, StartSpanOptions } from '@sentry/core';\nimport {\n addBreadcrumb,\n getClient,\n hasSpanStreamingEnabled,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SPAN_STATUS_ERROR,\n startSpan,\n} from '@sentry/core';\nimport { _INTERNAL_getSqlQuerySummary, _INTERNAL_sanitizeSqlQuery } from '@sentry/core/server';\nimport { ensureInstrumented } from '../../instrument';\n\n// Patching is based on internal Cloudflare D1 API\n// https://github.com/cloudflare/workerd/blob/cd5279e7b305003f1d9c851e73efa9d67e4b68b2/src/cloudflare/internal/d1-api.ts\n\nconst patchedStatement = new WeakSet<D1PreparedStatement>();\n\n/**\n * Patches the query methods of a Cloudflare D1 prepared statement with Sentry.\n */\nfunction instrumentD1PreparedStatementQueries(statement: D1PreparedStatement, query: string): D1PreparedStatement {\n if (patchedStatement.has(statement)) {\n return statement;\n }\n\n // eslint-disable-next-line @typescript-eslint/unbound-method\n statement.first = new Proxy(statement.first, {\n apply(target, thisArg, args: Parameters<typeof statement.first>) {\n return startSpan(createStartSpanOptions(query, 'first'), async () => {\n const res = await Reflect.apply(target, thisArg, args);\n createD1Breadcrumb(query, 'first');\n return res;\n });\n },\n });\n\n // eslint-disable-next-line @typescript-eslint/unbound-method\n statement.run = new Proxy(statement.run, {\n apply(target, thisArg, args: Parameters<typeof statement.run>) {\n return startSpan(createStartSpanOptions(query, 'run'), async span => {\n const d1Response = await Reflect.apply(target, thisArg, args);\n applyD1ReturnObjectToSpan(span, d1Response);\n createD1Breadcrumb(query, 'run', d1Response);\n return d1Response;\n });\n },\n });\n\n // eslint-disable-next-line @typescript-eslint/unbound-method\n statement.all = new Proxy(statement.all, {\n apply(target, thisArg, args: Parameters<typeof statement.all>) {\n return startSpan(createStartSpanOptions(query, 'all'), async span => {\n const d1Result = await Reflect.apply(target, thisArg, args);\n applyD1ReturnObjectToSpan(span, d1Result);\n createD1Breadcrumb(query, 'all', d1Result);\n return d1Result;\n });\n },\n });\n\n // eslint-disable-next-line @typescript-eslint/unbound-method\n statement.raw = new Proxy(statement.raw, {\n apply(target, thisArg, args: Parameters<typeof statement.raw>) {\n return startSpan(createStartSpanOptions(query, 'raw'), async () => {\n const res = await Reflect.apply(target, thisArg, args);\n createD1Breadcrumb(query, 'raw');\n return res;\n });\n },\n });\n\n patchedStatement.add(statement);\n\n return statement;\n}\n\n/**\n * Instruments a Cloudflare D1 prepared statement with Sentry.\n *\n * This is meant to be used as a top-level call, under the hood it calls `instrumentD1PreparedStatementQueries`\n * to patch the query methods. The reason for this abstraction is to ensure that the `bind` method is also patched.\n */\nfunction instrumentD1PreparedStatement(statement: D1PreparedStatement, query: string): D1PreparedStatement {\n // statement.bind() returns a new instance of D1PreparedStatement, so we have to patch it as well.\n // eslint-disable-next-line @typescript-eslint/unbound-method\n statement.bind = new Proxy(statement.bind, {\n apply(target, thisArg, args: Parameters<typeof statement.bind>) {\n return instrumentD1PreparedStatementQueries(Reflect.apply(target, thisArg, args), query);\n },\n });\n\n return instrumentD1PreparedStatementQueries(statement, query);\n}\n\n/**\n * Add D1Response meta information to a span.\n *\n * See: https://developers.cloudflare.com/d1/build-with-d1/d1-client-api/#return-object\n */\nfunction applyD1ReturnObjectToSpan(span: Span, d1Result: D1Response): void {\n if (!d1Result.success) {\n span.setStatus({ code: SPAN_STATUS_ERROR });\n }\n\n span.setAttributes(getAttributesFromD1Response(d1Result));\n}\n\nfunction getAttributesFromD1Response(d1Result: D1Response): SpanAttributes {\n return {\n 'cloudflare.d1.duration': d1Result.meta.duration,\n 'cloudflare.d1.rows_read': d1Result.meta.rows_read,\n 'cloudflare.d1.rows_written': d1Result.meta.rows_written,\n };\n}\n\ntype D1QueryType = 'first' | 'run' | 'all' | 'raw' | 'batch' | 'exec';\n\nfunction createD1Breadcrumb(query: string, type: D1QueryType, d1Result?: D1Response): void {\n addBreadcrumb({\n category: 'query',\n message: query,\n data: {\n ...(d1Result ? getAttributesFromD1Response(d1Result) : {}),\n 'db.operation.name': type,\n },\n });\n}\n\nfunction createStartSpanOptions(query: string, type: D1QueryType): StartSpanOptions {\n const querySummary = query ? _INTERNAL_getSqlQuerySummary(_INTERNAL_sanitizeSqlQuery(query)) : undefined;\n\n const client = getClient();\n const name = client && hasSpanStreamingEnabled(client) ? querySummary || 'cloudflare-d1' : query;\n\n return {\n name,\n attributes: {\n [SENTRY_OP]: DB_QUERY,\n 'db.system.name': 'cloudflare-d1',\n 'db.operation.name': type,\n 'db.query.text': query,\n 'db.query.summary': querySummary,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.db.cloudflare.d1',\n },\n };\n}\n\nfunction instrumentPrepare(\n prepare: D1Database['prepare'] | D1DatabaseSession['prepare'],\n): D1Database['prepare'] | D1DatabaseSession['prepare'] {\n return new Proxy(prepare, {\n apply(target, thisArg, args: Parameters<typeof prepare>) {\n const [query] = args;\n return instrumentD1PreparedStatement(Reflect.apply(target, thisArg, args), query);\n },\n });\n}\n\nfunction instrumentBatch(\n batch: D1Database['batch'] | D1DatabaseSession['batch'],\n): D1Database['batch'] | D1DatabaseSession['batch'] {\n return new Proxy(batch, {\n apply(target, thisArg, args: Parameters<typeof batch>) {\n const statements = args[0];\n // D1PreparedStatement exposes a `statement` property at runtime, but it's not in @cloudflare/workers-types.\n // https://github.com/cloudflare/workerd/blob/dc12d7650b4f5d4f9ba6a47aa45fad769cdf8db4/src/cloudflare/internal/d1-api.ts#L210\n const queryText = statements\n .map(statement => (statement as unknown as { statement?: string }).statement ?? '')\n .filter(Boolean)\n .join('\\n');\n\n return startSpan(\n {\n name: 'D1 batch',\n attributes: {\n [SENTRY_OP]: DB_QUERY,\n 'db.system.name': 'cloudflare-d1',\n 'db.operation.name': 'batch',\n 'db.query.text': queryText || undefined,\n 'db.operation.batch.size': statements.length,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.db.cloudflare.d1',\n },\n },\n async () => {\n const res = await Reflect.apply(target, thisArg, args);\n createD1Breadcrumb('D1 batch', 'batch');\n return res;\n },\n );\n },\n });\n}\n\nfunction instrumentD1Session(session: D1DatabaseSession): D1DatabaseSession {\n session.prepare = instrumentPrepare(session.prepare);\n session.batch = instrumentBatch(session.batch);\n return session;\n}\n\nfunction _instrumentD1(db: D1Database): D1Database {\n db.prepare = instrumentPrepare(db.prepare);\n db.batch = instrumentBatch(db.batch);\n\n db.exec = new Proxy(db.exec, {\n apply(target, thisArg, args: Parameters<typeof db.exec>) {\n const [query] = args;\n return startSpan(createStartSpanOptions(query, 'exec'), async () => {\n const res = await Reflect.apply(target, thisArg, args);\n createD1Breadcrumb(query, 'exec');\n return res;\n });\n },\n });\n\n if ('withSession' in db && typeof db.withSession === 'function') {\n db.withSession = new Proxy(db.withSession, {\n apply(target, thisArg, args: [unknown]) {\n return instrumentD1Session(Reflect.apply(target, thisArg, args) as D1DatabaseSession);\n },\n });\n }\n\n return db;\n}\n\nexport function instrumentD1(db: D1Database): D1Database {\n return ensureInstrumented(db, _instrumentD1);\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { isObjectLike } from '@sentry/core';
|
|
2
2
|
import { instrumentWorkersAiClient } from '@sentry/server-utils';
|
|
3
|
-
import { isD1Database, isQueue, isR2Bucket,
|
|
3
|
+
import { isD1Database, isQueue, isR2Bucket, isAiBinding, isDurableObjectNamespace, isJSRPC } from '../../utils/isBinding.js';
|
|
4
4
|
import { instrumentD1 } from './instrumentD1.js';
|
|
5
5
|
import { appendRpcMeta } from '../../utils/rpcMeta.js';
|
|
6
6
|
import { createRpcPropagationResolver } from '../../utils/rpcPropagation.js';
|
|
@@ -8,7 +8,6 @@ import { instrumentDurableObjectNamespace, STUB_NON_RPC_METHODS } from '../instr
|
|
|
8
8
|
import { instrumentFetcher } from './instrumentFetcher.js';
|
|
9
9
|
import { instrumentQueueProducer } from './instrumentQueueProducer.js';
|
|
10
10
|
import { instrumentR2Bucket } from './instrumentR2.js';
|
|
11
|
-
import { instrumentRateLimit } from './instrumentRateLimit.js';
|
|
12
11
|
|
|
13
12
|
function isProxyable(item) {
|
|
14
13
|
return isObjectLike(item) || typeof item === "function";
|
|
@@ -46,12 +45,6 @@ function instrumentEnv(env, options) {
|
|
|
46
45
|
instrumentedBindings.set(item, instrumented);
|
|
47
46
|
return instrumented;
|
|
48
47
|
}
|
|
49
|
-
if (isRateLimit(item)) {
|
|
50
|
-
const bindingName = typeof prop === "string" ? prop : String(prop);
|
|
51
|
-
const instrumented = instrumentRateLimit(item, bindingName);
|
|
52
|
-
instrumentedBindings.set(item, instrumented);
|
|
53
|
-
return instrumented;
|
|
54
|
-
}
|
|
55
48
|
if (isAiBinding(item)) {
|
|
56
49
|
const instrumented = instrumentWorkersAiClient(item);
|
|
57
50
|
instrumentedBindings.set(item, instrumented);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instrumentEnv.js","sources":["../../../../../src/instrumentations/worker/instrumentEnv.ts"],"sourcesContent":["import { isObjectLike } from '@sentry/core';\nimport { instrumentWorkersAiClient } from '@sentry/server-utils';\nimport type { CloudflareOptions } from '../../client';\nimport {\n isAiBinding,\n isD1Database,\n isDurableObjectNamespace,\n isJSRPC,\n isQueue,\n isR2Bucket,\n
|
|
1
|
+
{"version":3,"file":"instrumentEnv.js","sources":["../../../../../src/instrumentations/worker/instrumentEnv.ts"],"sourcesContent":["import { isObjectLike } from '@sentry/core';\nimport { instrumentWorkersAiClient } from '@sentry/server-utils';\nimport type { CloudflareOptions } from '../../client';\nimport {\n isAiBinding,\n isD1Database,\n isDurableObjectNamespace,\n isJSRPC,\n isQueue,\n isR2Bucket,\n} from '../../utils/isBinding';\nimport { instrumentD1 } from './instrumentD1';\nimport { appendRpcMeta } from '../../utils/rpcMeta';\nimport { createRpcPropagationResolver } from '../../utils/rpcPropagation';\nimport { instrumentDurableObjectNamespace, STUB_NON_RPC_METHODS } from '../instrumentDurableObjectNamespace';\nimport { instrumentFetcher } from './instrumentFetcher';\nimport { instrumentQueueProducer } from './instrumentQueueProducer';\nimport { instrumentR2Bucket } from './instrumentR2';\n\nfunction isProxyable(item: unknown): item is object {\n return isObjectLike(item) || typeof item === 'function';\n}\n\nconst instrumentedBindings = new WeakMap<object, unknown>();\n\n/**\n * Wraps the Cloudflare `env` object in a Proxy that detects binding types\n * on property access and returns instrumented versions.\n *\n * Currently detects:\n * - DurableObjectNamespace (via `idFromName` duck-typing)\n * - Service bindings / JSRPC proxies\n * - Queue producers (via `send` + `sendBatch` duck-typing)\n * - R2 Buckets (via `head` + `put` + `createMultipartUpload` duck-typing)\n * - Workers AI (via `run` + `gateway` + `toMarkdown` duck-typing)\n *\n * @param env - The Cloudflare env object to instrument\n * @param options - Optional CloudflareOptions to control RPC trace propagation\n */\nexport function instrumentEnv<Env extends Record<string, unknown>>(env: Env, options?: CloudflareOptions): Env {\n if (!env || typeof env !== 'object') {\n return env;\n }\n\n const shouldPropagateRpcTrace = createRpcPropagationResolver(options);\n\n return new Proxy(env, {\n get(target, prop, receiver) {\n const item = Reflect.get(target, prop, receiver);\n\n if (!isProxyable(item)) {\n return item;\n }\n\n const cached = instrumentedBindings.get(item);\n\n if (cached) {\n return cached;\n }\n\n if (isD1Database(item)) {\n const instrumented = instrumentD1(item);\n instrumentedBindings.set(item, instrumented);\n return instrumented;\n }\n\n if (isQueue(item)) {\n const bindingName = typeof prop === 'string' ? prop : String(prop);\n const instrumented = instrumentQueueProducer(item, bindingName);\n instrumentedBindings.set(item, instrumented);\n return instrumented;\n }\n\n if (isR2Bucket(item)) {\n const bindingName = typeof prop === 'string' ? prop : String(prop);\n const instrumented = instrumentR2Bucket(item, bindingName);\n instrumentedBindings.set(item, instrumented);\n return instrumented;\n }\n\n if (isAiBinding(item)) {\n const instrumented = instrumentWorkersAiClient(item);\n instrumentedBindings.set(item, instrumented);\n return instrumented;\n }\n\n const propagateRpcTrace = shouldPropagateRpcTrace(String(prop));\n\n if (isDurableObjectNamespace(item)) {\n const instrumented = instrumentDurableObjectNamespace(item, propagateRpcTrace);\n instrumentedBindings.set(item, instrumented);\n return instrumented;\n }\n\n if (isJSRPC(item)) {\n const instrumented = new Proxy(item, {\n get(target, p) {\n const value = Reflect.get(target, p);\n\n if (p === 'fetch' && typeof value === 'function') {\n return instrumentFetcher((...args) => Reflect.apply(value, target, args));\n }\n\n if (\n propagateRpcTrace &&\n typeof value === 'function' &&\n typeof p === 'string' &&\n !STUB_NON_RPC_METHODS.has(p)\n ) {\n return (...args: unknown[]) => Reflect.apply(value, target, appendRpcMeta(args));\n }\n\n return value;\n },\n });\n\n instrumentedBindings.set(item, instrumented);\n return instrumented;\n }\n\n return item;\n },\n });\n}\n"],"names":["target"],"mappings":"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"}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { SENTRY_OP } from '@sentry/conventions/attributes';
|
|
2
|
+
import { QUEUE_PUBLISH } from '@sentry/conventions/op';
|
|
3
|
+
import { startSpan, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/core';
|
|
2
4
|
|
|
3
5
|
const ORIGIN = "auto.faas.cloudflare.queue";
|
|
4
6
|
function startPublishSpan(options, callback) {
|
|
5
7
|
const { bindingName, bodySize, messageCount } = options;
|
|
6
8
|
return startSpan(
|
|
7
9
|
{
|
|
8
|
-
op: "queue.publish",
|
|
9
10
|
name: `send ${bindingName}`,
|
|
10
11
|
attributes: {
|
|
11
12
|
"messaging.system": "cloudflare",
|
|
@@ -14,7 +15,7 @@ function startPublishSpan(options, callback) {
|
|
|
14
15
|
"messaging.operation.name": "send",
|
|
15
16
|
...messageCount !== void 0 && { "messaging.batch.message_count": messageCount },
|
|
16
17
|
"messaging.message.body.size": bodySize,
|
|
17
|
-
[
|
|
18
|
+
[SENTRY_OP]: QUEUE_PUBLISH,
|
|
18
19
|
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN
|
|
19
20
|
}
|
|
20
21
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instrumentQueueProducer.js","sources":["../../../../../src/instrumentations/worker/instrumentQueueProducer.ts"],"sourcesContent":["import type { MessageSendRequest, Queue, QueueSendBatchOptions, QueueSendOptions } from '@cloudflare/workers-types';\nimport {
|
|
1
|
+
{"version":3,"file":"instrumentQueueProducer.js","sources":["../../../../../src/instrumentations/worker/instrumentQueueProducer.ts"],"sourcesContent":["import type { MessageSendRequest, Queue, QueueSendBatchOptions, QueueSendOptions } from '@cloudflare/workers-types';\nimport { SENTRY_OP } from '@sentry/conventions/attributes';\nimport { QUEUE_PUBLISH } from '@sentry/conventions/op';\nimport { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, startSpan } from '@sentry/core';\n\nconst ORIGIN = 'auto.faas.cloudflare.queue';\n\nfunction startPublishSpan<T>(\n options: {\n bindingName: string;\n bodySize: number | undefined;\n messageCount?: number;\n },\n callback: () => T,\n): T {\n const { bindingName, bodySize, messageCount } = options;\n\n return startSpan(\n {\n name: `send ${bindingName}`,\n attributes: {\n 'messaging.system': 'cloudflare',\n 'messaging.destination.name': bindingName,\n 'messaging.operation.type': 'send',\n 'messaging.operation.name': 'send',\n ...(messageCount !== undefined && { 'messaging.batch.message_count': messageCount }),\n 'messaging.message.body.size': bodySize,\n [SENTRY_OP]: QUEUE_PUBLISH,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n },\n },\n callback,\n );\n}\n\nfunction getBodySize(body: unknown): number | undefined {\n if (body == null) {\n return undefined;\n }\n\n if (typeof body === 'string') {\n return new TextEncoder().encode(body).byteLength;\n }\n\n if (body instanceof ArrayBuffer) {\n return body.byteLength;\n }\n\n if (ArrayBuffer.isView(body)) {\n return body.byteLength;\n }\n\n try {\n return new TextEncoder().encode(JSON.stringify(body)).byteLength;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Wraps a Queue producer binding to create `queue.publish` spans on\n * `send` and `sendBatch` calls.\n *\n * The queue's own name is not available on the binding object, so we use\n * the env binding key (e.g. `MY_QUEUE`) as `messaging.destination.name`.\n */\nexport function instrumentQueueProducer<T extends Queue>(queue: T, bindingName: string): T {\n return new Proxy(queue, {\n get(target, prop, receiver) {\n if (prop === 'send') {\n const original = Reflect.get(target, prop, receiver) as Queue['send'];\n\n return function (this: unknown, message: unknown, options?: QueueSendOptions): ReturnType<Queue['send']> {\n return startPublishSpan({ bindingName, bodySize: getBodySize(message) }, () =>\n Reflect.apply(original, target, [message, options]),\n );\n };\n }\n\n if (prop === 'sendBatch') {\n const original = Reflect.get(target, prop, receiver) as Queue['sendBatch'];\n return function (\n this: unknown,\n messages: Iterable<MessageSendRequest>,\n options?: QueueSendBatchOptions,\n ): ReturnType<Queue['sendBatch']> {\n const messageArray = Array.from(messages);\n const totalBodySize = messageArray.reduce<number | undefined>((acc, m) => {\n const size = getBodySize(m.body);\n if (size === undefined) {\n return acc;\n }\n return (acc ?? 0) + size;\n }, undefined);\n\n return startPublishSpan({ bindingName, bodySize: totalBodySize, messageCount: messageArray.length }, () =>\n Reflect.apply(original, target, [messageArray, options]),\n );\n };\n }\n\n return Reflect.get(target, prop, receiver);\n },\n });\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"type":"module","version":"11.0.0-
|
|
1
|
+
{"type":"module","version":"11.0.0-beta.0","sideEffects":false}
|