@sentry/cloudflare 10.55.0 → 10.57.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/client.js +3 -0
- package/build/cjs/client.js.map +1 -1
- package/build/cjs/durableobject.js +21 -20
- package/build/cjs/durableobject.js.map +1 -1
- package/build/cjs/index.js +2 -2
- package/build/cjs/instrumentations/instrumentDurableObjectStorage.js +1 -1
- package/build/cjs/instrumentations/instrumentDurableObjectStorage.js.map +1 -1
- package/build/cjs/{d1.js → instrumentations/worker/instrumentD1.js} +10 -2
- package/build/cjs/instrumentations/worker/instrumentD1.js.map +1 -0
- package/build/cjs/instrumentations/worker/instrumentEnv.js +6 -0
- package/build/cjs/instrumentations/worker/instrumentEnv.js.map +1 -1
- package/build/cjs/integrations/httpServer.js.map +1 -1
- package/build/cjs/request.js +7 -3
- package/build/cjs/request.js.map +1 -1
- package/build/cjs/sdk.js +2 -3
- package/build/cjs/sdk.js.map +1 -1
- package/build/cjs/utils/isBinding.js +4 -0
- package/build/cjs/utils/isBinding.js.map +1 -1
- package/build/cjs/utils/traceLinks.js +6 -5
- package/build/cjs/utils/traceLinks.js.map +1 -1
- package/build/cjs/workflows.js +9 -1
- package/build/cjs/workflows.js.map +1 -1
- package/build/cjs/wrapMethodWithSentry.js +43 -66
- package/build/cjs/wrapMethodWithSentry.js.map +1 -1
- package/build/esm/client.js +4 -1
- package/build/esm/client.js.map +1 -1
- package/build/esm/durableobject.js +21 -20
- package/build/esm/durableobject.js.map +1 -1
- package/build/esm/index.js +1 -1
- package/build/esm/instrumentations/instrumentDurableObjectStorage.js +1 -1
- package/build/esm/instrumentations/instrumentDurableObjectStorage.js.map +1 -1
- package/build/esm/{d1.js → instrumentations/worker/instrumentD1.js} +10 -3
- package/build/esm/instrumentations/worker/instrumentD1.js.map +1 -0
- package/build/esm/instrumentations/worker/instrumentEnv.js +7 -1
- package/build/esm/instrumentations/worker/instrumentEnv.js.map +1 -1
- package/build/esm/integrations/httpServer.js.map +1 -1
- package/build/esm/package.json +1 -1
- package/build/esm/request.js +7 -3
- package/build/esm/request.js.map +1 -1
- package/build/esm/sdk.js +2 -3
- package/build/esm/sdk.js.map +1 -1
- package/build/esm/utils/isBinding.js +4 -1
- package/build/esm/utils/isBinding.js.map +1 -1
- package/build/esm/utils/traceLinks.js +7 -6
- package/build/esm/utils/traceLinks.js.map +1 -1
- package/build/esm/workflows.js +9 -1
- package/build/esm/workflows.js.map +1 -1
- package/build/esm/wrapMethodWithSentry.js +45 -68
- package/build/esm/wrapMethodWithSentry.js.map +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 +1 -1
- package/build/types/index.d.ts.map +1 -1
- package/build/types/{d1.d.ts → instrumentations/worker/instrumentD1.d.ts} +4 -2
- package/build/types/instrumentations/worker/instrumentD1.d.ts.map +1 -0
- package/build/types/instrumentations/worker/instrumentEnv.d.ts.map +1 -1
- package/build/types/integrations/httpServer.d.ts.map +1 -1
- package/build/types/request.d.ts.map +1 -1
- package/build/types/sdk.d.ts.map +1 -1
- package/build/types/utils/isBinding.d.ts +6 -1
- package/build/types/utils/isBinding.d.ts.map +1 -1
- package/build/types/utils/traceLinks.d.ts +3 -2
- package/build/types/utils/traceLinks.d.ts.map +1 -1
- package/build/types/workflows.d.ts.map +1 -1
- package/build/types/wrapMethodWithSentry.d.ts.map +1 -1
- package/build/types-ts3.8/index.d.ts +1 -1
- package/build/types-ts3.8/{d1.d.ts → instrumentations/worker/instrumentD1.d.ts} +4 -2
- package/build/types-ts3.8/utils/isBinding.d.ts +6 -1
- package/build/types-ts3.8/utils/traceLinks.d.ts +3 -2
- package/package.json +2 -2
- package/build/cjs/d1.js.map +0 -1
- package/build/esm/d1.js.map +0 -1
- package/build/types/d1.d.ts.map +0 -1
|
@@ -8,7 +8,7 @@ const SENTRY_TRACE_LINK_KEY_PREFIX = "__SENTRY_TRACE_LINK__";
|
|
|
8
8
|
function getTraceLinkKey(methodName) {
|
|
9
9
|
return `${SENTRY_TRACE_LINK_KEY_PREFIX}${methodName}`;
|
|
10
10
|
}
|
|
11
|
-
|
|
11
|
+
function storeSpanContext(originalStorage, methodName) {
|
|
12
12
|
try {
|
|
13
13
|
const activeSpan = core.getActiveSpan();
|
|
14
14
|
if (activeSpan) {
|
|
@@ -18,16 +18,17 @@ async function storeSpanContext(originalStorage, methodName) {
|
|
|
18
18
|
spanId: spanContext.spanId,
|
|
19
19
|
sampled: core.spanIsSampled(activeSpan)
|
|
20
20
|
};
|
|
21
|
-
|
|
21
|
+
originalStorage.kv.put(getTraceLinkKey(methodName), storedContext);
|
|
22
22
|
}
|
|
23
23
|
} catch (error) {
|
|
24
24
|
debugBuild.DEBUG_BUILD && core.debug.log(`[CloudflareClient] Error storing span context for method ${methodName}`, error);
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
|
-
|
|
27
|
+
function getStoredSpanContext(originalStorage, methodName) {
|
|
28
28
|
try {
|
|
29
|
-
return
|
|
30
|
-
} catch {
|
|
29
|
+
return originalStorage.kv.get(getTraceLinkKey(methodName));
|
|
30
|
+
} catch (error) {
|
|
31
|
+
debugBuild.DEBUG_BUILD && core.debug.log(`[CloudflareClient] Error retrieving span context for method ${methodName}`, error);
|
|
31
32
|
return void 0;
|
|
32
33
|
}
|
|
33
34
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"traceLinks.js","sources":["../../../src/utils/traceLinks.ts"],"sourcesContent":["import type { DurableObjectStorage } from '@cloudflare/workers-types';\nimport { TraceFlags } from '@opentelemetry/api';\nimport type { SpanLink } from '@sentry/core';\nimport { debug, getActiveSpan, SEMANTIC_LINK_ATTRIBUTE_LINK_TYPE, spanIsSampled } from '@sentry/core';\nimport { DEBUG_BUILD } from '../debug-build';\n\n/** Storage key prefix for the span context that links consecutive method invocations */\nconst SENTRY_TRACE_LINK_KEY_PREFIX = '__SENTRY_TRACE_LINK__';\n\n/** Stored span context for creating span links */\nexport interface StoredSpanContext {\n traceId: string;\n spanId: string;\n sampled: boolean;\n}\n\n/**\n * Gets the storage key for a specific method's trace link.\n */\nexport function getTraceLinkKey(methodName: string): string {\n return `${SENTRY_TRACE_LINK_KEY_PREFIX}${methodName}`;\n}\n\n/**\n * Stores the current span context in Durable Object storage for trace linking.\n * Uses the original uninstrumented storage to avoid creating spans for internal operations.\n * Errors are silently ignored to prevent internal storage failures from propagating to user code.\n */\nexport
|
|
1
|
+
{"version":3,"file":"traceLinks.js","sources":["../../../src/utils/traceLinks.ts"],"sourcesContent":["import type { DurableObjectStorage } from '@cloudflare/workers-types';\nimport { TraceFlags } from '@opentelemetry/api';\nimport type { SpanLink } from '@sentry/core';\nimport { debug, getActiveSpan, SEMANTIC_LINK_ATTRIBUTE_LINK_TYPE, spanIsSampled } from '@sentry/core';\nimport { DEBUG_BUILD } from '../debug-build';\n\n/** Storage key prefix for the span context that links consecutive method invocations */\nconst SENTRY_TRACE_LINK_KEY_PREFIX = '__SENTRY_TRACE_LINK__';\n\n/** Stored span context for creating span links */\nexport interface StoredSpanContext {\n traceId: string;\n spanId: string;\n sampled: boolean;\n}\n\n/**\n * Gets the storage key for a specific method's trace link.\n */\nexport function getTraceLinkKey(methodName: string): string {\n return `${SENTRY_TRACE_LINK_KEY_PREFIX}${methodName}`;\n}\n\n/**\n * Stores the current span context in Durable Object storage for trace linking.\n * Uses the original uninstrumented storage to avoid creating spans for internal operations.\n * Errors are silently ignored to prevent internal storage failures from propagating to user code.\n */\nexport function storeSpanContext(originalStorage: DurableObjectStorage, methodName: string): void {\n try {\n const activeSpan = getActiveSpan();\n if (activeSpan) {\n const spanContext = activeSpan.spanContext();\n const storedContext: StoredSpanContext = {\n traceId: spanContext.traceId,\n spanId: spanContext.spanId,\n sampled: spanIsSampled(activeSpan),\n };\n\n originalStorage.kv.put(getTraceLinkKey(methodName), storedContext);\n }\n } catch (error) {\n // Silently ignore storage errors to prevent internal failures from affecting user code\n DEBUG_BUILD && debug.log(`[CloudflareClient] Error storing span context for method ${methodName}`, error);\n }\n}\n\n/**\n * Retrieves a stored span context from Durable Object storage.\n * Errors are silently ignored to prevent internal storage failures from propagating to user code.\n */\nexport function getStoredSpanContext(\n originalStorage: DurableObjectStorage,\n methodName: string,\n): StoredSpanContext | undefined {\n try {\n return originalStorage.kv.get<StoredSpanContext>(getTraceLinkKey(methodName));\n } catch (error) {\n // Silently ignore storage errors to prevent internal failures from affecting user code\n DEBUG_BUILD && debug.log(`[CloudflareClient] Error retrieving span context for method ${methodName}`, error);\n return undefined;\n }\n}\n\n/**\n * Builds span links from a stored span context.\n */\nexport function buildSpanLinks(storedContext: StoredSpanContext): SpanLink[] {\n return [\n {\n context: {\n traceId: storedContext.traceId,\n spanId: storedContext.spanId,\n traceFlags: storedContext.sampled ? TraceFlags.SAMPLED : TraceFlags.NONE,\n },\n attributes: {\n [SEMANTIC_LINK_ATTRIBUTE_LINK_TYPE]: 'previous_trace',\n },\n },\n ];\n}\n"],"names":["getActiveSpan","spanIsSampled","DEBUG_BUILD","debug","TraceFlags","SEMANTIC_LINK_ATTRIBUTE_LINK_TYPE"],"mappings":";;;;;;AAOA,MAAM,4BAAA,GAA+B,uBAAA;AAY9B,SAAS,gBAAgB,UAAA,EAA4B;AAC1D,EAAA,OAAO,CAAA,EAAG,4BAA4B,CAAA,EAAG,UAAU,CAAA,CAAA;AACrD;AAOO,SAAS,gBAAA,CAAiB,iBAAuC,UAAA,EAA0B;AAChG,EAAA,IAAI;AACF,IAAA,MAAM,aAAaA,kBAAA,EAAc;AACjC,IAAA,IAAI,UAAA,EAAY;AACd,MAAA,MAAM,WAAA,GAAc,WAAW,WAAA,EAAY;AAC3C,MAAA,MAAM,aAAA,GAAmC;AAAA,QACvC,SAAS,WAAA,CAAY,OAAA;AAAA,QACrB,QAAQ,WAAA,CAAY,MAAA;AAAA,QACpB,OAAA,EAASC,mBAAc,UAAU;AAAA,OACnC;AAEA,MAAA,eAAA,CAAgB,EAAA,CAAG,GAAA,CAAI,eAAA,CAAgB,UAAU,GAAG,aAAa,CAAA;AAAA,IACnE;AAAA,EACF,SAAS,KAAA,EAAO;AAEd,IAAAC,sBAAA,IAAeC,UAAA,CAAM,GAAA,CAAI,CAAA,yDAAA,EAA4D,UAAU,IAAI,KAAK,CAAA;AAAA,EAC1G;AACF;AAMO,SAAS,oBAAA,CACd,iBACA,UAAA,EAC+B;AAC/B,EAAA,IAAI;AACF,IAAA,OAAO,eAAA,CAAgB,EAAA,CAAG,GAAA,CAAuB,eAAA,CAAgB,UAAU,CAAC,CAAA;AAAA,EAC9E,SAAS,KAAA,EAAO;AAEd,IAAAD,sBAAA,IAAeC,UAAA,CAAM,GAAA,CAAI,CAAA,4DAAA,EAA+D,UAAU,IAAI,KAAK,CAAA;AAC3G,IAAA,OAAO,MAAA;AAAA,EACT;AACF;AAKO,SAAS,eAAe,aAAA,EAA8C;AAC3E,EAAA,OAAO;AAAA,IACL;AAAA,MACE,OAAA,EAAS;AAAA,QACP,SAAS,aAAA,CAAc,OAAA;AAAA,QACvB,QAAQ,aAAA,CAAc,MAAA;AAAA,QACtB,UAAA,EAAY,aAAA,CAAc,OAAA,GAAUC,cAAA,CAAW,UAAUA,cAAA,CAAW;AAAA,OACtE;AAAA,MACA,UAAA,EAAY;AAAA,QACV,CAACC,sCAAiC,GAAG;AAAA;AACvC;AACF,GACF;AACF;;;;;;;"}
|
package/build/cjs/workflows.js
CHANGED
|
@@ -33,6 +33,11 @@ class WrappedWorkflowStep {
|
|
|
33
33
|
const userCallback = maybeCallback || configOrCallback;
|
|
34
34
|
const config = typeof configOrCallback === "function" ? void 0 : configOrCallback;
|
|
35
35
|
const instrumentedCallback = async (...args) => {
|
|
36
|
+
const stepContext = args[0];
|
|
37
|
+
const attempt = stepContext?.attempt;
|
|
38
|
+
const retryLimit = stepContext?.config?.retries?.limit;
|
|
39
|
+
const hasStepContext = typeof attempt === "number" && typeof retryLimit === "number";
|
|
40
|
+
const isFinalAttempt = !hasStepContext || attempt > retryLimit;
|
|
36
41
|
return core.startSpan(
|
|
37
42
|
{
|
|
38
43
|
op: "function.step.do",
|
|
@@ -43,6 +48,7 @@ class WrappedWorkflowStep {
|
|
|
43
48
|
"cloudflare.workflow.retries.backoff": config?.retries?.backoff,
|
|
44
49
|
"cloudflare.workflow.retries.delay": config?.retries?.delay,
|
|
45
50
|
"cloudflare.workflow.retries.limit": config?.retries?.limit,
|
|
51
|
+
"cloudflare.workflow.attempt": attempt,
|
|
46
52
|
[core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.faas.cloudflare.workflow",
|
|
47
53
|
[core.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: "task"
|
|
48
54
|
}
|
|
@@ -53,7 +59,9 @@ class WrappedWorkflowStep {
|
|
|
53
59
|
span.setStatus({ code: 1 });
|
|
54
60
|
return result;
|
|
55
61
|
} catch (error) {
|
|
56
|
-
|
|
62
|
+
if (isFinalAttempt) {
|
|
63
|
+
core.captureException(error, { mechanism: { handled: true, type: "auto.faas.cloudflare.workflow" } });
|
|
64
|
+
}
|
|
57
65
|
throw error;
|
|
58
66
|
} finally {
|
|
59
67
|
this._waitUntil(core.flush(2e3));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflows.js","sources":["../../src/workflows.ts"],"sourcesContent":["import type { PropagationContext } from '@sentry/core';\nimport {\n captureException,\n flush,\n getCurrentScope,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,\n startSpan,\n withIsolationScope,\n withScope,\n} from '@sentry/core';\nimport type {\n WorkflowEntrypoint,\n WorkflowEvent,\n WorkflowSleepDuration,\n WorkflowStep,\n WorkflowStepConfig,\n WorkflowStepEvent,\n WorkflowTimeoutDuration,\n} from 'cloudflare:workers';\nimport { setAsyncLocalStorageAsyncContextStrategy } from './async';\nimport type { CloudflareOptions } from './client';\nimport { flushAndDispose } from './flush';\nimport { instrumentEnv } from './instrumentations/worker/instrumentEnv';\nimport { addCloudResourceContext } from './scope-utils';\nimport { init } from './sdk';\nimport { instrumentContext } from './utils/instrumentContext';\n\nconst UUID_REGEX = /^[0-9a-f]{8}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{12}$/i;\n\n/**\n * Hashes a string to a UUID using SHA-1.\n */\nexport async function deterministicTraceIdFromInstanceId(instanceId: string): Promise<string> {\n const buf = await crypto.subtle.digest('SHA-1', new TextEncoder().encode(instanceId));\n return (\n Array.from(new Uint8Array(buf))\n // We only need the first 16 bytes for the 32 characters\n .slice(0, 16)\n .map(b => b.toString(16).padStart(2, '0'))\n .join('')\n );\n}\n\nasync function propagationContextFromInstanceId(instanceId: string): Promise<PropagationContext> {\n const traceId = UUID_REGEX.test(instanceId)\n ? instanceId.replace(/-/g, '')\n : await deterministicTraceIdFromInstanceId(instanceId);\n\n // Derive sampleRand from last 4 characters of the random UUID\n //\n // We cannot store any state between workflow steps, so we derive the\n // sampleRand from the traceId itself. This ensures that the sampling is\n // consistent across all steps in the same workflow instance.\n const sampleRand = parseInt(traceId.slice(-4), 16) / 0xffff;\n\n return {\n traceId,\n sampleRand,\n };\n}\n\nclass WrappedWorkflowStep implements WorkflowStep {\n public constructor(\n private _instanceId: string,\n private _options: CloudflareOptions,\n private _step: WorkflowStep,\n private _waitUntil: ExecutionContext['waitUntil'],\n ) {}\n\n public async do<T extends Rpc.Serializable<T>>(\n name: string,\n callback: (...args: unknown[]) => Promise<T>,\n ): Promise<T>;\n public async do<T extends Rpc.Serializable<T>>(\n name: string,\n config: WorkflowStepConfig,\n callback: (...args: unknown[]) => Promise<T>,\n ): Promise<T>;\n public async do<T extends Rpc.Serializable<T>>(\n name: string,\n configOrCallback: WorkflowStepConfig | (() => Promise<T>),\n maybeCallback?: (...args: unknown[]) => Promise<T>,\n ): Promise<T> {\n // Capture the current scope, so parent span (e.g., a startSpan surrounding step.do) is preserved\n const scopeForStep = getCurrentScope();\n\n const userCallback = (maybeCallback || configOrCallback) as (...args: unknown[]) => Promise<T>;\n const config = typeof configOrCallback === 'function' ? undefined : configOrCallback;\n\n const instrumentedCallback = async (...args: unknown[]): Promise<T> => {\n return startSpan(\n {\n op: 'function.step.do',\n name,\n scope: scopeForStep,\n attributes: {\n 'cloudflare.workflow.timeout': config?.timeout,\n 'cloudflare.workflow.retries.backoff': config?.retries?.backoff,\n 'cloudflare.workflow.retries.delay': config?.retries?.delay,\n 'cloudflare.workflow.retries.limit': config?.retries?.limit,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.faas.cloudflare.workflow',\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'task',\n },\n },\n async span => {\n try {\n const result = await userCallback(...args);\n span.setStatus({ code: 1 });\n return result;\n } catch (error) {\n captureException(error, { mechanism: { handled: true, type: 'auto.faas.cloudflare.workflow' } });\n throw error;\n } finally {\n this._waitUntil(flush(2000));\n }\n },\n );\n };\n\n return config ? this._step.do(name, config, instrumentedCallback) : this._step.do(name, instrumentedCallback);\n }\n\n public async sleep(name: string, duration: WorkflowSleepDuration): Promise<void> {\n return this._step.sleep(name, duration);\n }\n\n public async sleepUntil(name: string, timestamp: Date | number): Promise<void> {\n return this._step.sleepUntil(name, timestamp);\n }\n\n public async waitForEvent<T extends Rpc.Serializable<T>>(\n name: string,\n options: { type: string; timeout?: WorkflowTimeoutDuration | number },\n ): Promise<WorkflowStepEvent<T>> {\n return this._step.waitForEvent<T>(name, options);\n }\n}\n\n/**\n * Instruments a Cloudflare Workflow class with Sentry.\n *\n * @example\n * ```typescript\n * const InstrumentedWorkflow = instrumentWorkflowWithSentry(\n * (env) => ({ dsn: env.SENTRY_DSN }),\n * MyWorkflowClass\n * );\n *\n * export default InstrumentedWorkflow;\n * ```\n *\n * @param optionsCallback - Function that returns Sentry options to initialize Sentry\n * @param WorkflowClass - The workflow class to instrument\n * @returns Instrumented workflow class with the same interface\n */\nexport function instrumentWorkflowWithSentry<\n E, // Environment type\n P, // Payload type\n T extends WorkflowEntrypoint<E, P>, // WorkflowEntrypoint type\n C extends new (ctx: ExecutionContext, env: E) => T, // Constructor type of the WorkflowEntrypoint class\n>(optionsCallback: (env: E) => CloudflareOptions, WorkFlowClass: C): C {\n return new Proxy(WorkFlowClass, {\n construct(target: C, args: [ctx: ExecutionContext, env: E], newTarget) {\n const [ctx, env] = args;\n const context = instrumentContext(ctx);\n const options = optionsCallback(env);\n args[0] = context;\n args[1] = instrumentEnv(env as Record<string, unknown>, options) as E;\n const instance = Reflect.construct(target, args, newTarget) as T;\n return new Proxy(instance, {\n get(obj, prop, receiver) {\n if (prop === 'run') {\n return async function (event: WorkflowEvent<P>, step: WorkflowStep): Promise<unknown> {\n setAsyncLocalStorageAsyncContextStrategy();\n\n return withIsolationScope(async isolationScope => {\n const waitUntil = context.waitUntil.bind(context);\n const client = init({ ...options, ctx: context, enableDedupe: false });\n isolationScope.setClient(client);\n\n addCloudResourceContext(isolationScope);\n\n return withScope(async scope => {\n const propagationContext = await propagationContextFromInstanceId(event.instanceId);\n scope.setPropagationContext(propagationContext);\n\n try {\n return await obj.run.call(\n obj,\n event,\n new WrappedWorkflowStep(event.instanceId, options, step, waitUntil),\n );\n } finally {\n waitUntil(flushAndDispose(client));\n }\n });\n });\n };\n }\n return Reflect.get(obj, prop, receiver);\n },\n });\n },\n });\n}\n"],"names":["getCurrentScope","startSpan","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","SEMANTIC_ATTRIBUTE_SENTRY_SOURCE","captureException","flush","instrumentContext","instrumentEnv","setAsyncLocalStorageAsyncContextStrategy","withIsolationScope","init","addCloudResourceContext","withScope","flushAndDispose"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"workflows.js","sources":["../../src/workflows.ts"],"sourcesContent":["import type { PropagationContext } from '@sentry/core';\nimport {\n captureException,\n flush,\n getCurrentScope,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,\n startSpan,\n withIsolationScope,\n withScope,\n} from '@sentry/core';\nimport type {\n WorkflowEntrypoint,\n WorkflowEvent,\n WorkflowSleepDuration,\n WorkflowStep,\n WorkflowStepConfig,\n WorkflowStepEvent,\n WorkflowTimeoutDuration,\n} from 'cloudflare:workers';\nimport { setAsyncLocalStorageAsyncContextStrategy } from './async';\nimport type { CloudflareOptions } from './client';\nimport { flushAndDispose } from './flush';\nimport { instrumentEnv } from './instrumentations/worker/instrumentEnv';\nimport { addCloudResourceContext } from './scope-utils';\nimport { init } from './sdk';\nimport { instrumentContext } from './utils/instrumentContext';\n\nconst UUID_REGEX = /^[0-9a-f]{8}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{12}$/i;\n\n/**\n * Hashes a string to a UUID using SHA-1.\n */\nexport async function deterministicTraceIdFromInstanceId(instanceId: string): Promise<string> {\n const buf = await crypto.subtle.digest('SHA-1', new TextEncoder().encode(instanceId));\n return (\n Array.from(new Uint8Array(buf))\n // We only need the first 16 bytes for the 32 characters\n .slice(0, 16)\n .map(b => b.toString(16).padStart(2, '0'))\n .join('')\n );\n}\n\nasync function propagationContextFromInstanceId(instanceId: string): Promise<PropagationContext> {\n const traceId = UUID_REGEX.test(instanceId)\n ? instanceId.replace(/-/g, '')\n : await deterministicTraceIdFromInstanceId(instanceId);\n\n // Derive sampleRand from last 4 characters of the random UUID\n //\n // We cannot store any state between workflow steps, so we derive the\n // sampleRand from the traceId itself. This ensures that the sampling is\n // consistent across all steps in the same workflow instance.\n const sampleRand = parseInt(traceId.slice(-4), 16) / 0xffff;\n\n return {\n traceId,\n sampleRand,\n };\n}\n\nclass WrappedWorkflowStep implements WorkflowStep {\n public constructor(\n private _instanceId: string,\n private _options: CloudflareOptions,\n private _step: WorkflowStep,\n private _waitUntil: ExecutionContext['waitUntil'],\n ) {}\n\n public async do<T extends Rpc.Serializable<T>>(\n name: string,\n callback: (...args: unknown[]) => Promise<T>,\n ): Promise<T>;\n public async do<T extends Rpc.Serializable<T>>(\n name: string,\n config: WorkflowStepConfig,\n callback: (...args: unknown[]) => Promise<T>,\n ): Promise<T>;\n public async do<T extends Rpc.Serializable<T>>(\n name: string,\n configOrCallback: WorkflowStepConfig | (() => Promise<T>),\n maybeCallback?: (...args: unknown[]) => Promise<T>,\n ): Promise<T> {\n // Capture the current scope, so parent span (e.g., a startSpan surrounding step.do) is preserved\n const scopeForStep = getCurrentScope();\n\n const userCallback = (maybeCallback || configOrCallback) as (...args: unknown[]) => Promise<T>;\n const config = typeof configOrCallback === 'function' ? undefined : configOrCallback;\n\n const instrumentedCallback = async (...args: unknown[]): Promise<T> => {\n // Feature detection: Cloudflare Workflows (April 2026+) pass a step context\n // with `attempt` and `config.retries.limit`. When available, we only capture\n // errors on the final attempt to avoid duplicates during retries.\n const stepContext = args[0] as { attempt?: number; config?: { retries?: { limit?: number } } } | undefined;\n const attempt = stepContext?.attempt;\n const retryLimit = stepContext?.config?.retries?.limit;\n const hasStepContext = typeof attempt === 'number' && typeof retryLimit === 'number';\n\n // Only capture error on final attempt (attempt > retryLimit means no more retries left)\n // or when step context is unavailable (legacy behavior - capture all errors)\n const isFinalAttempt = !hasStepContext || attempt > retryLimit;\n\n return startSpan(\n {\n op: 'function.step.do',\n name,\n scope: scopeForStep,\n attributes: {\n 'cloudflare.workflow.timeout': config?.timeout,\n 'cloudflare.workflow.retries.backoff': config?.retries?.backoff,\n 'cloudflare.workflow.retries.delay': config?.retries?.delay,\n 'cloudflare.workflow.retries.limit': config?.retries?.limit,\n 'cloudflare.workflow.attempt': attempt,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.faas.cloudflare.workflow',\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'task',\n },\n },\n async span => {\n try {\n const result = await userCallback(...args);\n span.setStatus({ code: 1 });\n return result;\n } catch (error) {\n if (isFinalAttempt) {\n captureException(error, { mechanism: { handled: true, type: 'auto.faas.cloudflare.workflow' } });\n }\n throw error;\n } finally {\n this._waitUntil(flush(2000));\n }\n },\n );\n };\n\n return config ? this._step.do(name, config, instrumentedCallback) : this._step.do(name, instrumentedCallback);\n }\n\n public async sleep(name: string, duration: WorkflowSleepDuration): Promise<void> {\n return this._step.sleep(name, duration);\n }\n\n public async sleepUntil(name: string, timestamp: Date | number): Promise<void> {\n return this._step.sleepUntil(name, timestamp);\n }\n\n public async waitForEvent<T extends Rpc.Serializable<T>>(\n name: string,\n options: { type: string; timeout?: WorkflowTimeoutDuration | number },\n ): Promise<WorkflowStepEvent<T>> {\n return this._step.waitForEvent<T>(name, options);\n }\n}\n\n/**\n * Instruments a Cloudflare Workflow class with Sentry.\n *\n * @example\n * ```typescript\n * const InstrumentedWorkflow = instrumentWorkflowWithSentry(\n * (env) => ({ dsn: env.SENTRY_DSN }),\n * MyWorkflowClass\n * );\n *\n * export default InstrumentedWorkflow;\n * ```\n *\n * @param optionsCallback - Function that returns Sentry options to initialize Sentry\n * @param WorkflowClass - The workflow class to instrument\n * @returns Instrumented workflow class with the same interface\n */\nexport function instrumentWorkflowWithSentry<\n E, // Environment type\n P, // Payload type\n T extends WorkflowEntrypoint<E, P>, // WorkflowEntrypoint type\n C extends new (ctx: ExecutionContext, env: E) => T, // Constructor type of the WorkflowEntrypoint class\n>(optionsCallback: (env: E) => CloudflareOptions, WorkFlowClass: C): C {\n return new Proxy(WorkFlowClass, {\n construct(target: C, args: [ctx: ExecutionContext, env: E], newTarget) {\n const [ctx, env] = args;\n const context = instrumentContext(ctx);\n const options = optionsCallback(env);\n args[0] = context;\n args[1] = instrumentEnv(env as Record<string, unknown>, options) as E;\n const instance = Reflect.construct(target, args, newTarget) as T;\n return new Proxy(instance, {\n get(obj, prop, receiver) {\n if (prop === 'run') {\n return async function (event: WorkflowEvent<P>, step: WorkflowStep): Promise<unknown> {\n setAsyncLocalStorageAsyncContextStrategy();\n\n return withIsolationScope(async isolationScope => {\n const waitUntil = context.waitUntil.bind(context);\n const client = init({ ...options, ctx: context, enableDedupe: false });\n isolationScope.setClient(client);\n\n addCloudResourceContext(isolationScope);\n\n return withScope(async scope => {\n const propagationContext = await propagationContextFromInstanceId(event.instanceId);\n scope.setPropagationContext(propagationContext);\n\n try {\n return await obj.run.call(\n obj,\n event,\n new WrappedWorkflowStep(event.instanceId, options, step, waitUntil),\n );\n } finally {\n waitUntil(flushAndDispose(client));\n }\n });\n });\n };\n }\n return Reflect.get(obj, prop, receiver);\n },\n });\n },\n });\n}\n"],"names":["getCurrentScope","startSpan","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","SEMANTIC_ATTRIBUTE_SENTRY_SOURCE","captureException","flush","instrumentContext","instrumentEnv","setAsyncLocalStorageAsyncContextStrategy","withIsolationScope","init","addCloudResourceContext","withScope","flushAndDispose"],"mappings":"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"}
|
|
@@ -7,6 +7,18 @@ const sdk = require('./sdk.js');
|
|
|
7
7
|
const rpcMeta = require('./utils/rpcMeta.js');
|
|
8
8
|
const traceLinks = require('./utils/traceLinks.js');
|
|
9
9
|
|
|
10
|
+
function resolveOriginalStorage(context, thisArg) {
|
|
11
|
+
if (thisArg && typeof thisArg === "object" && "ctx" in thisArg) {
|
|
12
|
+
const doCtx = thisArg.ctx;
|
|
13
|
+
if (doCtx?.originalStorage) {
|
|
14
|
+
return doCtx.originalStorage;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
if (context && "originalStorage" in context && context.originalStorage) {
|
|
18
|
+
return context.originalStorage;
|
|
19
|
+
}
|
|
20
|
+
return void 0;
|
|
21
|
+
}
|
|
10
22
|
function wrapMethodWithSentry(wrapperOptions, handler, callback, noMark) {
|
|
11
23
|
return instrument.ensureInstrumented(
|
|
12
24
|
handler,
|
|
@@ -25,7 +37,7 @@ function wrapMethodWithSentry(wrapperOptions, handler, callback, noMark) {
|
|
|
25
37
|
const wrappedFunction = (scope) => {
|
|
26
38
|
const context = wrapperOptions.context;
|
|
27
39
|
const waitUntil = context?.waitUntil?.bind?.(context);
|
|
28
|
-
const storage = context
|
|
40
|
+
const storage = resolveOriginalStorage(context, thisArg);
|
|
29
41
|
let scopeClient = scope.getClient();
|
|
30
42
|
if (startNewTrace || !scopeClient?.getTransport()) {
|
|
31
43
|
const client = sdk.init({
|
|
@@ -39,10 +51,24 @@ function wrapMethodWithSentry(wrapperOptions, handler, callback, noMark) {
|
|
|
39
51
|
const methodName = wrapperOptions.spanName || "unknown";
|
|
40
52
|
const teardown = async () => {
|
|
41
53
|
if (startNewTrace && storage) {
|
|
42
|
-
|
|
54
|
+
traceLinks.storeSpanContext(storage, methodName);
|
|
43
55
|
}
|
|
44
56
|
await flush.flushAndDispose(clientToDispose);
|
|
45
57
|
};
|
|
58
|
+
const onFulfilled = (res) => {
|
|
59
|
+
waitUntil?.(teardown());
|
|
60
|
+
return res;
|
|
61
|
+
};
|
|
62
|
+
const onRejected = (e) => {
|
|
63
|
+
core.captureException(e, {
|
|
64
|
+
mechanism: {
|
|
65
|
+
type: "auto.faas.cloudflare.durable_object",
|
|
66
|
+
handled: false
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
waitUntil?.(teardown());
|
|
70
|
+
throw e;
|
|
71
|
+
};
|
|
46
72
|
if (!wrapperOptions.spanName) {
|
|
47
73
|
try {
|
|
48
74
|
if (callback) {
|
|
@@ -50,35 +76,12 @@ function wrapMethodWithSentry(wrapperOptions, handler, callback, noMark) {
|
|
|
50
76
|
}
|
|
51
77
|
const result = Reflect.apply(target, thisArg, args);
|
|
52
78
|
if (core.isThenable(result)) {
|
|
53
|
-
return result.then(
|
|
54
|
-
(res) => {
|
|
55
|
-
waitUntil?.(teardown());
|
|
56
|
-
return res;
|
|
57
|
-
},
|
|
58
|
-
(e) => {
|
|
59
|
-
core.captureException(e, {
|
|
60
|
-
mechanism: {
|
|
61
|
-
type: "auto.faas.cloudflare.durable_object",
|
|
62
|
-
handled: false
|
|
63
|
-
}
|
|
64
|
-
});
|
|
65
|
-
waitUntil?.(teardown());
|
|
66
|
-
throw e;
|
|
67
|
-
}
|
|
68
|
-
);
|
|
79
|
+
return result.then(onFulfilled, onRejected);
|
|
69
80
|
} else {
|
|
70
|
-
|
|
71
|
-
return result;
|
|
81
|
+
return onFulfilled(result);
|
|
72
82
|
}
|
|
73
83
|
} catch (e) {
|
|
74
|
-
|
|
75
|
-
mechanism: {
|
|
76
|
-
type: "auto.faas.cloudflare.durable_object",
|
|
77
|
-
handled: false
|
|
78
|
-
}
|
|
79
|
-
});
|
|
80
|
-
waitUntil?.(teardown());
|
|
81
|
-
throw e;
|
|
84
|
+
return onRejected(e);
|
|
82
85
|
}
|
|
83
86
|
}
|
|
84
87
|
const attributes = wrapperOptions.spanOp ? {
|
|
@@ -88,51 +91,25 @@ function wrapMethodWithSentry(wrapperOptions, handler, callback, noMark) {
|
|
|
88
91
|
const executeSpan = () => {
|
|
89
92
|
return core.startSpan({ name: methodName, attributes }, (span) => {
|
|
90
93
|
if (startNewTrace && storage) {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}
|
|
101
|
-
})
|
|
102
|
-
);
|
|
94
|
+
const storedContext = traceLinks.getStoredSpanContext(storage, methodName);
|
|
95
|
+
if (storedContext) {
|
|
96
|
+
span.addLinks(traceLinks.buildSpanLinks(storedContext));
|
|
97
|
+
const sampledFlag = storedContext.sampled ? "1" : "0";
|
|
98
|
+
span.setAttribute(
|
|
99
|
+
"sentry.previous_trace",
|
|
100
|
+
`${storedContext.traceId}-${storedContext.spanId}-${sampledFlag}`
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
103
|
}
|
|
104
104
|
try {
|
|
105
105
|
const result = Reflect.apply(target, thisArg, args);
|
|
106
106
|
if (core.isThenable(result)) {
|
|
107
|
-
return result.then(
|
|
108
|
-
(res) => {
|
|
109
|
-
waitUntil?.(teardown());
|
|
110
|
-
return res;
|
|
111
|
-
},
|
|
112
|
-
(e) => {
|
|
113
|
-
core.captureException(e, {
|
|
114
|
-
mechanism: {
|
|
115
|
-
type: "auto.faas.cloudflare.durable_object",
|
|
116
|
-
handled: false
|
|
117
|
-
}
|
|
118
|
-
});
|
|
119
|
-
waitUntil?.(teardown());
|
|
120
|
-
throw e;
|
|
121
|
-
}
|
|
122
|
-
);
|
|
107
|
+
return result.then(onFulfilled, onRejected);
|
|
123
108
|
} else {
|
|
124
|
-
|
|
125
|
-
return result;
|
|
109
|
+
return onFulfilled(result);
|
|
126
110
|
}
|
|
127
111
|
} catch (e) {
|
|
128
|
-
|
|
129
|
-
mechanism: {
|
|
130
|
-
type: "auto.faas.cloudflare.durable_object",
|
|
131
|
-
handled: false
|
|
132
|
-
}
|
|
133
|
-
});
|
|
134
|
-
waitUntil?.(teardown());
|
|
135
|
-
throw e;
|
|
112
|
+
return onRejected(e);
|
|
136
113
|
}
|
|
137
114
|
});
|
|
138
115
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wrapMethodWithSentry.js","sources":["../../src/wrapMethodWithSentry.ts"],"sourcesContent":["import type { DurableObjectStorage } from '@cloudflare/workers-types';\nimport type { SerializedTraceData } from '@sentry/core';\nimport {\n captureException,\n continueTrace,\n getClient,\n isThenable,\n type Scope,\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n startNewTrace as startNewTraceCore,\n startSpan,\n withIsolationScope,\n withScope,\n} from '@sentry/core';\nimport type { CloudflareOptions } from './client';\nimport { flushAndDispose } from './flush';\nimport { ensureInstrumented } from './instrument';\nimport { init } from './sdk';\nimport { extractRpcMeta } from './utils/rpcMeta';\nimport { buildSpanLinks, getStoredSpanContext, storeSpanContext } from './utils/traceLinks';\n\n/** Extended DurableObjectState with originalStorage exposed by instrumentContext */\ninterface InstrumentedDurableObjectState extends DurableObjectState {\n originalStorage?: DurableObjectStorage;\n}\n\ntype MethodWrapperOptions = {\n spanName?: string;\n spanOp?: string;\n options: CloudflareOptions;\n context: ExecutionContext | InstrumentedDurableObjectState;\n /**\n * If true, starts a fresh trace instead of inheriting from a parent trace.\n * Useful for scheduled/independent invocations like alarms.\n *\n * If true, it also stores the current span context and links to the previous invocation's span.\n * Uses Durable Object storage to persist the link. The link is set asynchronously via `span.addLinks()`\n * in a `waitUntil` to avoid blocking.\n *\n * @default false\n */\n startNewTrace?: boolean;\n};\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type UncheckedMethod = (...args: any[]) => any;\ntype OriginalMethod = UncheckedMethod;\n\n/**\n * Wraps a method with Sentry error tracking and optional tracing.\n * Supports starting new traces and linking to previous invocations via Durable Object storage.\n *\n * @param wrapperOptions - The options for the wrapper.\n * @param handler - The method to wrap.\n * @param callback - The callback to call.\n * @param noMark - Whether to mark the method as instrumented.\n * @returns The wrapped method.\n */\nexport function wrapMethodWithSentry<T extends OriginalMethod>(\n wrapperOptions: MethodWrapperOptions,\n handler: T,\n callback?: (...args: Parameters<T>) => void,\n noMark?: true,\n): T {\n return ensureInstrumented(\n handler,\n original =>\n new Proxy(original, {\n apply(target, thisArg, rawArgs: Parameters<T>) {\n const { startNewTrace } = wrapperOptions;\n\n // For RPC methods, extract Sentry trace context from the trailing argument.\n // The caller side (instrumentDurableObjectStub / JSRPC proxy) appends it;\n // we strip it here so the user's method never sees it.\n let args = rawArgs;\n let rpcMeta: SerializedTraceData | undefined;\n\n if (wrapperOptions.spanOp === 'rpc') {\n const extracted = extractRpcMeta(rawArgs);\n args = extracted.args;\n rpcMeta = extracted.rpcMeta;\n }\n\n // For startNewTrace, always use withIsolationScope to ensure a fresh scope\n // Otherwise, use existing client's scope or isolation scope\n const currentClient = getClient();\n const sentryWithScope = startNewTrace ? withIsolationScope : currentClient ? withScope : withIsolationScope;\n\n const wrappedFunction = (scope: Scope): unknown | Promise<unknown> => {\n // In certain situations, the passed context can become undefined.\n // For example, for Astro while prerendering pages at build time.\n // see: https://github.com/getsentry/sentry-javascript/issues/13217\n const context: typeof wrapperOptions.context | undefined = wrapperOptions.context;\n\n const waitUntil = context?.waitUntil?.bind?.(context);\n const storage = context && 'originalStorage' in context ? context.originalStorage : undefined;\n\n let scopeClient = scope.getClient();\n // Check if client exists AND is still usable (transport not disposed)\n // This handles the case where a previous handler disposed the client\n // but the scope still holds a reference to it (e.g., alarm handlers in Durable Objects)\n // For startNewTrace, always create a fresh client\n if (startNewTrace || !scopeClient?.getTransport()) {\n const client = init({\n ...wrapperOptions.options,\n ctx: context as unknown as ExecutionContext | undefined,\n });\n scope.setClient(client);\n scopeClient = client;\n }\n\n const clientToDispose = scopeClient;\n const methodName = wrapperOptions.spanName || 'unknown';\n\n const teardown = async (): Promise<void> => {\n if (startNewTrace && storage) {\n await storeSpanContext(storage, methodName);\n }\n await flushAndDispose(clientToDispose);\n };\n\n if (!wrapperOptions.spanName) {\n try {\n if (callback) {\n callback(...args);\n }\n\n const result = Reflect.apply(target, thisArg, args);\n\n if (isThenable(result)) {\n return result.then(\n (res: unknown) => {\n waitUntil?.(teardown());\n return res;\n },\n (e: unknown) => {\n captureException(e, {\n mechanism: {\n type: 'auto.faas.cloudflare.durable_object',\n handled: false,\n },\n });\n waitUntil?.(teardown());\n throw e;\n },\n );\n } else {\n waitUntil?.(teardown());\n return result;\n }\n } catch (e) {\n captureException(e, {\n mechanism: {\n type: 'auto.faas.cloudflare.durable_object',\n handled: false,\n },\n });\n waitUntil?.(teardown());\n throw e;\n }\n }\n\n const attributes = wrapperOptions.spanOp\n ? {\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: wrapperOptions.spanOp,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.faas.cloudflare.durable_object',\n }\n : {};\n\n const executeSpan = (): unknown => {\n return startSpan({ name: methodName, attributes }, span => {\n // When linking to previous trace, fetch the stored context and add links asynchronously\n // This avoids blocking the response while fetching from storage\n if (startNewTrace && storage) {\n waitUntil?.(\n getStoredSpanContext(storage, methodName).then(storedContext => {\n if (storedContext) {\n span.addLinks(buildSpanLinks(storedContext));\n // TODO: Remove this once EAP can store span links. We currently only set this attribute so that we\n // can obtain the previous trace information from the EAP store. Long-term, EAP will handle\n // span links and then we should remove this again. Also throwing in a TODO(v11), to remind us\n // to check this at v11 time :)\n const sampledFlag = storedContext.sampled ? '1' : '0';\n span.setAttribute(\n 'sentry.previous_trace',\n `${storedContext.traceId}-${storedContext.spanId}-${sampledFlag}`,\n );\n }\n }),\n );\n }\n\n try {\n const result = Reflect.apply(target, thisArg, args);\n\n if (isThenable(result)) {\n return result.then(\n (res: unknown) => {\n waitUntil?.(teardown());\n return res;\n },\n (e: unknown) => {\n captureException(e, {\n mechanism: {\n type: 'auto.faas.cloudflare.durable_object',\n handled: false,\n },\n });\n waitUntil?.(teardown());\n throw e;\n },\n );\n } else {\n waitUntil?.(teardown());\n return result;\n }\n } catch (e) {\n captureException(e, {\n mechanism: {\n type: 'auto.faas.cloudflare.durable_object',\n handled: false,\n },\n });\n waitUntil?.(teardown());\n throw e;\n }\n });\n };\n\n if (rpcMeta) {\n return continueTrace(\n { sentryTrace: rpcMeta['sentry-trace'] || '', baggage: rpcMeta.baggage || '' },\n executeSpan,\n );\n }\n\n if (startNewTrace) {\n return startNewTraceCore(() => executeSpan());\n }\n\n return executeSpan();\n };\n\n return sentryWithScope(wrappedFunction);\n },\n }),\n noMark,\n );\n}\n"],"names":["ensureInstrumented","rpcMeta","extractRpcMeta","getClient","withIsolationScope","withScope","init","storeSpanContext","flushAndDispose","isThenable","captureException","SEMANTIC_ATTRIBUTE_SENTRY_OP","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","startSpan","getStoredSpanContext","buildSpanLinks","continueTrace","startNewTraceCore"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"wrapMethodWithSentry.js","sources":["../../src/wrapMethodWithSentry.ts"],"sourcesContent":["import type { DurableObjectStorage } from '@cloudflare/workers-types';\nimport type { SerializedTraceData } from '@sentry/core';\nimport {\n captureException,\n continueTrace,\n getClient,\n isThenable,\n type Scope,\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n startNewTrace as startNewTraceCore,\n startSpan,\n withIsolationScope,\n withScope,\n} from '@sentry/core';\nimport type { CloudflareOptions } from './client';\nimport { flushAndDispose } from './flush';\nimport { ensureInstrumented } from './instrument';\nimport { init } from './sdk';\nimport { extractRpcMeta } from './utils/rpcMeta';\nimport { buildSpanLinks, getStoredSpanContext, storeSpanContext } from './utils/traceLinks';\n\n/** Extended DurableObjectState with originalStorage exposed by instrumentContext */\ninterface InstrumentedDurableObjectState extends DurableObjectState {\n originalStorage?: DurableObjectStorage;\n}\n\n/**\n * Resolves uninstrumented DO storage for the current invocation.\n * Prefer `thisArg.ctx` (the live Durable Object instance) over the context captured at\n * construction time to avoid cross-DO I/O errors in the same isolate.\n */\nfunction resolveOriginalStorage(\n context: ExecutionContext | InstrumentedDurableObjectState | undefined,\n thisArg: unknown,\n): DurableObjectStorage | undefined {\n if (thisArg && typeof thisArg === 'object' && 'ctx' in thisArg) {\n const doCtx = (thisArg as { ctx: InstrumentedDurableObjectState }).ctx;\n if (doCtx?.originalStorage) {\n return doCtx.originalStorage;\n }\n }\n\n if (context && 'originalStorage' in context && context.originalStorage) {\n return context.originalStorage;\n }\n\n return undefined;\n}\n\ntype MethodWrapperOptions = {\n spanName?: string;\n spanOp?: string;\n options: CloudflareOptions;\n context: ExecutionContext | InstrumentedDurableObjectState;\n /**\n * If true, starts a fresh trace instead of inheriting from a parent trace.\n * Useful for scheduled/independent invocations like alarms.\n *\n * If true, it also stores the current span context and links to the previous invocation's span.\n * Uses Durable Object storage to persist the link. The link is set asynchronously via `span.addLinks()`\n * in a `waitUntil` to avoid blocking.\n *\n * @default false\n */\n startNewTrace?: boolean;\n};\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type UncheckedMethod = (...args: any[]) => any;\ntype OriginalMethod = UncheckedMethod;\n\n/**\n * Wraps a method with Sentry error tracking and optional tracing.\n * Supports starting new traces and linking to previous invocations via Durable Object storage.\n *\n * @param wrapperOptions - The options for the wrapper.\n * @param handler - The method to wrap.\n * @param callback - The callback to call.\n * @param noMark - Whether to mark the method as instrumented.\n * @returns The wrapped method.\n */\nexport function wrapMethodWithSentry<T extends OriginalMethod>(\n wrapperOptions: MethodWrapperOptions,\n handler: T,\n callback?: (...args: Parameters<T>) => void,\n noMark?: true,\n): T {\n return ensureInstrumented(\n handler,\n original =>\n new Proxy(original, {\n apply(target, thisArg, rawArgs: Parameters<T>) {\n const { startNewTrace } = wrapperOptions;\n\n // For RPC methods, extract Sentry trace context from the trailing argument.\n // The caller side (instrumentDurableObjectStub / JSRPC proxy) appends it;\n // we strip it here so the user's method never sees it.\n let args = rawArgs;\n let rpcMeta: SerializedTraceData | undefined;\n\n if (wrapperOptions.spanOp === 'rpc') {\n const extracted = extractRpcMeta(rawArgs);\n args = extracted.args;\n rpcMeta = extracted.rpcMeta;\n }\n\n // For startNewTrace, always use withIsolationScope to ensure a fresh scope\n // Otherwise, use existing client's scope or isolation scope\n const currentClient = getClient();\n const sentryWithScope = startNewTrace ? withIsolationScope : currentClient ? withScope : withIsolationScope;\n\n const wrappedFunction = (scope: Scope): unknown | Promise<unknown> => {\n // In certain situations, the passed context can become undefined.\n // For example, for Astro while prerendering pages at build time.\n // see: https://github.com/getsentry/sentry-javascript/issues/13217\n const context: typeof wrapperOptions.context | undefined = wrapperOptions.context;\n\n const waitUntil = context?.waitUntil?.bind?.(context);\n const storage = resolveOriginalStorage(context, thisArg);\n\n let scopeClient = scope.getClient();\n // Check if client exists AND is still usable (transport not disposed)\n // This handles the case where a previous handler disposed the client\n // but the scope still holds a reference to it (e.g., alarm handlers in Durable Objects)\n // For startNewTrace, always create a fresh client\n if (startNewTrace || !scopeClient?.getTransport()) {\n const client = init({\n ...wrapperOptions.options,\n ctx: context as unknown as ExecutionContext | undefined,\n });\n scope.setClient(client);\n scopeClient = client;\n }\n\n const clientToDispose = scopeClient;\n const methodName = wrapperOptions.spanName || 'unknown';\n\n const teardown = async (): Promise<void> => {\n if (startNewTrace && storage) {\n storeSpanContext(storage, methodName);\n }\n await flushAndDispose(clientToDispose);\n };\n\n const onFulfilled = (res: unknown) => {\n waitUntil?.(teardown());\n return res;\n };\n\n const onRejected = (e: unknown) => {\n captureException(e, {\n mechanism: {\n type: 'auto.faas.cloudflare.durable_object',\n handled: false,\n },\n });\n waitUntil?.(teardown());\n throw e;\n };\n\n if (!wrapperOptions.spanName) {\n try {\n if (callback) {\n callback(...args);\n }\n\n const result = Reflect.apply(target, thisArg, args);\n\n if (isThenable(result)) {\n return result.then(onFulfilled, onRejected);\n } else {\n return onFulfilled(result);\n }\n } catch (e) {\n return onRejected(e);\n }\n }\n\n const attributes = wrapperOptions.spanOp\n ? {\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: wrapperOptions.spanOp,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.faas.cloudflare.durable_object',\n }\n : {};\n\n const executeSpan = (): unknown => {\n return startSpan({ name: methodName, attributes }, span => {\n if (startNewTrace && storage) {\n const storedContext = getStoredSpanContext(storage, methodName);\n\n if (storedContext) {\n span.addLinks(buildSpanLinks(storedContext));\n // TODO: Remove this once EAP can store span links. We currently only set this attribute so that we\n // can obtain the previous trace information from the EAP store. Long-term, EAP will handle\n // span links and then we should remove this again. Also throwing in a TODO(v11), to remind us\n // to check this at v11 time :)\n const sampledFlag = storedContext.sampled ? '1' : '0';\n span.setAttribute(\n 'sentry.previous_trace',\n `${storedContext.traceId}-${storedContext.spanId}-${sampledFlag}`,\n );\n }\n }\n\n try {\n const result = Reflect.apply(target, thisArg, args);\n\n if (isThenable(result)) {\n return result.then(onFulfilled, onRejected);\n } else {\n return onFulfilled(result);\n }\n } catch (e) {\n return onRejected(e);\n }\n });\n };\n\n if (rpcMeta) {\n return continueTrace(\n { sentryTrace: rpcMeta['sentry-trace'] || '', baggage: rpcMeta.baggage || '' },\n executeSpan,\n );\n }\n\n if (startNewTrace) {\n return startNewTraceCore(() => executeSpan());\n }\n\n return executeSpan();\n };\n\n return sentryWithScope(wrappedFunction);\n },\n }),\n noMark,\n );\n}\n"],"names":["ensureInstrumented","rpcMeta","extractRpcMeta","getClient","withIsolationScope","withScope","init","storeSpanContext","flushAndDispose","captureException","isThenable","SEMANTIC_ATTRIBUTE_SENTRY_OP","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","startSpan","getStoredSpanContext","buildSpanLinks","continueTrace","startNewTraceCore"],"mappings":"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"}
|
package/build/esm/client.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ServerRuntimeClient, applySdkMetadata, debug } from '@sentry/core';
|
|
1
|
+
import { ServerRuntimeClient, applySdkMetadata, debug, spanIsSampled } from '@sentry/core';
|
|
2
2
|
import { DEBUG_BUILD } from './debug-build.js';
|
|
3
3
|
|
|
4
4
|
class CloudflareClient extends ServerRuntimeClient {
|
|
@@ -28,6 +28,9 @@ class CloudflareClient extends ServerRuntimeClient {
|
|
|
28
28
|
this._unsubscribeSpanStart = this.on("spanStart", (span) => {
|
|
29
29
|
const spanId = span.spanContext().spanId;
|
|
30
30
|
DEBUG_BUILD && debug.log("[CloudflareClient] Span started:", spanId);
|
|
31
|
+
if (!spanIsSampled(span)) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
31
34
|
this._pendingSpans.add(spanId);
|
|
32
35
|
if (!this._spanCompletionPromise) {
|
|
33
36
|
this._spanCompletionPromise = new Promise((resolve) => {
|
package/build/esm/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sources":["../../src/client.ts"],"sourcesContent":["import type { ClientOptions, Options, ServerRuntimeClientOptions } from '@sentry/core';\nimport { applySdkMetadata, debug, ServerRuntimeClient } from '@sentry/core';\nimport { DEBUG_BUILD } from './debug-build';\nimport type { makeFlushLock } from './flush';\nimport type { CloudflareTransportOptions } from './transport';\n\n/**\n * The Sentry Cloudflare SDK Client.\n *\n * @see CloudflareClientOptions for documentation on configuration options.\n * @see ServerRuntimeClient for usage documentation.\n */\nexport class CloudflareClient extends ServerRuntimeClient {\n private readonly _flushLock: ReturnType<typeof makeFlushLock> | void;\n private _pendingSpans: Set<string> = new Set();\n private _spanCompletionPromise: Promise<void> | null = null;\n private _resolveSpanCompletion: (() => void) | null = null;\n\n private _unsubscribeSpanStart: (() => void) | null = null;\n private _unsubscribeSpanEnd: (() => void) | null = null;\n\n /**\n * Creates a new Cloudflare SDK instance.\n * @param options Configuration options for this SDK.\n */\n public constructor(options: CloudflareClientOptions) {\n applySdkMetadata(options, 'cloudflare');\n options._metadata = options._metadata || {};\n const { flushLock, ...serverOptions } = options;\n\n const clientOptions: ServerRuntimeClientOptions = {\n ...serverOptions,\n platform: 'javascript',\n // TODO: Grab version information\n runtime: { name: 'cloudflare' },\n // TODO: Add server name\n _flushInterval: 0,\n };\n\n super(clientOptions);\n this._flushLock = flushLock;\n\n // Track span lifecycle to know when to flush\n this._unsubscribeSpanStart = this.on('spanStart', span => {\n const spanId = span.spanContext().spanId;\n DEBUG_BUILD && debug.log('[CloudflareClient] Span started:', spanId);\n this._pendingSpans.add(spanId);\n\n if (!this._spanCompletionPromise) {\n this._spanCompletionPromise = new Promise(resolve => {\n this._resolveSpanCompletion = resolve;\n });\n }\n });\n\n this._unsubscribeSpanEnd = this.on('spanEnd', span => {\n const spanId = span.spanContext().spanId;\n DEBUG_BUILD && debug.log('[CloudflareClient] Span ended:', spanId);\n this._pendingSpans.delete(spanId);\n\n // If no more pending spans, resolve the completion promise\n if (this._pendingSpans.size === 0 && this._resolveSpanCompletion) {\n DEBUG_BUILD && debug.log('[CloudflareClient] All spans completed, resolving promise');\n this._resolveSpanCompletion();\n this._resetSpanCompletionPromise();\n }\n });\n }\n\n /**\n * Flushes pending operations and ensures all data is processed.\n * If a timeout is provided, the operation will be completed within the specified time limit.\n *\n * It will wait for all pending spans to complete before flushing.\n *\n * @param {number} [timeout] - Optional timeout in milliseconds to force the completion of the flush operation.\n * @return {Promise<boolean>} A promise that resolves to a boolean indicating whether the flush operation was successful.\n */\n public async flush(timeout?: number): Promise<boolean> {\n if (this._flushLock) {\n await this._flushLock.finalize();\n }\n\n if (this._pendingSpans.size > 0 && this._spanCompletionPromise) {\n DEBUG_BUILD &&\n debug.log('[CloudflareClient] Waiting for', this._pendingSpans.size, 'pending spans to complete...');\n\n const timeoutMs = timeout ?? 5000;\n const spanCompletionRace = Promise.race([\n this._spanCompletionPromise,\n new Promise(resolve =>\n setTimeout(() => {\n DEBUG_BUILD &&\n debug.log('[CloudflareClient] Span completion timeout after', timeoutMs, 'ms, flushing anyway');\n resolve(undefined);\n }, timeoutMs),\n ),\n ]);\n\n await spanCompletionRace;\n }\n\n return super.flush(timeout);\n }\n\n /**\n * Disposes of the client and releases all resources.\n *\n * This method clears all Cloudflare-specific state in addition to the base client cleanup.\n * It unsubscribes from span lifecycle events and clears pending span tracking.\n *\n * Call this method after flushing to allow the client to be garbage collected.\n * After calling dispose(), the client should not be used anymore.\n */\n public override dispose(): void {\n DEBUG_BUILD && debug.log('[CloudflareClient] Disposing client...');\n\n super.dispose();\n\n if (this._unsubscribeSpanStart) {\n this._unsubscribeSpanStart();\n this._unsubscribeSpanStart = null;\n }\n if (this._unsubscribeSpanEnd) {\n this._unsubscribeSpanEnd();\n this._unsubscribeSpanEnd = null;\n }\n\n this._resetSpanCompletionPromise();\n (this as unknown as { _flushLock: ReturnType<typeof makeFlushLock> | void })._flushLock = undefined;\n }\n\n /**\n * Resets the span completion promise and resolve function.\n */\n private _resetSpanCompletionPromise(): void {\n this._pendingSpans.clear();\n this._spanCompletionPromise = null;\n this._resolveSpanCompletion = null;\n }\n}\n\ninterface BaseCloudflareOptions {\n /**\n * @ignore Used internally to disable the deDupeIntegration for workflows.\n * @hidden Used internally to disable the deDupeIntegration for workflows.\n * @default true\n */\n enableDedupe?: boolean;\n\n /**\n * The Cloudflare SDK is not OpenTelemetry native, however, we set up some OpenTelemetry compatibility\n * via a custom trace provider.\n * This ensures that any spans emitted via `@opentelemetry/api` will be captured by Sentry.\n * HOWEVER, big caveat: This does not handle custom context handling, it will always work off the current scope.\n * This should be good enough for many, but not all integrations.\n *\n * If you want to opt-out of setting up the OpenTelemetry compatibility tracer, set this to `true`.\n *\n * @default false\n */\n skipOpenTelemetrySetup?: boolean;\n\n /**\n * Enable trace propagation for RPC calls between Workers, Durable Objects, and Service Bindings.\n *\n * When enabled, trace context (sentry-trace + baggage) is propagated across:\n * - `stub.fetch()` calls to Durable Objects (via HTTP headers)\n * - Service binding `fetch()` calls (via HTTP headers)\n * - RPC method calls to Durable Objects (via trailing argument)\n *\n * When enabled on the **receiver side** (DurableObject), the SDK will also:\n * - Extract and continue traces from incoming RPC calls\n * - Create spans for each RPC method invocation\n * - Capture errors thrown by RPC methods\n *\n * **Important:** This option should be enabled on **both sides** for full trace propagation.\n *\n * @default false\n * @example\n * ```ts\n * // Worker side (caller)\n * export default Sentry.withSentry(\n * (env) => ({\n * dsn: env.SENTRY_DSN,\n * enableRpcTracePropagation: true,\n * }),\n * handler,\n * );\n *\n * // Durable Object side (receiver)\n * export const MyDO = Sentry.instrumentDurableObjectWithSentry(\n * (env) => ({\n * dsn: env.SENTRY_DSN,\n * enableRpcTracePropagation: true,\n * }),\n * MyDOBase,\n * );\n * ```\n */\n enableRpcTracePropagation?: boolean;\n\n /**\n * @deprecated Use `enableRpcTracePropagation` instead. This option will be removed in a future major version.\n *\n * Enable instrumentation of prototype methods for DurableObjects.\n *\n * When `true`, the SDK will wrap all methods on the DurableObject prototype chain\n * to automatically create spans and capture errors for RPC method calls.\n *\n * When an array of strings is provided, only the specified method names will be instrumented.\n *\n * @default false\n */\n instrumentPrototypeMethods?: boolean | string[];\n}\n\n/**\n * Configuration options for the Sentry Cloudflare SDK\n *\n * @see @sentry/core Options for more information.\n */\nexport interface CloudflareOptions extends Options<CloudflareTransportOptions>, BaseCloudflareOptions {\n ctx?: ExecutionContext;\n}\n\n/**\n * Configuration options for the Sentry Cloudflare SDK Client class\n *\n * @see CloudflareClient for more information.\n */\nexport interface CloudflareClientOptions extends ClientOptions<CloudflareTransportOptions>, BaseCloudflareOptions {\n flushLock?: ReturnType<typeof makeFlushLock>;\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"client.js","sources":["../../src/client.ts"],"sourcesContent":["import type { ClientOptions, Options, ServerRuntimeClientOptions } from '@sentry/core';\nimport { applySdkMetadata, debug, ServerRuntimeClient, spanIsSampled } from '@sentry/core';\nimport { DEBUG_BUILD } from './debug-build';\nimport type { makeFlushLock } from './flush';\nimport type { CloudflareTransportOptions } from './transport';\n\n/**\n * The Sentry Cloudflare SDK Client.\n *\n * @see CloudflareClientOptions for documentation on configuration options.\n * @see ServerRuntimeClient for usage documentation.\n */\nexport class CloudflareClient extends ServerRuntimeClient {\n private readonly _flushLock: ReturnType<typeof makeFlushLock> | void;\n private _pendingSpans: Set<string> = new Set();\n private _spanCompletionPromise: Promise<void> | null = null;\n private _resolveSpanCompletion: (() => void) | null = null;\n\n private _unsubscribeSpanStart: (() => void) | null = null;\n private _unsubscribeSpanEnd: (() => void) | null = null;\n\n /**\n * Creates a new Cloudflare SDK instance.\n * @param options Configuration options for this SDK.\n */\n public constructor(options: CloudflareClientOptions) {\n applySdkMetadata(options, 'cloudflare');\n options._metadata = options._metadata || {};\n const { flushLock, ...serverOptions } = options;\n\n const clientOptions: ServerRuntimeClientOptions = {\n ...serverOptions,\n platform: 'javascript',\n // TODO: Grab version information\n runtime: { name: 'cloudflare' },\n // TODO: Add server name\n _flushInterval: 0,\n };\n\n super(clientOptions);\n this._flushLock = flushLock;\n\n // Track span lifecycle to know when to flush\n this._unsubscribeSpanStart = this.on('spanStart', span => {\n const spanId = span.spanContext().spanId;\n DEBUG_BUILD && debug.log('[CloudflareClient] Span started:', spanId);\n\n // Negatively sampled spans never emit spanEnd,\n // so tracking them would cause _pendingSpans to grow unboundedly.\n // We should fix the inconsistent behavior for NonRecordingSpans in the future but\n // for now, we just ignore them.\n if (!spanIsSampled(span)) {\n return;\n }\n\n this._pendingSpans.add(spanId);\n\n if (!this._spanCompletionPromise) {\n this._spanCompletionPromise = new Promise(resolve => {\n this._resolveSpanCompletion = resolve;\n });\n }\n });\n\n this._unsubscribeSpanEnd = this.on('spanEnd', span => {\n const spanId = span.spanContext().spanId;\n DEBUG_BUILD && debug.log('[CloudflareClient] Span ended:', spanId);\n this._pendingSpans.delete(spanId);\n\n // If no more pending spans, resolve the completion promise\n if (this._pendingSpans.size === 0 && this._resolveSpanCompletion) {\n DEBUG_BUILD && debug.log('[CloudflareClient] All spans completed, resolving promise');\n this._resolveSpanCompletion();\n this._resetSpanCompletionPromise();\n }\n });\n }\n\n /**\n * Flushes pending operations and ensures all data is processed.\n * If a timeout is provided, the operation will be completed within the specified time limit.\n *\n * It will wait for all pending spans to complete before flushing.\n *\n * @param {number} [timeout] - Optional timeout in milliseconds to force the completion of the flush operation.\n * @return {Promise<boolean>} A promise that resolves to a boolean indicating whether the flush operation was successful.\n */\n public async flush(timeout?: number): Promise<boolean> {\n if (this._flushLock) {\n await this._flushLock.finalize();\n }\n\n if (this._pendingSpans.size > 0 && this._spanCompletionPromise) {\n DEBUG_BUILD &&\n debug.log('[CloudflareClient] Waiting for', this._pendingSpans.size, 'pending spans to complete...');\n\n const timeoutMs = timeout ?? 5000;\n const spanCompletionRace = Promise.race([\n this._spanCompletionPromise,\n new Promise(resolve =>\n setTimeout(() => {\n DEBUG_BUILD &&\n debug.log('[CloudflareClient] Span completion timeout after', timeoutMs, 'ms, flushing anyway');\n resolve(undefined);\n }, timeoutMs),\n ),\n ]);\n\n await spanCompletionRace;\n }\n\n return super.flush(timeout);\n }\n\n /**\n * Disposes of the client and releases all resources.\n *\n * This method clears all Cloudflare-specific state in addition to the base client cleanup.\n * It unsubscribes from span lifecycle events and clears pending span tracking.\n *\n * Call this method after flushing to allow the client to be garbage collected.\n * After calling dispose(), the client should not be used anymore.\n */\n public override dispose(): void {\n DEBUG_BUILD && debug.log('[CloudflareClient] Disposing client...');\n\n super.dispose();\n\n if (this._unsubscribeSpanStart) {\n this._unsubscribeSpanStart();\n this._unsubscribeSpanStart = null;\n }\n if (this._unsubscribeSpanEnd) {\n this._unsubscribeSpanEnd();\n this._unsubscribeSpanEnd = null;\n }\n\n this._resetSpanCompletionPromise();\n (this as unknown as { _flushLock: ReturnType<typeof makeFlushLock> | void })._flushLock = undefined;\n }\n\n /**\n * Resets the span completion promise and resolve function.\n */\n private _resetSpanCompletionPromise(): void {\n this._pendingSpans.clear();\n this._spanCompletionPromise = null;\n this._resolveSpanCompletion = null;\n }\n}\n\ninterface BaseCloudflareOptions {\n /**\n * @ignore Used internally to disable the deDupeIntegration for workflows.\n * @hidden Used internally to disable the deDupeIntegration for workflows.\n * @default true\n */\n enableDedupe?: boolean;\n\n /**\n * The Cloudflare SDK is not OpenTelemetry native, however, we set up some OpenTelemetry compatibility\n * via a custom trace provider.\n * This ensures that any spans emitted via `@opentelemetry/api` will be captured by Sentry.\n * HOWEVER, big caveat: This does not handle custom context handling, it will always work off the current scope.\n * This should be good enough for many, but not all integrations.\n *\n * If you want to opt-out of setting up the OpenTelemetry compatibility tracer, set this to `true`.\n *\n * @default false\n */\n skipOpenTelemetrySetup?: boolean;\n\n /**\n * Enable trace propagation for RPC calls between Workers, Durable Objects, and Service Bindings.\n *\n * When enabled, trace context (sentry-trace + baggage) is propagated across:\n * - `stub.fetch()` calls to Durable Objects (via HTTP headers)\n * - Service binding `fetch()` calls (via HTTP headers)\n * - RPC method calls to Durable Objects (via trailing argument)\n *\n * When enabled on the **receiver side** (DurableObject), the SDK will also:\n * - Extract and continue traces from incoming RPC calls\n * - Create spans for each RPC method invocation\n * - Capture errors thrown by RPC methods\n *\n * **Important:** This option should be enabled on **both sides** for full trace propagation.\n *\n * @default false\n * @example\n * ```ts\n * // Worker side (caller)\n * export default Sentry.withSentry(\n * (env) => ({\n * dsn: env.SENTRY_DSN,\n * enableRpcTracePropagation: true,\n * }),\n * handler,\n * );\n *\n * // Durable Object side (receiver)\n * export const MyDO = Sentry.instrumentDurableObjectWithSentry(\n * (env) => ({\n * dsn: env.SENTRY_DSN,\n * enableRpcTracePropagation: true,\n * }),\n * MyDOBase,\n * );\n * ```\n */\n enableRpcTracePropagation?: boolean;\n\n /**\n * @deprecated Use `enableRpcTracePropagation` instead. This option will be removed in a future major version.\n *\n * Enable instrumentation of prototype methods for DurableObjects.\n *\n * When `true`, the SDK will wrap all methods on the DurableObject prototype chain\n * to automatically create spans and capture errors for RPC method calls.\n *\n * When an array of strings is provided, only the specified method names will be instrumented.\n *\n * @default false\n */\n instrumentPrototypeMethods?: boolean | string[];\n}\n\n/**\n * Configuration options for the Sentry Cloudflare SDK\n *\n * @see @sentry/core Options for more information.\n */\nexport interface CloudflareOptions extends Options<CloudflareTransportOptions>, BaseCloudflareOptions {\n ctx?: ExecutionContext;\n}\n\n/**\n * Configuration options for the Sentry Cloudflare SDK Client class\n *\n * @see CloudflareClient for more information.\n */\nexport interface CloudflareClientOptions extends ClientOptions<CloudflareTransportOptions>, BaseCloudflareOptions {\n flushLock?: ReturnType<typeof makeFlushLock>;\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -5,17 +5,10 @@ import { instrumentEnv } from './instrumentations/worker/instrumentEnv.js';
|
|
|
5
5
|
import { getFinalOptions } from './options.js';
|
|
6
6
|
import { wrapRequestHandler } from './request.js';
|
|
7
7
|
import { instrumentContext } from './utils/instrumentContext.js';
|
|
8
|
+
import { extractRpcMeta } from './utils/rpcMeta.js';
|
|
8
9
|
import { getEffectiveRpcPropagation } from './utils/rpcOptions.js';
|
|
9
10
|
import { wrapMethodWithSentry } from './wrapMethodWithSentry.js';
|
|
10
11
|
|
|
11
|
-
const BUILT_IN_DO_METHODS = /* @__PURE__ */ new Set([
|
|
12
|
-
"constructor",
|
|
13
|
-
"fetch",
|
|
14
|
-
"alarm",
|
|
15
|
-
"webSocketError",
|
|
16
|
-
"webSocketClose",
|
|
17
|
-
"webSocketMessage"
|
|
18
|
-
]);
|
|
19
12
|
function instrumentDurableObjectWithSentry(optionsCallback, DurableObjectClass) {
|
|
20
13
|
return new Proxy(DurableObjectClass, {
|
|
21
14
|
construct(target, [ctx, env]) {
|
|
@@ -69,31 +62,39 @@ function instrumentDurableObjectWithSentry(optionsCallback, DurableObjectClass)
|
|
|
69
62
|
}
|
|
70
63
|
const instrumentPrototypeMethods = Array.isArray(options.instrumentPrototypeMethods) ? options.instrumentPrototypeMethods : void 0;
|
|
71
64
|
const allowSet = instrumentPrototypeMethods ? new Set(instrumentPrototypeMethods) : null;
|
|
72
|
-
const
|
|
65
|
+
const alwaysTrace = options.enableRpcTracePropagation === void 0;
|
|
66
|
+
const methodCache = /* @__PURE__ */ new Map();
|
|
73
67
|
return new Proxy(obj, {
|
|
74
68
|
get(proxyTarget, prop, receiver) {
|
|
75
69
|
const value = Reflect.get(proxyTarget, prop, receiver);
|
|
76
|
-
if (typeof prop !== "string" ||
|
|
70
|
+
if (typeof prop !== "string" || typeof value !== "function" || prop === "constructor") {
|
|
77
71
|
return value;
|
|
78
72
|
}
|
|
79
|
-
const cached =
|
|
73
|
+
const cached = methodCache.get(prop);
|
|
80
74
|
if (cached) {
|
|
81
75
|
return cached;
|
|
82
76
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
return
|
|
77
|
+
const boundMethod = value.bind(proxyTarget);
|
|
78
|
+
if (prop in Object.prototype || Object.prototype.hasOwnProperty.call(proxyTarget, prop) || allowSet && !allowSet.has(prop)) {
|
|
79
|
+
methodCache.set(prop, boundMethod);
|
|
80
|
+
return boundMethod;
|
|
87
81
|
}
|
|
88
|
-
const
|
|
89
|
-
const wrapped = wrapMethodWithSentry(
|
|
82
|
+
const tracedMethod = wrapMethodWithSentry(
|
|
90
83
|
{ options, context, spanName: prop, spanOp: "rpc" },
|
|
91
|
-
|
|
84
|
+
boundMethod,
|
|
92
85
|
void 0,
|
|
93
86
|
true
|
|
94
87
|
);
|
|
95
|
-
|
|
96
|
-
|
|
88
|
+
if (alwaysTrace) {
|
|
89
|
+
methodCache.set(prop, tracedMethod);
|
|
90
|
+
return tracedMethod;
|
|
91
|
+
}
|
|
92
|
+
const wrappedMethod = ((...args) => {
|
|
93
|
+
const { rpcMeta } = extractRpcMeta(args);
|
|
94
|
+
return rpcMeta ? tracedMethod(...args) : boundMethod(...args);
|
|
95
|
+
});
|
|
96
|
+
methodCache.set(prop, wrappedMethod);
|
|
97
|
+
return wrappedMethod;
|
|
97
98
|
}
|
|
98
99
|
});
|
|
99
100
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"durableobject.js","sources":["../../src/durableobject.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/unbound-method */\nimport { captureException } from '@sentry/core';\nimport type { DurableObject } from 'cloudflare:workers';\nimport { setAsyncLocalStorageAsyncContextStrategy } from './async';\nimport type { CloudflareOptions } from './client';\nimport { ensureInstrumented } from './instrument';\nimport { instrumentEnv } from './instrumentations/worker/instrumentEnv';\nimport { getFinalOptions } from './options';\nimport { wrapRequestHandler } from './request';\nimport { instrumentContext } from './utils/instrumentContext';\nimport { getEffectiveRpcPropagation } from './utils/rpcOptions';\nimport { type UncheckedMethod, wrapMethodWithSentry } from './wrapMethodWithSentry';\n\nconst BUILT_IN_DO_METHODS = new Set([\n 'constructor',\n 'fetch',\n 'alarm',\n 'webSocketError',\n 'webSocketClose',\n 'webSocketMessage',\n]);\n\n/**\n * Instruments a Durable Object class to capture errors and performance data.\n *\n * Instruments the following methods by default:\n * - fetch\n * - alarm\n * - webSocketMessage\n * - webSocketClose\n * - webSocketError\n *\n * To instrument RPC methods (prototype methods), enable the `enableRpcTracePropagation` option.\n *\n * @param optionsCallback Function that returns the options for the SDK initialization.\n * @param DurableObjectClass The Durable Object class to instrument.\n * @returns The instrumented Durable Object class.\n *\n * @example\n * ```ts\n * class MyDurableObjectBase extends DurableObject {\n * constructor(ctx: DurableObjectState, env: Env) {\n * super(ctx, env);\n * }\n * }\n *\n * export const MyDurableObject = instrumentDurableObjectWithSentry(\n * env => ({\n * dsn: env.SENTRY_DSN,\n * tracesSampleRate: 1.0,\n * }),\n * MyDurableObjectBase,\n * );\n * ```\n */\nexport function instrumentDurableObjectWithSentry<\n E,\n T extends DurableObject<E>,\n C extends new (state: DurableObjectState, env: E) => T,\n>(optionsCallback: (env: E) => CloudflareOptions, DurableObjectClass: C): C {\n return new Proxy(DurableObjectClass, {\n construct(target, [ctx, env]) {\n setAsyncLocalStorageAsyncContextStrategy();\n const context = instrumentContext(ctx);\n const options = getFinalOptions(optionsCallback(env), env);\n const instrumentedEnv = instrumentEnv(env, options);\n\n const obj = new target(context, instrumentedEnv);\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 if (obj.fetch && typeof obj.fetch === 'function') {\n obj.fetch = ensureInstrumented(\n obj.fetch,\n original =>\n new Proxy(original, {\n apply(target, thisArg, args) {\n return wrapRequestHandler({ options, request: args[0], context }, () => {\n return Reflect.apply(target, thisArg, args);\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 obj.alarm = wrapMethodWithSentry(\n { options, context, spanName: 'alarm', spanOp: 'function', startNewTrace: true },\n obj.alarm,\n );\n }\n\n if (obj.webSocketMessage && typeof obj.webSocketMessage === 'function') {\n obj.webSocketMessage = wrapMethodWithSentry(\n { options, context, spanName: 'webSocketMessage' },\n obj.webSocketMessage,\n );\n }\n\n if (obj.webSocketClose && typeof obj.webSocketClose === 'function') {\n obj.webSocketClose = wrapMethodWithSentry({ options, context, spanName: 'webSocketClose' }, obj.webSocketClose);\n }\n\n if (obj.webSocketError && typeof obj.webSocketError === 'function') {\n obj.webSocketError = wrapMethodWithSentry(\n { options, context, spanName: 'webSocketError' },\n obj.webSocketError,\n (_, error) =>\n captureException(error, {\n mechanism: {\n type: 'auto.faas.cloudflare.durable_object_websocket',\n handled: false,\n },\n }),\n );\n }\n\n // Get effective RPC propagation setting (handles deprecation of instrumentPrototypeMethods)\n const rpcPropagation = getEffectiveRpcPropagation(options);\n\n // Skip RPC instrumentation if not enabled\n if (!rpcPropagation) {\n return obj;\n }\n\n // If `instrumentPrototypeMethods` was passed as an array (deprecated),\n // only the listed method names should be instrumented.\n const instrumentPrototypeMethods = Array.isArray(options.instrumentPrototypeMethods)\n ? options.instrumentPrototypeMethods\n : undefined;\n const allowSet = instrumentPrototypeMethods ? new Set(instrumentPrototypeMethods) : null;\n\n // Return a Proxy that lazily wraps prototype methods on access.\n // This avoids iterating the prototype chain at construction time —\n // we only check if a property is an RPC method when it's accessed.\n const rpcMethodCache = new Map<string, UncheckedMethod>();\n\n return new Proxy(obj, {\n get(proxyTarget, prop, receiver) {\n const value = Reflect.get(proxyTarget, prop, receiver);\n\n if (typeof prop !== 'string' || BUILT_IN_DO_METHODS.has(prop)) {\n return value;\n }\n\n const cached = rpcMethodCache.get(prop);\n\n if (cached) {\n return cached;\n }\n\n if (\n typeof value !== 'function' ||\n Object.prototype.hasOwnProperty.call(proxyTarget, prop) ||\n (allowSet && !allowSet.has(prop)) ||\n // Exclude inherited Object.prototype methods (toString, valueOf, etc.)\n // These are not RPC methods and should not create spans\n prop in Object.prototype\n ) {\n return value;\n }\n\n // Bind the method to the original object to ensure private fields work correctly.\n // When called via the Proxy, `this` would be the Proxy, but private fields require\n // the original object. Bound functions ignore the thisArg passed via Reflect.apply.\n const boundValue = (value as UncheckedMethod).bind(proxyTarget);\n\n const wrapped = wrapMethodWithSentry(\n { options, context, spanName: prop, spanOp: 'rpc' },\n boundValue,\n undefined,\n true,\n );\n\n rpcMethodCache.set(prop, wrapped);\n\n return wrapped;\n },\n });\n },\n });\n}\n"],"names":["target"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"durableobject.js","sources":["../../src/durableobject.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/unbound-method */\nimport { captureException } from '@sentry/core';\nimport type { DurableObject } from 'cloudflare:workers';\nimport { setAsyncLocalStorageAsyncContextStrategy } from './async';\nimport type { CloudflareOptions } from './client';\nimport { ensureInstrumented } from './instrument';\nimport { instrumentEnv } from './instrumentations/worker/instrumentEnv';\nimport { getFinalOptions } from './options';\nimport { wrapRequestHandler } from './request';\nimport { instrumentContext } from './utils/instrumentContext';\nimport { extractRpcMeta } from './utils/rpcMeta';\nimport { getEffectiveRpcPropagation } from './utils/rpcOptions';\nimport { type UncheckedMethod, wrapMethodWithSentry } from './wrapMethodWithSentry';\n\n/**\n * Instruments a Durable Object class to capture errors and performance data.\n *\n * Instruments the following methods by default:\n * - fetch\n * - alarm\n * - webSocketMessage\n * - webSocketClose\n * - webSocketError\n *\n * To instrument RPC methods (prototype methods), enable the `enableRpcTracePropagation` option.\n *\n * @param optionsCallback Function that returns the options for the SDK initialization.\n * @param DurableObjectClass The Durable Object class to instrument.\n * @returns The instrumented Durable Object class.\n *\n * @example\n * ```ts\n * class MyDurableObjectBase extends DurableObject {\n * constructor(ctx: DurableObjectState, env: Env) {\n * super(ctx, env);\n * }\n * }\n *\n * export const MyDurableObject = instrumentDurableObjectWithSentry(\n * env => ({\n * dsn: env.SENTRY_DSN,\n * tracesSampleRate: 1.0,\n * }),\n * MyDurableObjectBase,\n * );\n * ```\n */\nexport function instrumentDurableObjectWithSentry<\n E,\n T extends DurableObject<E>,\n C extends new (state: DurableObjectState, env: E) => T,\n>(optionsCallback: (env: E) => CloudflareOptions, DurableObjectClass: C): C {\n return new Proxy(DurableObjectClass, {\n construct(target, [ctx, env]) {\n setAsyncLocalStorageAsyncContextStrategy();\n const context = instrumentContext(ctx);\n const options = getFinalOptions(optionsCallback(env), env);\n const instrumentedEnv = instrumentEnv(env, options);\n\n const obj = new target(context, instrumentedEnv);\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 if (obj.fetch && typeof obj.fetch === 'function') {\n obj.fetch = ensureInstrumented(\n obj.fetch,\n original =>\n new Proxy(original, {\n apply(target, thisArg, args) {\n return wrapRequestHandler({ options, request: args[0], context }, () => {\n return Reflect.apply(target, thisArg, args);\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 obj.alarm = wrapMethodWithSentry(\n { options, context, spanName: 'alarm', spanOp: 'function', startNewTrace: true },\n obj.alarm,\n );\n }\n\n if (obj.webSocketMessage && typeof obj.webSocketMessage === 'function') {\n obj.webSocketMessage = wrapMethodWithSentry(\n { options, context, spanName: 'webSocketMessage' },\n obj.webSocketMessage,\n );\n }\n\n if (obj.webSocketClose && typeof obj.webSocketClose === 'function') {\n obj.webSocketClose = wrapMethodWithSentry({ options, context, spanName: 'webSocketClose' }, obj.webSocketClose);\n }\n\n if (obj.webSocketError && typeof obj.webSocketError === 'function') {\n obj.webSocketError = wrapMethodWithSentry(\n { options, context, spanName: 'webSocketError' },\n obj.webSocketError,\n (_, error) =>\n captureException(error, {\n mechanism: {\n type: 'auto.faas.cloudflare.durable_object_websocket',\n handled: false,\n },\n }),\n );\n }\n\n // Get effective RPC propagation setting (handles deprecation of instrumentPrototypeMethods)\n const rpcPropagation = getEffectiveRpcPropagation(options);\n\n // Skip RPC instrumentation if not enabled\n if (!rpcPropagation) {\n return obj;\n }\n\n // If `instrumentPrototypeMethods` was passed as an array (deprecated),\n // only the listed method names should be instrumented.\n const instrumentPrototypeMethods = Array.isArray(options.instrumentPrototypeMethods)\n ? options.instrumentPrototypeMethods\n : undefined;\n const allowSet = instrumentPrototypeMethods ? new Set(instrumentPrototypeMethods) : null;\n\n // When using the deprecated `instrumentPrototypeMethods` option, always create spans.\n // When using the new `enableRpcTracePropagation`, only create spans when RPC metadata is present.\n const alwaysTrace = options.enableRpcTracePropagation === undefined;\n\n // Return a Proxy that binds all methods to the original object and creates spans\n // for RPC calls that have Sentry trace context propagated.\n // Binding is required because frameworks may use private fields (babel WeakMap pattern),\n // which fail if `this` is the Proxy instead of the original object.\n const methodCache = new Map<string, UncheckedMethod>();\n\n return new Proxy(obj, {\n get(proxyTarget, prop, receiver) {\n const value = Reflect.get(proxyTarget, prop, receiver);\n\n if (typeof prop !== 'string' || typeof value !== 'function' || prop === 'constructor') {\n return value;\n }\n\n const cached = methodCache.get(prop);\n\n if (cached) {\n return cached;\n }\n\n const boundMethod = (value as UncheckedMethod).bind(proxyTarget);\n\n if (\n prop in Object.prototype ||\n Object.prototype.hasOwnProperty.call(proxyTarget, prop) ||\n (allowSet && !allowSet.has(prop))\n ) {\n methodCache.set(prop, boundMethod);\n\n return boundMethod;\n }\n\n // Pre-create the traced version\n const tracedMethod = wrapMethodWithSentry(\n { options, context, spanName: prop, spanOp: 'rpc' },\n boundMethod,\n undefined,\n true,\n );\n\n // For deprecated `instrumentPrototypeMethods`, always trace.\n // For new `enableRpcTracePropagation`, only trace when RPC metadata is present.\n if (alwaysTrace) {\n methodCache.set(prop, tracedMethod);\n\n return tracedMethod;\n }\n\n // Wrapper that checks for Sentry RPC metadata at call time\n const wrappedMethod = ((...args: unknown[]) => {\n const { rpcMeta } = extractRpcMeta(args);\n\n // If Sentry RPC metadata is present, use the traced version (creates span)\n // Otherwise, call the bound method directly (no span)\n return rpcMeta ? tracedMethod(...args) : boundMethod(...args);\n }) as UncheckedMethod;\n\n methodCache.set(prop, wrappedMethod);\n\n return wrappedMethod;\n },\n });\n },\n });\n}\n"],"names":["target"],"mappings":"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"}
|
package/build/esm/index.js
CHANGED
|
@@ -9,7 +9,7 @@ export { httpServerIntegration } from './integrations/httpServer.js';
|
|
|
9
9
|
export { fetchIntegration } from './integrations/fetch.js';
|
|
10
10
|
export { vercelAIIntegration } from './integrations/tracing/vercelai.js';
|
|
11
11
|
export { honoIntegration } from './integrations/hono.js';
|
|
12
|
-
export { instrumentD1WithSentry } from './
|
|
12
|
+
export { instrumentD1WithSentry } from './instrumentations/worker/instrumentD1.js';
|
|
13
13
|
export { instrumentWorkflowWithSentry } from './workflows.js';
|
|
14
14
|
export { setAsyncLocalStorageAsyncContextStrategy } from './async.js';
|
|
15
15
|
//# sourceMappingURL=index.js.map
|
|
@@ -28,7 +28,7 @@ function instrumentDurableObjectStorage(storage, waitUntil) {
|
|
|
28
28
|
() => {
|
|
29
29
|
const teardown = async () => {
|
|
30
30
|
if (methodName === "setAlarm") {
|
|
31
|
-
|
|
31
|
+
storeSpanContext(target, "alarm");
|
|
32
32
|
}
|
|
33
33
|
};
|
|
34
34
|
const result = original.apply(target, args);
|