autotel-cloudflare 2.13.0 → 2.14.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/dist/{chunk-ZJPX4N7S.js → chunk-WDNZVVRW.js} +7 -8
- package/dist/chunk-WDNZVVRW.js.map +1 -0
- package/dist/handlers.d.ts +5 -14
- package/dist/handlers.js +1 -1
- package/dist/index.js +2 -2
- package/package.json +2 -2
- package/src/handlers/workflows.test.ts +325 -0
- package/src/handlers/workflows.ts +51 -41
- package/dist/chunk-ZJPX4N7S.js.map +0 -1
|
@@ -228,17 +228,16 @@ function instrumentWorkflowRun(runFn, workflowName, workflowClass) {
|
|
|
228
228
|
kind: SpanKind.INTERNAL,
|
|
229
229
|
attributes: {
|
|
230
230
|
"workflow.name": workflowName,
|
|
231
|
+
"workflow.instance_id": event.instanceId,
|
|
231
232
|
"faas.trigger": "workflow",
|
|
232
|
-
"faas.coldstart": isColdStart2(workflowClass)
|
|
233
|
-
// Add workflow event attributes if available
|
|
234
|
-
...event?.workflowId && { "workflow.id": event.workflowId },
|
|
235
|
-
...event?.runId && { "workflow.run_id": event.runId }
|
|
233
|
+
"faas.coldstart": isColdStart2(workflowClass)
|
|
236
234
|
}
|
|
237
235
|
},
|
|
238
236
|
async (span) => {
|
|
239
237
|
try {
|
|
240
|
-
await runFn.call(this, event, instrumentedStep);
|
|
238
|
+
const result = await runFn.call(this, event, instrumentedStep);
|
|
241
239
|
span.setStatus({ code: SpanStatusCode.OK });
|
|
240
|
+
return result;
|
|
242
241
|
} catch (error) {
|
|
243
242
|
span.recordException(error);
|
|
244
243
|
span.setStatus({
|
|
@@ -277,7 +276,7 @@ function instrumentWorkflow(workflowClass, workflowName, config) {
|
|
|
277
276
|
const classHandler = {
|
|
278
277
|
construct(target, args) {
|
|
279
278
|
const env = args[args.length - 1] || {};
|
|
280
|
-
const trigger =
|
|
279
|
+
const trigger = { type: "workflow", name: workflowName };
|
|
281
280
|
const workflowConfig = initialiser(env, trigger);
|
|
282
281
|
const context$1 = setConfig(workflowConfig);
|
|
283
282
|
const workflowInstance = context.with(context$1, () => {
|
|
@@ -294,5 +293,5 @@ function instrumentWorkflow(workflowClass, workflowName, config) {
|
|
|
294
293
|
}
|
|
295
294
|
|
|
296
295
|
export { instrumentDO, instrumentWorkflow };
|
|
297
|
-
//# sourceMappingURL=chunk-
|
|
298
|
-
//# sourceMappingURL=chunk-
|
|
296
|
+
//# sourceMappingURL=chunk-WDNZVVRW.js.map
|
|
297
|
+
//# sourceMappingURL=chunk-WDNZVVRW.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/handlers/durable-objects.ts","../src/handlers/workflows.ts"],"names":["api_context","context","coldStarts","isColdStart","trace","SpanKind","SpanStatusCode","createInitialiser","setConfig"],"mappings":"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","file":"chunk-WDNZVVRW.js","sourcesContent":["/**\n * Durable Objects instrumentation for Cloudflare Workers\n * \n * Note: This file uses Cloudflare Workers types (DurableObjectId, DurableObjectState, etc.)\n * which are globally available via @cloudflare/workers-types when listed in tsconfig.json.\n * These types are devDependencies only - they're not runtime dependencies.\n * At runtime, Cloudflare Workers runtime provides the actual implementations.\n */\n\nimport {\n trace,\n context as api_context,\n propagation,\n SpanStatusCode,\n SpanKind,\n} from '@opentelemetry/api';\nimport type { ConfigurationOption } from 'autotel-edge';\nimport { createInitialiser, setConfig, WorkerTracer } from 'autotel-edge';\nimport { wrap } from '../bindings/common';\n\n// Durable Object types\ntype DOFetchFn = (request: Request) => Response | Promise<Response>;\ntype DOAlarmFn = () => void | Promise<void>;\n\n/**\n * Track cold starts per DO class\n */\nconst coldStarts = new WeakMap<any, boolean>();\n\nfunction isColdStart(doClass: any): boolean {\n if (!coldStarts.has(doClass)) {\n coldStarts.set(doClass, true);\n return true;\n }\n return false;\n}\n\n/**\n * Instrument a Durable Object fetch method\n */\nfunction instrumentDOFetch(\n fetchFn: DOFetchFn,\n id: DurableObjectId,\n doClass: any,\n): DOFetchFn {\n return async function instrumentedFetch(\n this: any,\n request: Request,\n ): Promise<Response> {\n const tracer = trace.getTracer('autotel-edge') as WorkerTracer;\n\n // Extract parent context from request headers\n const parentContext = propagation.extract(\n api_context.active(),\n request.headers,\n );\n\n const url = new URL(request.url);\n const spanName = `DO ${id.name || id.toString()}: ${request.method} ${url.pathname}`;\n\n return tracer.startActiveSpan(\n spanName,\n {\n kind: SpanKind.SERVER,\n attributes: {\n 'http.request.method': request.method,\n 'url.full': request.url,\n 'do.id': id.toString(),\n 'do.id.name': id.name || '',\n 'faas.trigger': 'http',\n 'faas.coldstart': isColdStart(doClass),\n },\n },\n parentContext,\n async (span) => {\n try {\n const response = await fetchFn.call(this, request);\n\n span.setAttributes({\n 'http.response.status_code': response.status,\n });\n\n if (response.ok) {\n span.setStatus({ code: SpanStatusCode.OK });\n } else {\n span.setStatus({ code: SpanStatusCode.ERROR });\n }\n\n return response;\n } catch (error) {\n span.recordException(error as Error);\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: error instanceof Error ? error.message : String(error),\n });\n throw error;\n } finally {\n span.end();\n }\n },\n );\n };\n}\n\n/**\n * Instrument a Durable Object alarm method\n */\nfunction instrumentDOAlarm(\n alarmFn: DOAlarmFn,\n id: DurableObjectId,\n doClass: any,\n): DOAlarmFn {\n return async function instrumentedAlarm(this: any): Promise<void> {\n const tracer = trace.getTracer('autotel-edge') as WorkerTracer;\n\n const spanName = `DO ${id.name || id.toString()}: alarm`;\n\n return tracer.startActiveSpan(\n spanName,\n {\n kind: SpanKind.INTERNAL,\n attributes: {\n 'do.id': id.toString(),\n 'do.id.name': id.name || '',\n 'faas.trigger': 'timer',\n 'faas.coldstart': isColdStart(doClass),\n },\n },\n async (span) => {\n try {\n await alarmFn.call(this);\n span.setStatus({ code: SpanStatusCode.OK });\n } catch (error) {\n span.recordException(error as Error);\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: error instanceof Error ? error.message : String(error),\n });\n throw error;\n } finally {\n span.end();\n }\n },\n );\n };\n}\n\n/**\n * Instrument a Durable Object instance\n */\nfunction instrumentDOInstance(\n doInstance: any,\n state: DurableObjectState,\n _env: any,\n doClass: any,\n): any {\n const instanceHandler: ProxyHandler<any> = {\n get(target, prop) {\n const value = Reflect.get(target, prop);\n\n if (prop === 'fetch' && typeof value === 'function') {\n return instrumentDOFetch(value.bind(target), state.id, doClass);\n }\n\n if (prop === 'alarm' && typeof value === 'function') {\n return instrumentDOAlarm(value.bind(target), state.id, doClass);\n }\n\n // Bind other methods to the target\n if (typeof value === 'function') {\n return value.bind(target);\n }\n\n return value;\n },\n };\n\n return wrap(doInstance, instanceHandler);\n}\n\n/**\n * Instrument a Durable Object class\n *\n * This wraps the DO class to automatically trace all fetch and alarm calls,\n * as well as initialize the telemetry configuration.\n *\n * **Usage:**\n * ```typescript\n * import { DurableObject } from 'cloudflare:workers'\n * import { instrumentDO } from 'autotel-edge'\n *\n * export class Counter extends DurableObject<Env> {\n * async fetch(request: Request) {\n * // Your DO logic here\n * return new Response('OK')\n * }\n * }\n *\n * // Wrap the class before exporting\n * export const CounterDO = instrumentDO(Counter, (env: Env) => ({\n * exporter: {\n * url: env.OTLP_ENDPOINT,\n * headers: { 'x-api-key': env.API_KEY }\n * },\n * service: {\n * name: 'my-durable-object',\n * version: '1.0.0'\n * }\n * }))\n * ```\n *\n * **What you get:**\n * - 🎯 Automatic spans for fetch() calls with HTTP attributes\n * - ⏰ Automatic spans for alarm() calls\n * - 🥶 Cold start tracking\n * - 🔗 Context propagation from incoming requests\n * - ⚡ Automatic span lifecycle management\n *\n * @param doClass - The Durable Object class to instrument\n * @param config - Configuration or configuration function\n * @returns Instrumented Durable Object class\n */\nexport function instrumentDO<C extends new (state: DurableObjectState, env: any) => any>(\n doClass: C,\n config: ConfigurationOption,\n): C {\n const initialiser = createInitialiser(config);\n\n const classHandler: ProxyHandler<C> = {\n construct(target, [state, env]: [DurableObjectState, any]) {\n // Initialize config for this DO instance\n const trigger = {\n id: state.id.toString(),\n name: state.id.name,\n };\n const doConfig = initialiser(env, trigger);\n const context = setConfig(doConfig);\n\n // Create the DO instance within the config context\n const doInstance = api_context.with(context, () => {\n return new target(state, env);\n });\n\n // Instrument the instance\n return instrumentDOInstance(doInstance, state, env, doClass);\n },\n };\n\n return wrap(doClass, classHandler);\n}\n","/**\n * Cloudflare Workflows instrumentation for autotel-edge\n *\n * Instruments WorkflowEntrypoint classes to automatically trace workflow execution,\n * step operations, retries, and sleeps.\n *\n * Based on Cloudflare Workflows API:\n * https://developers.cloudflare.com/workflows/\n */\n\nimport {\n trace,\n context as api_context,\n SpanStatusCode,\n SpanKind,\n} from '@opentelemetry/api';\nimport type { ConfigurationOption, WorkflowTrigger } from 'autotel-edge';\nimport { createInitialiser, setConfig, WorkerTracer } from 'autotel-edge';\nimport { wrap } from '../bindings/common';\n\n/**\n * Workflow types matching the Cloudflare Workers Workflows API.\n * @see https://developers.cloudflare.com/workflows/\n */\n\ninterface WorkflowEvent<T = unknown> {\n payload: Readonly<T>;\n timestamp: Date;\n instanceId: string;\n}\n\ninterface WorkflowStepConfig {\n retries?: {\n limit: number;\n delay?: string | number;\n backoff?: 'constant' | 'linear' | 'exponential';\n };\n timeout?: string | number;\n}\n\ninterface WorkflowStep {\n do<T>(name: string, callback: () => Promise<T>): Promise<T>;\n do<T>(name: string, config: WorkflowStepConfig, callback: () => Promise<T>): Promise<T>;\n sleep(name: string, duration: string | number): Promise<void>;\n sleepUntil(name: string, timestamp: Date | number): Promise<void>;\n}\n\ntype WorkflowRunFn = (\n event: Readonly<WorkflowEvent>,\n step: WorkflowStep,\n) => Promise<unknown> | void;\n\n/**\n * Track cold starts per Workflow class\n */\nconst coldStarts = new WeakMap<object, boolean>();\n\nfunction isColdStart(workflowClass: object): boolean {\n if (!coldStarts.has(workflowClass)) {\n coldStarts.set(workflowClass, true);\n return true;\n }\n return false;\n}\n\n/**\n * Proxy the step object to instrument step.do() and step.sleep() calls\n */\nfunction instrumentWorkflowStep(\n step: WorkflowStep,\n workflowName: string,\n): WorkflowStep {\n const stepHandler: ProxyHandler<WorkflowStep> = {\n get(target, prop) {\n const value = Reflect.get(target, prop);\n\n // Instrument step.do() to create spans for each workflow step\n if (prop === 'do' && typeof value === 'function') {\n return new Proxy(value, {\n apply: (fnTarget, thisArg, args) => {\n const [stepName] = args as [string, ...unknown[]];\n\n const tracer = trace.getTracer('autotel-edge') as WorkerTracer;\n\n return tracer.startActiveSpan(\n `Workflow ${workflowName}: ${stepName}`,\n {\n kind: SpanKind.INTERNAL,\n attributes: {\n 'workflow.step.name': stepName,\n 'workflow.name': workflowName,\n },\n },\n async (span) => {\n try {\n const result = await Reflect.apply(fnTarget, thisArg, args);\n span.setStatus({ code: SpanStatusCode.OK });\n return result;\n } catch (error) {\n span.recordException(error as Error);\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message:\n error instanceof Error ? error.message : String(error),\n });\n throw error;\n } finally {\n span.end();\n }\n },\n );\n },\n });\n }\n\n // Instrument step.sleep() to track workflow delays\n if (prop === 'sleep' && typeof value === 'function') {\n return new Proxy(value, {\n apply: (fnTarget, thisArg, args) => {\n const [sleepName, duration] = args as [string, string | number];\n\n const tracer = trace.getTracer('autotel-edge') as WorkerTracer;\n\n return tracer.startActiveSpan(\n `Workflow ${workflowName}: sleep ${sleepName}`,\n {\n kind: SpanKind.INTERNAL,\n attributes: {\n 'workflow.sleep.name': sleepName,\n 'workflow.sleep.duration': String(duration),\n 'workflow.name': workflowName,\n },\n },\n async (span) => {\n try {\n const result = await Reflect.apply(fnTarget, thisArg, args);\n span.setStatus({ code: SpanStatusCode.OK });\n return result;\n } catch (error) {\n span.recordException(error as Error);\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message:\n error instanceof Error ? error.message : String(error),\n });\n throw error;\n } finally {\n span.end();\n }\n },\n );\n },\n });\n }\n\n // Pass through other step methods\n if (typeof value === 'function') {\n return value.bind(target);\n }\n\n return value;\n },\n };\n\n return wrap(step, stepHandler);\n}\n\n/**\n * Instrument a Workflow run method\n */\nfunction instrumentWorkflowRun(\n runFn: WorkflowRunFn,\n workflowName: string,\n workflowClass: object,\n): WorkflowRunFn {\n return async function instrumentedRun(\n this: unknown,\n event: Readonly<WorkflowEvent>,\n step: WorkflowStep,\n ): Promise<unknown> {\n const tracer = trace.getTracer('autotel-edge') as WorkerTracer;\n\n // Instrument the step object to track individual operations\n const instrumentedStep = instrumentWorkflowStep(step, workflowName);\n\n const spanName = `Workflow ${workflowName}: run`;\n\n return tracer.startActiveSpan(\n spanName,\n {\n kind: SpanKind.INTERNAL,\n attributes: {\n 'workflow.name': workflowName,\n 'workflow.instance_id': event.instanceId,\n 'faas.trigger': 'workflow',\n 'faas.coldstart': isColdStart(workflowClass),\n },\n },\n async (span) => {\n try {\n const result = await runFn.call(this, event, instrumentedStep);\n span.setStatus({ code: SpanStatusCode.OK });\n return result;\n } catch (error) {\n span.recordException(error as Error);\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: error instanceof Error ? error.message : String(error),\n });\n throw error;\n } finally {\n span.end();\n }\n },\n );\n };\n}\n\n/**\n * Instrument a Workflow instance\n */\nfunction instrumentWorkflowInstance(\n workflowInstance: Record<string, unknown>,\n workflowName: string,\n workflowClass: object,\n): Record<string, unknown> {\n const instanceHandler: ProxyHandler<Record<string, unknown>> = {\n get(target, prop) {\n const value = Reflect.get(target, prop);\n\n if (prop === 'run' && typeof value === 'function') {\n return instrumentWorkflowRun(\n value.bind(target) as WorkflowRunFn,\n workflowName,\n workflowClass,\n );\n }\n\n // Bind other methods to the target\n if (typeof value === 'function') {\n return value.bind(target);\n }\n\n return value;\n },\n };\n\n return wrap(workflowInstance, instanceHandler);\n}\n\n/**\n * Instrument a Cloudflare Workflow class\n *\n * This wraps the WorkflowEntrypoint class to automatically trace workflow execution,\n * step operations, retries, and sleeps.\n *\n * **Usage:**\n * ```typescript\n * import { WorkflowEntrypoint } from 'cloudflare:workers'\n * import { instrumentWorkflow } from 'autotel-cloudflare/handlers'\n *\n * class MyWorkflow extends WorkflowEntrypoint {\n * async run(event, step) {\n * await step.do('submit payment', async () => {\n * return await submitToPaymentProcessor(event.payload.payment)\n * })\n *\n * await step.sleep('wait for feedback', '2 days')\n *\n * await step.do('send feedback email', sendFeedbackEmail)\n * }\n * }\n *\n * export const CheckoutWorkflow = instrumentWorkflow(\n * MyWorkflow,\n * 'checkout-workflow',\n * (env: Env) => ({\n * exporter: {\n * url: env.OTLP_ENDPOINT,\n * headers: { 'x-api-key': env.API_KEY }\n * },\n * service: {\n * name: 'checkout-workflow',\n * version: '1.0.0'\n * }\n * })\n * )\n * ```\n *\n * @param workflowClass - The WorkflowEntrypoint class to instrument\n * @param workflowName - The name of the workflow (used in span names)\n * @param config - Configuration or configuration function\n * @returns Instrumented Workflow class\n */\nexport function instrumentWorkflow<\n C extends new (...args: any[]) => any,\n>(\n workflowClass: C,\n workflowName: string,\n config: ConfigurationOption,\n): C {\n const initialiser = createInitialiser(config);\n\n const classHandler: ProxyHandler<C> = {\n construct(target, args: any[]) {\n // Extract env from constructor args (typically last arg)\n const env = args[args.length - 1] || {};\n\n const trigger: WorkflowTrigger = { type: 'workflow', name: workflowName };\n const workflowConfig = initialiser(env, trigger);\n const context = setConfig(workflowConfig);\n\n // Create the workflow instance within the config context\n const workflowInstance = api_context.with(context, () => {\n return new target(...args);\n });\n\n // Instrument the instance\n return instrumentWorkflowInstance(\n workflowInstance,\n workflowName,\n workflowClass,\n );\n },\n };\n\n return wrap(workflowClass, classHandler);\n}\n"]}
|
package/dist/handlers.d.ts
CHANGED
|
@@ -72,12 +72,12 @@ declare function instrumentDO<C extends new (state: DurableObjectState, env: any
|
|
|
72
72
|
* **Usage:**
|
|
73
73
|
* ```typescript
|
|
74
74
|
* import { WorkflowEntrypoint } from 'cloudflare:workers'
|
|
75
|
-
* import { instrumentWorkflow } from 'autotel-
|
|
75
|
+
* import { instrumentWorkflow } from 'autotel-cloudflare/handlers'
|
|
76
76
|
*
|
|
77
|
-
*
|
|
77
|
+
* class MyWorkflow extends WorkflowEntrypoint {
|
|
78
78
|
* async run(event, step) {
|
|
79
79
|
* await step.do('submit payment', async () => {
|
|
80
|
-
* return await submitToPaymentProcessor(event.
|
|
80
|
+
* return await submitToPaymentProcessor(event.payload.payment)
|
|
81
81
|
* })
|
|
82
82
|
*
|
|
83
83
|
* await step.sleep('wait for feedback', '2 days')
|
|
@@ -86,9 +86,8 @@ declare function instrumentDO<C extends new (state: DurableObjectState, env: any
|
|
|
86
86
|
* }
|
|
87
87
|
* }
|
|
88
88
|
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
* CheckoutWorkflow,
|
|
89
|
+
* export const CheckoutWorkflow = instrumentWorkflow(
|
|
90
|
+
* MyWorkflow,
|
|
92
91
|
* 'checkout-workflow',
|
|
93
92
|
* (env: Env) => ({
|
|
94
93
|
* exporter: {
|
|
@@ -103,14 +102,6 @@ declare function instrumentDO<C extends new (state: DurableObjectState, env: any
|
|
|
103
102
|
* )
|
|
104
103
|
* ```
|
|
105
104
|
*
|
|
106
|
-
* **What you get:**
|
|
107
|
-
* - 🎯 Automatic spans for workflow.run() execution
|
|
108
|
-
* - 📋 Automatic spans for each step.do() operation
|
|
109
|
-
* - ⏸️ Automatic spans for step.sleep() operations
|
|
110
|
-
* - 🔄 Automatic retry tracking (via step.do retries)
|
|
111
|
-
* - 🥶 Cold start tracking
|
|
112
|
-
* - ⚡ Automatic span lifecycle management
|
|
113
|
-
*
|
|
114
105
|
* @param workflowClass - The WorkflowEntrypoint class to instrument
|
|
115
106
|
* @param workflowName - The name of the workflow (used in span names)
|
|
116
107
|
* @param config - Configuration or configuration function
|
package/dist/handlers.js
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { instrumentBindings } from './chunk-4UG2QCPQ.js';
|
|
2
2
|
export { instrumentAI, instrumentAnalyticsEngine, instrumentBindings, instrumentBrowserRendering, instrumentD1, instrumentHyperdrive, instrumentImages, instrumentKV, instrumentQueueProducer, instrumentR2, instrumentRateLimiter, instrumentServiceBinding, instrumentVectorize } from './chunk-4UG2QCPQ.js';
|
|
3
|
-
import { instrumentDO } from './chunk-
|
|
4
|
-
export { instrumentDO, instrumentWorkflow } from './chunk-
|
|
3
|
+
import { instrumentDO } from './chunk-WDNZVVRW.js';
|
|
4
|
+
export { instrumentDO, instrumentWorkflow } from './chunk-WDNZVVRW.js';
|
|
5
5
|
import { wrap, unwrap, proxyExecutionContext } from './chunk-O4IYKWPJ.js';
|
|
6
6
|
import { createInitialiser, getActiveConfig, setConfig, WorkerTracerProvider, WorkerTracer } from 'autotel-edge';
|
|
7
7
|
export * from 'autotel-edge';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "autotel-cloudflare",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.14.0",
|
|
4
4
|
"description": "The #1 OpenTelemetry package for Cloudflare Workers - complete bindings coverage, native CF OTel integration, advanced sampling",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"dependencies": {
|
|
70
70
|
"@opentelemetry/api": "^1.9.0",
|
|
71
71
|
"@opentelemetry/resources": "^2.5.1",
|
|
72
|
-
"autotel-edge": "3.
|
|
72
|
+
"autotel-edge": "3.12.0"
|
|
73
73
|
},
|
|
74
74
|
"peerDependencies": {
|
|
75
75
|
"@cloudflare/workers-types": "^4.20260302.0"
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
2
|
+
import { instrumentWorkflow } from './workflows';
|
|
3
|
+
import { trace, SpanStatusCode, SpanKind } from '@opentelemetry/api';
|
|
4
|
+
|
|
5
|
+
describe('Workflow Instrumentation', () => {
|
|
6
|
+
let mockTracer: any;
|
|
7
|
+
let mockSpan: any;
|
|
8
|
+
let getTracerSpy: any;
|
|
9
|
+
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
mockSpan = {
|
|
12
|
+
spanContext: () => ({
|
|
13
|
+
traceId: 'test-trace-id',
|
|
14
|
+
spanId: 'test-span-id',
|
|
15
|
+
traceFlags: 1,
|
|
16
|
+
}),
|
|
17
|
+
setAttribute: vi.fn(),
|
|
18
|
+
setAttributes: vi.fn(),
|
|
19
|
+
setStatus: vi.fn(),
|
|
20
|
+
recordException: vi.fn(),
|
|
21
|
+
end: vi.fn(),
|
|
22
|
+
isRecording: () => true,
|
|
23
|
+
updateName: vi.fn(),
|
|
24
|
+
addEvent: vi.fn(),
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
mockTracer = {
|
|
28
|
+
startActiveSpan: vi.fn((name, options, context, fn) => {
|
|
29
|
+
if (typeof options === 'function') return options(mockSpan);
|
|
30
|
+
if (typeof context === 'function') return context(mockSpan);
|
|
31
|
+
if (typeof fn === 'function') return fn(mockSpan);
|
|
32
|
+
return Promise.resolve();
|
|
33
|
+
}),
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
getTracerSpy = vi.spyOn(trace, 'getTracer').mockReturnValue(mockTracer as any);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
afterEach(() => {
|
|
40
|
+
getTracerSpy.mockRestore();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
describe('instrumentWorkflow()', () => {
|
|
44
|
+
it('should wrap workflow class constructor', () => {
|
|
45
|
+
class TestWorkflow {
|
|
46
|
+
constructor(public ctx: any, public env: any) {}
|
|
47
|
+
async run() {}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const Instrumented = instrumentWorkflow(TestWorkflow, 'test-workflow', {
|
|
51
|
+
service: { name: 'test' },
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
expect(Instrumented).toBeDefined();
|
|
55
|
+
expect(typeof Instrumented).toBe('function');
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('should create workflow instance with instrumented run()', () => {
|
|
59
|
+
class TestWorkflow {
|
|
60
|
+
constructor(public ctx: any, public env: any) {}
|
|
61
|
+
async run() { return 'done'; }
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const Instrumented = instrumentWorkflow(TestWorkflow, 'test-workflow', {
|
|
65
|
+
service: { name: 'test' },
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const instance = new Instrumented({}, {});
|
|
69
|
+
|
|
70
|
+
expect(instance).toBeDefined();
|
|
71
|
+
expect(typeof instance.run).toBe('function');
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('should accept static config', () => {
|
|
75
|
+
class TestWorkflow {
|
|
76
|
+
constructor(public ctx: any, public env: any) {}
|
|
77
|
+
async run() {}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const Instrumented = instrumentWorkflow(TestWorkflow, 'test-workflow', {
|
|
81
|
+
service: { name: 'test', version: '1.0.0' },
|
|
82
|
+
exporter: { url: 'http://localhost:4318/v1/traces' },
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
expect(Instrumented).toBeDefined();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('should accept config function', () => {
|
|
89
|
+
class TestWorkflow {
|
|
90
|
+
constructor(public ctx: any, public env: any) {}
|
|
91
|
+
async run() {}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
interface Env {
|
|
95
|
+
OTLP_ENDPOINT: string;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const Instrumented = instrumentWorkflow(TestWorkflow, 'test-workflow', (env: Env) => ({
|
|
99
|
+
service: { name: 'test' },
|
|
100
|
+
exporter: { url: env.OTLP_ENDPOINT },
|
|
101
|
+
}));
|
|
102
|
+
|
|
103
|
+
expect(Instrumented).toBeDefined();
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
describe('run() instrumentation', () => {
|
|
108
|
+
it('should preserve run() return value', async () => {
|
|
109
|
+
const output = { status: 'ok', orderId: 'ord-123' };
|
|
110
|
+
|
|
111
|
+
class TestWorkflow {
|
|
112
|
+
constructor(public ctx: any, public env: any) {}
|
|
113
|
+
async run(event: any, step: any) {
|
|
114
|
+
return output;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const Instrumented = instrumentWorkflow(TestWorkflow, 'return-test', {
|
|
119
|
+
service: { name: 'test' },
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
const instance = new Instrumented({}, {});
|
|
123
|
+
const event = { payload: {}, timestamp: new Date(), instanceId: 'wf-return' };
|
|
124
|
+
const step = { do: vi.fn(), sleep: vi.fn(), sleepUntil: vi.fn() };
|
|
125
|
+
|
|
126
|
+
await expect(instance.run(event, step)).resolves.toEqual(output);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('should create span for run() with workflow attributes', async () => {
|
|
130
|
+
class TestWorkflow {
|
|
131
|
+
constructor(public ctx: any, public env: any) {}
|
|
132
|
+
async run(event: any, step: any) {}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const Instrumented = instrumentWorkflow(TestWorkflow, 'test-workflow', {
|
|
136
|
+
service: { name: 'test' },
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
const instance = new Instrumented({}, {});
|
|
140
|
+
const event = { payload: { foo: 'bar' }, timestamp: new Date(), instanceId: 'wf-123' };
|
|
141
|
+
const step = { do: vi.fn(), sleep: vi.fn(), sleepUntil: vi.fn() };
|
|
142
|
+
|
|
143
|
+
await instance.run(event, step);
|
|
144
|
+
|
|
145
|
+
expect(mockTracer.startActiveSpan).toHaveBeenCalled();
|
|
146
|
+
|
|
147
|
+
const spanName = mockTracer.startActiveSpan.mock.calls[0][0];
|
|
148
|
+
expect(spanName).toBe('Workflow test-workflow: run');
|
|
149
|
+
|
|
150
|
+
const options = mockTracer.startActiveSpan.mock.calls[0][1];
|
|
151
|
+
expect(options.kind).toBe(SpanKind.INTERNAL);
|
|
152
|
+
expect(options.attributes['workflow.name']).toBe('test-workflow');
|
|
153
|
+
expect(options.attributes['workflow.instance_id']).toBe('wf-123');
|
|
154
|
+
expect(options.attributes['faas.trigger']).toBe('workflow');
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it('should track cold starts', async () => {
|
|
158
|
+
class TestWorkflow {
|
|
159
|
+
constructor(public ctx: any, public env: any) {}
|
|
160
|
+
async run(event: any, step: any) {}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const Instrumented = instrumentWorkflow(TestWorkflow, 'cold-test', {
|
|
164
|
+
service: { name: 'test' },
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
const event = { payload: {}, timestamp: new Date(), instanceId: 'wf-1' };
|
|
168
|
+
const step = { do: vi.fn(), sleep: vi.fn(), sleepUntil: vi.fn() };
|
|
169
|
+
|
|
170
|
+
const instance1 = new Instrumented({}, {});
|
|
171
|
+
await instance1.run(event, step);
|
|
172
|
+
const firstOptions = mockTracer.startActiveSpan.mock.calls[0][1];
|
|
173
|
+
expect(firstOptions.attributes['faas.coldstart']).toBe(true);
|
|
174
|
+
|
|
175
|
+
const instance2 = new Instrumented({}, {});
|
|
176
|
+
await instance2.run(event, step);
|
|
177
|
+
const secondOptions = mockTracer.startActiveSpan.mock.calls[1][1];
|
|
178
|
+
expect(secondOptions.attributes['faas.coldstart']).toBe(false);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it('should handle run() errors', async () => {
|
|
182
|
+
class TestWorkflow {
|
|
183
|
+
constructor(public ctx: any, public env: any) {}
|
|
184
|
+
async run() {
|
|
185
|
+
throw new Error('Workflow failed');
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const Instrumented = instrumentWorkflow(TestWorkflow, 'error-test', {
|
|
190
|
+
service: { name: 'test' },
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
const instance = new Instrumented({}, {});
|
|
194
|
+
const event = { payload: {}, timestamp: new Date(), instanceId: 'wf-err' };
|
|
195
|
+
const step = { do: vi.fn(), sleep: vi.fn(), sleepUntil: vi.fn() };
|
|
196
|
+
|
|
197
|
+
await expect(instance.run(event, step)).rejects.toThrow('Workflow failed');
|
|
198
|
+
|
|
199
|
+
expect(mockSpan.recordException).toHaveBeenCalled();
|
|
200
|
+
expect(mockSpan.setStatus).toHaveBeenCalledWith({
|
|
201
|
+
code: SpanStatusCode.ERROR,
|
|
202
|
+
message: 'Workflow failed',
|
|
203
|
+
});
|
|
204
|
+
expect(mockSpan.end).toHaveBeenCalled();
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
describe('step.do() instrumentation', () => {
|
|
209
|
+
it('should create span for step.do() calls', async () => {
|
|
210
|
+
const stepDoResult = { paymentId: 'pay_123' };
|
|
211
|
+
const mockStepDo = vi.fn().mockResolvedValue(stepDoResult);
|
|
212
|
+
|
|
213
|
+
class TestWorkflow {
|
|
214
|
+
constructor(public ctx: any, public env: any) {}
|
|
215
|
+
async run(event: any, step: any) {
|
|
216
|
+
return await step.do('process payment', async () => stepDoResult);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const Instrumented = instrumentWorkflow(TestWorkflow, 'step-test', {
|
|
221
|
+
service: { name: 'test' },
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
const instance = new Instrumented({}, {});
|
|
225
|
+
const event = { payload: {}, timestamp: new Date(), instanceId: 'wf-step' };
|
|
226
|
+
const step = {
|
|
227
|
+
do: vi.fn(async (_name: string, cb: () => Promise<any>) => cb()),
|
|
228
|
+
sleep: vi.fn(),
|
|
229
|
+
sleepUntil: vi.fn(),
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
await instance.run(event, step);
|
|
233
|
+
|
|
234
|
+
// Should have spans for both run() and step.do()
|
|
235
|
+
expect(mockTracer.startActiveSpan.mock.calls.length).toBeGreaterThanOrEqual(2);
|
|
236
|
+
|
|
237
|
+
const stepSpanCall = mockTracer.startActiveSpan.mock.calls[1];
|
|
238
|
+
expect(stepSpanCall[0]).toBe('Workflow step-test: process payment');
|
|
239
|
+
expect(stepSpanCall[1].attributes['workflow.step.name']).toBe('process payment');
|
|
240
|
+
expect(stepSpanCall[1].attributes['workflow.name']).toBe('step-test');
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
it('should handle step.do() errors', async () => {
|
|
244
|
+
class TestWorkflow {
|
|
245
|
+
constructor(public ctx: any, public env: any) {}
|
|
246
|
+
async run(event: any, step: any) {
|
|
247
|
+
await step.do('failing step', async () => {
|
|
248
|
+
throw new Error('Step failed');
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const Instrumented = instrumentWorkflow(TestWorkflow, 'step-err', {
|
|
254
|
+
service: { name: 'test' },
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
const instance = new Instrumented({}, {});
|
|
258
|
+
const event = { payload: {}, timestamp: new Date(), instanceId: 'wf-step-err' };
|
|
259
|
+
const step = {
|
|
260
|
+
do: vi.fn(async (_name: string, cb: () => Promise<any>) => cb()),
|
|
261
|
+
sleep: vi.fn(),
|
|
262
|
+
sleepUntil: vi.fn(),
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
await expect(instance.run(event, step)).rejects.toThrow('Step failed');
|
|
266
|
+
|
|
267
|
+
expect(mockSpan.recordException).toHaveBeenCalled();
|
|
268
|
+
expect(mockSpan.end).toHaveBeenCalled();
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
describe('step.sleep() instrumentation', () => {
|
|
273
|
+
it('should create span for step.sleep() calls', async () => {
|
|
274
|
+
class TestWorkflow {
|
|
275
|
+
constructor(public ctx: any, public env: any) {}
|
|
276
|
+
async run(event: any, step: any) {
|
|
277
|
+
await step.sleep('wait for settlement', '2 hours');
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const Instrumented = instrumentWorkflow(TestWorkflow, 'sleep-test', {
|
|
282
|
+
service: { name: 'test' },
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
const instance = new Instrumented({}, {});
|
|
286
|
+
const event = { payload: {}, timestamp: new Date(), instanceId: 'wf-sleep' };
|
|
287
|
+
const step = {
|
|
288
|
+
do: vi.fn(),
|
|
289
|
+
sleep: vi.fn().mockResolvedValue(undefined),
|
|
290
|
+
sleepUntil: vi.fn(),
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
await instance.run(event, step);
|
|
294
|
+
|
|
295
|
+
const sleepSpanCall = mockTracer.startActiveSpan.mock.calls[1];
|
|
296
|
+
expect(sleepSpanCall[0]).toBe('Workflow sleep-test: sleep wait for settlement');
|
|
297
|
+
expect(sleepSpanCall[1].attributes['workflow.sleep.name']).toBe('wait for settlement');
|
|
298
|
+
expect(sleepSpanCall[1].attributes['workflow.sleep.duration']).toBe('2 hours');
|
|
299
|
+
expect(sleepSpanCall[1].attributes['workflow.name']).toBe('sleep-test');
|
|
300
|
+
});
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
describe('this binding', () => {
|
|
304
|
+
it('should preserve this context in run()', async () => {
|
|
305
|
+
class TestWorkflow {
|
|
306
|
+
private value = 42;
|
|
307
|
+
constructor(public ctx: any, public env: any) {}
|
|
308
|
+
async run(event: any, step: any) {
|
|
309
|
+
return this.value;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const Instrumented = instrumentWorkflow(TestWorkflow, 'this-test', {
|
|
314
|
+
service: { name: 'test' },
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
const instance = new Instrumented({}, {});
|
|
318
|
+
const event = { payload: {}, timestamp: new Date(), instanceId: 'wf-this' };
|
|
319
|
+
const step = { do: vi.fn(), sleep: vi.fn(), sleepUntil: vi.fn() };
|
|
320
|
+
|
|
321
|
+
// Should not throw — this.value should be accessible
|
|
322
|
+
await expect(instance.run(event, step)).resolves.not.toThrow();
|
|
323
|
+
});
|
|
324
|
+
});
|
|
325
|
+
});
|
|
@@ -14,25 +14,48 @@ import {
|
|
|
14
14
|
SpanStatusCode,
|
|
15
15
|
SpanKind,
|
|
16
16
|
} from '@opentelemetry/api';
|
|
17
|
-
import type { ConfigurationOption } from 'autotel-edge';
|
|
17
|
+
import type { ConfigurationOption, WorkflowTrigger } from 'autotel-edge';
|
|
18
18
|
import { createInitialiser, setConfig, WorkerTracer } from 'autotel-edge';
|
|
19
19
|
import { wrap } from '../bindings/common';
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Workflow types matching the Cloudflare Workers Workflows API.
|
|
23
|
+
* @see https://developers.cloudflare.com/workflows/
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
interface WorkflowEvent<T = unknown> {
|
|
27
|
+
payload: Readonly<T>;
|
|
28
|
+
timestamp: Date;
|
|
29
|
+
instanceId: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface WorkflowStepConfig {
|
|
33
|
+
retries?: {
|
|
34
|
+
limit: number;
|
|
35
|
+
delay?: string | number;
|
|
36
|
+
backoff?: 'constant' | 'linear' | 'exponential';
|
|
37
|
+
};
|
|
38
|
+
timeout?: string | number;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
interface WorkflowStep {
|
|
42
|
+
do<T>(name: string, callback: () => Promise<T>): Promise<T>;
|
|
43
|
+
do<T>(name: string, config: WorkflowStepConfig, callback: () => Promise<T>): Promise<T>;
|
|
44
|
+
sleep(name: string, duration: string | number): Promise<void>;
|
|
45
|
+
sleepUntil(name: string, timestamp: Date | number): Promise<void>;
|
|
46
|
+
}
|
|
24
47
|
|
|
25
48
|
type WorkflowRunFn = (
|
|
26
|
-
event: WorkflowEvent
|
|
49
|
+
event: Readonly<WorkflowEvent>,
|
|
27
50
|
step: WorkflowStep,
|
|
28
|
-
) => Promise<
|
|
51
|
+
) => Promise<unknown> | void;
|
|
29
52
|
|
|
30
53
|
/**
|
|
31
54
|
* Track cold starts per Workflow class
|
|
32
55
|
*/
|
|
33
|
-
const coldStarts = new WeakMap<
|
|
56
|
+
const coldStarts = new WeakMap<object, boolean>();
|
|
34
57
|
|
|
35
|
-
function isColdStart(workflowClass:
|
|
58
|
+
function isColdStart(workflowClass: object): boolean {
|
|
36
59
|
if (!coldStarts.has(workflowClass)) {
|
|
37
60
|
coldStarts.set(workflowClass, true);
|
|
38
61
|
return true;
|
|
@@ -41,7 +64,7 @@ function isColdStart(workflowClass: any): boolean {
|
|
|
41
64
|
}
|
|
42
65
|
|
|
43
66
|
/**
|
|
44
|
-
* Proxy the step object to instrument step.do() calls
|
|
67
|
+
* Proxy the step object to instrument step.do() and step.sleep() calls
|
|
45
68
|
*/
|
|
46
69
|
function instrumentWorkflowStep(
|
|
47
70
|
step: WorkflowStep,
|
|
@@ -55,7 +78,7 @@ function instrumentWorkflowStep(
|
|
|
55
78
|
if (prop === 'do' && typeof value === 'function') {
|
|
56
79
|
return new Proxy(value, {
|
|
57
80
|
apply: (fnTarget, thisArg, args) => {
|
|
58
|
-
const [stepName] = args as [string,
|
|
81
|
+
const [stepName] = args as [string, ...unknown[]];
|
|
59
82
|
|
|
60
83
|
const tracer = trace.getTracer('autotel-edge') as WorkerTracer;
|
|
61
84
|
|
|
@@ -148,13 +171,13 @@ function instrumentWorkflowStep(
|
|
|
148
171
|
function instrumentWorkflowRun(
|
|
149
172
|
runFn: WorkflowRunFn,
|
|
150
173
|
workflowName: string,
|
|
151
|
-
workflowClass:
|
|
174
|
+
workflowClass: object,
|
|
152
175
|
): WorkflowRunFn {
|
|
153
176
|
return async function instrumentedRun(
|
|
154
|
-
this:
|
|
155
|
-
event: WorkflowEvent
|
|
177
|
+
this: unknown,
|
|
178
|
+
event: Readonly<WorkflowEvent>,
|
|
156
179
|
step: WorkflowStep,
|
|
157
|
-
): Promise<
|
|
180
|
+
): Promise<unknown> {
|
|
158
181
|
const tracer = trace.getTracer('autotel-edge') as WorkerTracer;
|
|
159
182
|
|
|
160
183
|
// Instrument the step object to track individual operations
|
|
@@ -168,17 +191,16 @@ function instrumentWorkflowRun(
|
|
|
168
191
|
kind: SpanKind.INTERNAL,
|
|
169
192
|
attributes: {
|
|
170
193
|
'workflow.name': workflowName,
|
|
194
|
+
'workflow.instance_id': event.instanceId,
|
|
171
195
|
'faas.trigger': 'workflow',
|
|
172
196
|
'faas.coldstart': isColdStart(workflowClass),
|
|
173
|
-
// Add workflow event attributes if available
|
|
174
|
-
...(event?.workflowId && { 'workflow.id': event.workflowId }),
|
|
175
|
-
...(event?.runId && { 'workflow.run_id': event.runId }),
|
|
176
197
|
},
|
|
177
198
|
},
|
|
178
199
|
async (span) => {
|
|
179
200
|
try {
|
|
180
|
-
await runFn.call(this, event, instrumentedStep);
|
|
201
|
+
const result = await runFn.call(this, event, instrumentedStep);
|
|
181
202
|
span.setStatus({ code: SpanStatusCode.OK });
|
|
203
|
+
return result;
|
|
182
204
|
} catch (error) {
|
|
183
205
|
span.recordException(error as Error);
|
|
184
206
|
span.setStatus({
|
|
@@ -198,17 +220,17 @@ function instrumentWorkflowRun(
|
|
|
198
220
|
* Instrument a Workflow instance
|
|
199
221
|
*/
|
|
200
222
|
function instrumentWorkflowInstance(
|
|
201
|
-
workflowInstance:
|
|
223
|
+
workflowInstance: Record<string, unknown>,
|
|
202
224
|
workflowName: string,
|
|
203
|
-
workflowClass:
|
|
204
|
-
):
|
|
205
|
-
const instanceHandler: ProxyHandler<
|
|
225
|
+
workflowClass: object,
|
|
226
|
+
): Record<string, unknown> {
|
|
227
|
+
const instanceHandler: ProxyHandler<Record<string, unknown>> = {
|
|
206
228
|
get(target, prop) {
|
|
207
229
|
const value = Reflect.get(target, prop);
|
|
208
230
|
|
|
209
231
|
if (prop === 'run' && typeof value === 'function') {
|
|
210
232
|
return instrumentWorkflowRun(
|
|
211
|
-
value.bind(target),
|
|
233
|
+
value.bind(target) as WorkflowRunFn,
|
|
212
234
|
workflowName,
|
|
213
235
|
workflowClass,
|
|
214
236
|
);
|
|
@@ -235,12 +257,12 @@ function instrumentWorkflowInstance(
|
|
|
235
257
|
* **Usage:**
|
|
236
258
|
* ```typescript
|
|
237
259
|
* import { WorkflowEntrypoint } from 'cloudflare:workers'
|
|
238
|
-
* import { instrumentWorkflow } from 'autotel-
|
|
260
|
+
* import { instrumentWorkflow } from 'autotel-cloudflare/handlers'
|
|
239
261
|
*
|
|
240
|
-
*
|
|
262
|
+
* class MyWorkflow extends WorkflowEntrypoint {
|
|
241
263
|
* async run(event, step) {
|
|
242
264
|
* await step.do('submit payment', async () => {
|
|
243
|
-
* return await submitToPaymentProcessor(event.
|
|
265
|
+
* return await submitToPaymentProcessor(event.payload.payment)
|
|
244
266
|
* })
|
|
245
267
|
*
|
|
246
268
|
* await step.sleep('wait for feedback', '2 days')
|
|
@@ -249,9 +271,8 @@ function instrumentWorkflowInstance(
|
|
|
249
271
|
* }
|
|
250
272
|
* }
|
|
251
273
|
*
|
|
252
|
-
*
|
|
253
|
-
*
|
|
254
|
-
* CheckoutWorkflow,
|
|
274
|
+
* export const CheckoutWorkflow = instrumentWorkflow(
|
|
275
|
+
* MyWorkflow,
|
|
255
276
|
* 'checkout-workflow',
|
|
256
277
|
* (env: Env) => ({
|
|
257
278
|
* exporter: {
|
|
@@ -266,14 +287,6 @@ function instrumentWorkflowInstance(
|
|
|
266
287
|
* )
|
|
267
288
|
* ```
|
|
268
289
|
*
|
|
269
|
-
* **What you get:**
|
|
270
|
-
* - 🎯 Automatic spans for workflow.run() execution
|
|
271
|
-
* - 📋 Automatic spans for each step.do() operation
|
|
272
|
-
* - ⏸️ Automatic spans for step.sleep() operations
|
|
273
|
-
* - 🔄 Automatic retry tracking (via step.do retries)
|
|
274
|
-
* - 🥶 Cold start tracking
|
|
275
|
-
* - ⚡ Automatic span lifecycle management
|
|
276
|
-
*
|
|
277
290
|
* @param workflowClass - The WorkflowEntrypoint class to instrument
|
|
278
291
|
* @param workflowName - The name of the workflow (used in span names)
|
|
279
292
|
* @param config - Configuration or configuration function
|
|
@@ -293,9 +306,7 @@ export function instrumentWorkflow<
|
|
|
293
306
|
// Extract env from constructor args (typically last arg)
|
|
294
307
|
const env = args[args.length - 1] || {};
|
|
295
308
|
|
|
296
|
-
|
|
297
|
-
// Use Request as trigger type since workflows don't have a standard Trigger type yet
|
|
298
|
-
const trigger = new Request('https://workflow.local/run');
|
|
309
|
+
const trigger: WorkflowTrigger = { type: 'workflow', name: workflowName };
|
|
299
310
|
const workflowConfig = initialiser(env, trigger);
|
|
300
311
|
const context = setConfig(workflowConfig);
|
|
301
312
|
|
|
@@ -315,4 +326,3 @@ export function instrumentWorkflow<
|
|
|
315
326
|
|
|
316
327
|
return wrap(workflowClass, classHandler);
|
|
317
328
|
}
|
|
318
|
-
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/handlers/durable-objects.ts","../src/handlers/workflows.ts"],"names":["api_context","context","coldStarts","isColdStart","trace","SpanKind","SpanStatusCode","createInitialiser","setConfig"],"mappings":"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","file":"chunk-ZJPX4N7S.js","sourcesContent":["/**\n * Durable Objects instrumentation for Cloudflare Workers\n * \n * Note: This file uses Cloudflare Workers types (DurableObjectId, DurableObjectState, etc.)\n * which are globally available via @cloudflare/workers-types when listed in tsconfig.json.\n * These types are devDependencies only - they're not runtime dependencies.\n * At runtime, Cloudflare Workers runtime provides the actual implementations.\n */\n\nimport {\n trace,\n context as api_context,\n propagation,\n SpanStatusCode,\n SpanKind,\n} from '@opentelemetry/api';\nimport type { ConfigurationOption } from 'autotel-edge';\nimport { createInitialiser, setConfig, WorkerTracer } from 'autotel-edge';\nimport { wrap } from '../bindings/common';\n\n// Durable Object types\ntype DOFetchFn = (request: Request) => Response | Promise<Response>;\ntype DOAlarmFn = () => void | Promise<void>;\n\n/**\n * Track cold starts per DO class\n */\nconst coldStarts = new WeakMap<any, boolean>();\n\nfunction isColdStart(doClass: any): boolean {\n if (!coldStarts.has(doClass)) {\n coldStarts.set(doClass, true);\n return true;\n }\n return false;\n}\n\n/**\n * Instrument a Durable Object fetch method\n */\nfunction instrumentDOFetch(\n fetchFn: DOFetchFn,\n id: DurableObjectId,\n doClass: any,\n): DOFetchFn {\n return async function instrumentedFetch(\n this: any,\n request: Request,\n ): Promise<Response> {\n const tracer = trace.getTracer('autotel-edge') as WorkerTracer;\n\n // Extract parent context from request headers\n const parentContext = propagation.extract(\n api_context.active(),\n request.headers,\n );\n\n const url = new URL(request.url);\n const spanName = `DO ${id.name || id.toString()}: ${request.method} ${url.pathname}`;\n\n return tracer.startActiveSpan(\n spanName,\n {\n kind: SpanKind.SERVER,\n attributes: {\n 'http.request.method': request.method,\n 'url.full': request.url,\n 'do.id': id.toString(),\n 'do.id.name': id.name || '',\n 'faas.trigger': 'http',\n 'faas.coldstart': isColdStart(doClass),\n },\n },\n parentContext,\n async (span) => {\n try {\n const response = await fetchFn.call(this, request);\n\n span.setAttributes({\n 'http.response.status_code': response.status,\n });\n\n if (response.ok) {\n span.setStatus({ code: SpanStatusCode.OK });\n } else {\n span.setStatus({ code: SpanStatusCode.ERROR });\n }\n\n return response;\n } catch (error) {\n span.recordException(error as Error);\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: error instanceof Error ? error.message : String(error),\n });\n throw error;\n } finally {\n span.end();\n }\n },\n );\n };\n}\n\n/**\n * Instrument a Durable Object alarm method\n */\nfunction instrumentDOAlarm(\n alarmFn: DOAlarmFn,\n id: DurableObjectId,\n doClass: any,\n): DOAlarmFn {\n return async function instrumentedAlarm(this: any): Promise<void> {\n const tracer = trace.getTracer('autotel-edge') as WorkerTracer;\n\n const spanName = `DO ${id.name || id.toString()}: alarm`;\n\n return tracer.startActiveSpan(\n spanName,\n {\n kind: SpanKind.INTERNAL,\n attributes: {\n 'do.id': id.toString(),\n 'do.id.name': id.name || '',\n 'faas.trigger': 'timer',\n 'faas.coldstart': isColdStart(doClass),\n },\n },\n async (span) => {\n try {\n await alarmFn.call(this);\n span.setStatus({ code: SpanStatusCode.OK });\n } catch (error) {\n span.recordException(error as Error);\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: error instanceof Error ? error.message : String(error),\n });\n throw error;\n } finally {\n span.end();\n }\n },\n );\n };\n}\n\n/**\n * Instrument a Durable Object instance\n */\nfunction instrumentDOInstance(\n doInstance: any,\n state: DurableObjectState,\n _env: any,\n doClass: any,\n): any {\n const instanceHandler: ProxyHandler<any> = {\n get(target, prop) {\n const value = Reflect.get(target, prop);\n\n if (prop === 'fetch' && typeof value === 'function') {\n return instrumentDOFetch(value.bind(target), state.id, doClass);\n }\n\n if (prop === 'alarm' && typeof value === 'function') {\n return instrumentDOAlarm(value.bind(target), state.id, doClass);\n }\n\n // Bind other methods to the target\n if (typeof value === 'function') {\n return value.bind(target);\n }\n\n return value;\n },\n };\n\n return wrap(doInstance, instanceHandler);\n}\n\n/**\n * Instrument a Durable Object class\n *\n * This wraps the DO class to automatically trace all fetch and alarm calls,\n * as well as initialize the telemetry configuration.\n *\n * **Usage:**\n * ```typescript\n * import { DurableObject } from 'cloudflare:workers'\n * import { instrumentDO } from 'autotel-edge'\n *\n * export class Counter extends DurableObject<Env> {\n * async fetch(request: Request) {\n * // Your DO logic here\n * return new Response('OK')\n * }\n * }\n *\n * // Wrap the class before exporting\n * export const CounterDO = instrumentDO(Counter, (env: Env) => ({\n * exporter: {\n * url: env.OTLP_ENDPOINT,\n * headers: { 'x-api-key': env.API_KEY }\n * },\n * service: {\n * name: 'my-durable-object',\n * version: '1.0.0'\n * }\n * }))\n * ```\n *\n * **What you get:**\n * - 🎯 Automatic spans for fetch() calls with HTTP attributes\n * - ⏰ Automatic spans for alarm() calls\n * - 🥶 Cold start tracking\n * - 🔗 Context propagation from incoming requests\n * - ⚡ Automatic span lifecycle management\n *\n * @param doClass - The Durable Object class to instrument\n * @param config - Configuration or configuration function\n * @returns Instrumented Durable Object class\n */\nexport function instrumentDO<C extends new (state: DurableObjectState, env: any) => any>(\n doClass: C,\n config: ConfigurationOption,\n): C {\n const initialiser = createInitialiser(config);\n\n const classHandler: ProxyHandler<C> = {\n construct(target, [state, env]: [DurableObjectState, any]) {\n // Initialize config for this DO instance\n const trigger = {\n id: state.id.toString(),\n name: state.id.name,\n };\n const doConfig = initialiser(env, trigger);\n const context = setConfig(doConfig);\n\n // Create the DO instance within the config context\n const doInstance = api_context.with(context, () => {\n return new target(state, env);\n });\n\n // Instrument the instance\n return instrumentDOInstance(doInstance, state, env, doClass);\n },\n };\n\n return wrap(doClass, classHandler);\n}\n","/**\n * Cloudflare Workflows instrumentation for autotel-edge\n *\n * Instruments WorkflowEntrypoint classes to automatically trace workflow execution,\n * step operations, retries, and sleeps.\n *\n * Based on Cloudflare Workflows API:\n * https://developers.cloudflare.com/workflows/\n */\n\nimport {\n trace,\n context as api_context,\n SpanStatusCode,\n SpanKind,\n} from '@opentelemetry/api';\nimport type { ConfigurationOption } from 'autotel-edge';\nimport { createInitialiser, setConfig, WorkerTracer } from 'autotel-edge';\nimport { wrap } from '../bindings/common';\n\n// Workflow types (these would come from @cloudflare/workers-types when available)\ntype WorkflowEvent = any;\ntype WorkflowStep = any;\n\ntype WorkflowRunFn = (\n event: WorkflowEvent,\n step: WorkflowStep,\n) => Promise<void> | void;\n\n/**\n * Track cold starts per Workflow class\n */\nconst coldStarts = new WeakMap<any, boolean>();\n\nfunction isColdStart(workflowClass: any): boolean {\n if (!coldStarts.has(workflowClass)) {\n coldStarts.set(workflowClass, true);\n return true;\n }\n return false;\n}\n\n/**\n * Proxy the step object to instrument step.do() calls\n */\nfunction instrumentWorkflowStep(\n step: WorkflowStep,\n workflowName: string,\n): WorkflowStep {\n const stepHandler: ProxyHandler<WorkflowStep> = {\n get(target, prop) {\n const value = Reflect.get(target, prop);\n\n // Instrument step.do() to create spans for each workflow step\n if (prop === 'do' && typeof value === 'function') {\n return new Proxy(value, {\n apply: (fnTarget, thisArg, args) => {\n const [stepName] = args as [string, () => Promise<any>];\n\n const tracer = trace.getTracer('autotel-edge') as WorkerTracer;\n\n return tracer.startActiveSpan(\n `Workflow ${workflowName}: ${stepName}`,\n {\n kind: SpanKind.INTERNAL,\n attributes: {\n 'workflow.step.name': stepName,\n 'workflow.name': workflowName,\n },\n },\n async (span) => {\n try {\n const result = await Reflect.apply(fnTarget, thisArg, args);\n span.setStatus({ code: SpanStatusCode.OK });\n return result;\n } catch (error) {\n span.recordException(error as Error);\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message:\n error instanceof Error ? error.message : String(error),\n });\n throw error;\n } finally {\n span.end();\n }\n },\n );\n },\n });\n }\n\n // Instrument step.sleep() to track workflow delays\n if (prop === 'sleep' && typeof value === 'function') {\n return new Proxy(value, {\n apply: (fnTarget, thisArg, args) => {\n const [sleepName, duration] = args as [string, string | number];\n\n const tracer = trace.getTracer('autotel-edge') as WorkerTracer;\n\n return tracer.startActiveSpan(\n `Workflow ${workflowName}: sleep ${sleepName}`,\n {\n kind: SpanKind.INTERNAL,\n attributes: {\n 'workflow.sleep.name': sleepName,\n 'workflow.sleep.duration': String(duration),\n 'workflow.name': workflowName,\n },\n },\n async (span) => {\n try {\n const result = await Reflect.apply(fnTarget, thisArg, args);\n span.setStatus({ code: SpanStatusCode.OK });\n return result;\n } catch (error) {\n span.recordException(error as Error);\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message:\n error instanceof Error ? error.message : String(error),\n });\n throw error;\n } finally {\n span.end();\n }\n },\n );\n },\n });\n }\n\n // Pass through other step methods\n if (typeof value === 'function') {\n return value.bind(target);\n }\n\n return value;\n },\n };\n\n return wrap(step, stepHandler);\n}\n\n/**\n * Instrument a Workflow run method\n */\nfunction instrumentWorkflowRun(\n runFn: WorkflowRunFn,\n workflowName: string,\n workflowClass: any,\n): WorkflowRunFn {\n return async function instrumentedRun(\n this: any,\n event: WorkflowEvent,\n step: WorkflowStep,\n ): Promise<void> {\n const tracer = trace.getTracer('autotel-edge') as WorkerTracer;\n\n // Instrument the step object to track individual operations\n const instrumentedStep = instrumentWorkflowStep(step, workflowName);\n\n const spanName = `Workflow ${workflowName}: run`;\n\n return tracer.startActiveSpan(\n spanName,\n {\n kind: SpanKind.INTERNAL,\n attributes: {\n 'workflow.name': workflowName,\n 'faas.trigger': 'workflow',\n 'faas.coldstart': isColdStart(workflowClass),\n // Add workflow event attributes if available\n ...(event?.workflowId && { 'workflow.id': event.workflowId }),\n ...(event?.runId && { 'workflow.run_id': event.runId }),\n },\n },\n async (span) => {\n try {\n await runFn.call(this, event, instrumentedStep);\n span.setStatus({ code: SpanStatusCode.OK });\n } catch (error) {\n span.recordException(error as Error);\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: error instanceof Error ? error.message : String(error),\n });\n throw error;\n } finally {\n span.end();\n }\n },\n );\n };\n}\n\n/**\n * Instrument a Workflow instance\n */\nfunction instrumentWorkflowInstance(\n workflowInstance: any,\n workflowName: string,\n workflowClass: any,\n): any {\n const instanceHandler: ProxyHandler<any> = {\n get(target, prop) {\n const value = Reflect.get(target, prop);\n\n if (prop === 'run' && typeof value === 'function') {\n return instrumentWorkflowRun(\n value.bind(target),\n workflowName,\n workflowClass,\n );\n }\n\n // Bind other methods to the target\n if (typeof value === 'function') {\n return value.bind(target);\n }\n\n return value;\n },\n };\n\n return wrap(workflowInstance, instanceHandler);\n}\n\n/**\n * Instrument a Cloudflare Workflow class\n *\n * This wraps the WorkflowEntrypoint class to automatically trace workflow execution,\n * step operations, retries, and sleeps.\n *\n * **Usage:**\n * ```typescript\n * import { WorkflowEntrypoint } from 'cloudflare:workers'\n * import { instrumentWorkflow } from 'autotel-edge'\n *\n * export class CheckoutWorkflow extends WorkflowEntrypoint {\n * async run(event, step) {\n * await step.do('submit payment', async () => {\n * return await submitToPaymentProcessor(event.params.payment)\n * })\n *\n * await step.sleep('wait for feedback', '2 days')\n *\n * await step.do('send feedback email', sendFeedbackEmail)\n * }\n * }\n *\n * // Wrap the class before exporting\n * export const CheckoutWorkflowInstrumented = instrumentWorkflow(\n * CheckoutWorkflow,\n * 'checkout-workflow',\n * (env: Env) => ({\n * exporter: {\n * url: env.OTLP_ENDPOINT,\n * headers: { 'x-api-key': env.API_KEY }\n * },\n * service: {\n * name: 'checkout-workflow',\n * version: '1.0.0'\n * }\n * })\n * )\n * ```\n *\n * **What you get:**\n * - 🎯 Automatic spans for workflow.run() execution\n * - 📋 Automatic spans for each step.do() operation\n * - ⏸️ Automatic spans for step.sleep() operations\n * - 🔄 Automatic retry tracking (via step.do retries)\n * - 🥶 Cold start tracking\n * - ⚡ Automatic span lifecycle management\n *\n * @param workflowClass - The WorkflowEntrypoint class to instrument\n * @param workflowName - The name of the workflow (used in span names)\n * @param config - Configuration or configuration function\n * @returns Instrumented Workflow class\n */\nexport function instrumentWorkflow<\n C extends new (...args: any[]) => any,\n>(\n workflowClass: C,\n workflowName: string,\n config: ConfigurationOption,\n): C {\n const initialiser = createInitialiser(config);\n\n const classHandler: ProxyHandler<C> = {\n construct(target, args: any[]) {\n // Extract env from constructor args (typically last arg)\n const env = args[args.length - 1] || {};\n\n // Initialize config for this workflow instance\n // Use Request as trigger type since workflows don't have a standard Trigger type yet\n const trigger = new Request('https://workflow.local/run');\n const workflowConfig = initialiser(env, trigger);\n const context = setConfig(workflowConfig);\n\n // Create the workflow instance within the config context\n const workflowInstance = api_context.with(context, () => {\n return new target(...args);\n });\n\n // Instrument the instance\n return instrumentWorkflowInstance(\n workflowInstance,\n workflowName,\n workflowClass,\n );\n },\n };\n\n return wrap(workflowClass, classHandler);\n}\n\n"]}
|