autotel-cloudflare 18.0.0 → 18.1.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.
@@ -1,7 +1,8 @@
1
1
  import { _ as trapArgs, f as member, r as asFunction } from "./values-BC6rdpGG.js";
2
+ import { r as platformNativeTracer } from "./native-tracing-DjyDA54H.js";
2
3
  import { o as wrap, t as toException } from "./exception-D3xOCdBW.js";
3
4
  import { t as workerTracer } from "./tracer-z8zwRBVS.js";
4
- import { createInitialiser, setConfig } from "autotel-edge";
5
+ import { createInitialiser, nativeRecordException, setConfig, withoutNativeTracer } from "autotel-edge";
5
6
  import { SpanKind, SpanStatusCode, context, propagation } from "@opentelemetry/api";
6
7
 
7
8
  //#region src/handlers/durable-objects.ts
@@ -24,13 +25,55 @@ function isColdStart$1(doClass) {
24
25
  }
25
26
  return false;
26
27
  }
28
+ /** `exception.type` of the alarm-loop signal. */
29
+ const RUNAWAY_ALARM_EXCEPTION = "autotel.RunawayAlarm";
30
+ const alarmRuns = /* @__PURE__ */ new Map();
31
+ /**
32
+ * Note one alarm run. Returns the `autotel.RunawayAlarm` exception when the
33
+ * object's alarm has run more than `maxRuns` times within `windowMs`, at most
34
+ * once per window.
35
+ */
36
+ function checkRunawayAlarm(id, config, now = Date.now()) {
37
+ if (config === false) return void 0;
38
+ const maxRuns = config?.maxRuns ?? 10;
39
+ const windowMs = config?.windowMs ?? 6e4;
40
+ const key = id.toString();
41
+ const entry = alarmRuns.get(key) ?? { times: [] };
42
+ alarmRuns.set(key, entry);
43
+ entry.times = entry.times.filter((t) => now - t < windowMs);
44
+ entry.times.push(now);
45
+ if (entry.times.length > maxRuns + 1) entry.times.shift();
46
+ if (entry.times.length <= maxRuns) return void 0;
47
+ if (entry.firedAt !== void 0 && now - entry.firedAt < windowMs) return;
48
+ entry.firedAt = now;
49
+ const error = /* @__PURE__ */ new Error(`Durable Object ${id.name || key} alarm ran ${entry.times.length} times in ${windowMs / 1e3}s`);
50
+ error.name = RUNAWAY_ALARM_EXCEPTION;
51
+ return error;
52
+ }
53
+ /**
54
+ * Native tracing mode: shadow the instance's `alarm` with a Proxy over the
55
+ * original, so the runaway check lands on Cloudflare's alarm span. The
56
+ * instance itself stays unproxied (private fields keep working).
57
+ */
58
+ function watchNativeAlarm(doInstance, id, config) {
59
+ const alarm = asFunction(member(doInstance, "alarm"));
60
+ if (!alarm || config === false) return;
61
+ doInstance.alarm = new Proxy(alarm, { apply(target, thisArg, args) {
62
+ const runaway = checkRunawayAlarm(id, config);
63
+ if (runaway) {
64
+ const span = platformNativeTracer?.getActiveSpan?.();
65
+ if (span) nativeRecordException(span, runaway);
66
+ }
67
+ return Reflect.apply(target, thisArg, args);
68
+ } });
69
+ }
27
70
  /**
28
71
  * Instrument a Durable Object fetch method
29
72
  */
30
73
  function instrumentDOFetch(fetchFn, id, doClass) {
31
74
  return async function instrumentedFetch(request) {
32
75
  const tracer = workerTracer("autotel-edge");
33
- const parentContext = propagation.extract(context.active(), request.headers);
76
+ const parentContext = propagation.extract(withoutNativeTracer(), request.headers);
34
77
  const url = new URL(request.url);
35
78
  const spanName = `DO ${id.name || id.toString()}: ${request.method} ${url.pathname}`;
36
79
  return tracer.startActiveSpan(spanName, {
@@ -66,7 +109,7 @@ function instrumentDOFetch(fetchFn, id, doClass) {
66
109
  /**
67
110
  * Instrument a Durable Object alarm method
68
111
  */
69
- function instrumentDOAlarm(alarmFn, id, doClass) {
112
+ function instrumentDOAlarm(alarmFn, id, doClass, runawayAlarm) {
70
113
  return async function instrumentedAlarm() {
71
114
  const tracer = workerTracer("autotel-edge");
72
115
  const spanName = `DO ${id.name || id.toString()}: alarm`;
@@ -78,7 +121,9 @@ function instrumentDOAlarm(alarmFn, id, doClass) {
78
121
  "faas.trigger": "timer",
79
122
  "faas.coldstart": isColdStart$1(doClass)
80
123
  }
81
- }, async (span) => {
124
+ }, withoutNativeTracer(), async (span) => {
125
+ const runaway = checkRunawayAlarm(id, runawayAlarm);
126
+ if (runaway) span.recordException(runaway);
82
127
  try {
83
128
  await alarmFn.call(this);
84
129
  span.setStatus({ code: SpanStatusCode.OK });
@@ -98,12 +143,12 @@ function instrumentDOAlarm(alarmFn, id, doClass) {
98
143
  /**
99
144
  * Instrument a Durable Object instance
100
145
  */
101
- function instrumentDOInstance(doInstance, state, _env, doClass) {
146
+ function instrumentDOInstance(doInstance, state, doClass, runawayAlarm) {
102
147
  return wrap(doInstance, { get(target, prop) {
103
148
  const value = member(target, prop);
104
149
  const method = asFunction(value);
105
150
  if (prop === "fetch" && method) return instrumentDOFetch(method.bind(target), state.id, doClass);
106
- if (prop === "alarm" && method) return instrumentDOAlarm(method.bind(target), state.id, doClass);
151
+ if (prop === "alarm" && method) return instrumentDOAlarm(method.bind(target), state.id, doClass, runawayAlarm);
107
152
  return method ? method.bind(target) : value;
108
153
  } });
109
154
  }
@@ -158,9 +203,14 @@ function instrumentDO(doClass, config) {
158
203
  };
159
204
  const doConfig = initialiser(env, trigger);
160
205
  const context$2 = setConfig(doConfig);
161
- return instrumentDOInstance(context.with(context$2, () => {
206
+ const doInstance = context.with(context$2, () => {
162
207
  return new target(state, env);
163
- }), state, env, doClass);
208
+ });
209
+ if (platformNativeTracer && doConfig.nativeTracing !== "off") {
210
+ watchNativeAlarm(doInstance, state.id, doConfig.runawayAlarm);
211
+ return doInstance;
212
+ }
213
+ return instrumentDOInstance(doInstance, state, doClass, doConfig.runawayAlarm);
164
214
  } });
165
215
  }
166
216
 
@@ -292,7 +342,7 @@ function instrumentWorkflowRun(runFn, workflowName, workflowClass) {
292
342
  "faas.trigger": "workflow",
293
343
  "faas.coldstart": isColdStart(workflowClass)
294
344
  }
295
- }, async (span) => {
345
+ }, withoutNativeTracer(), async (span) => {
296
346
  try {
297
347
  const result = await runFn.call(this, event, instrumentedStep);
298
348
  span.setStatus({ code: SpanStatusCode.OK });
@@ -311,6 +361,31 @@ function instrumentWorkflowRun(runFn, workflowName, workflowClass) {
311
361
  };
312
362
  }
313
363
  /**
364
+ * Native tracing: Cloudflare traces the run and step RPC calls but not step
365
+ * names, so wrap only `step.do()` in a named native span.
366
+ */
367
+ function nativeWorkflowInstance(workflowInstance, workflowName, tracer) {
368
+ return wrap(workflowInstance, { get(target, prop) {
369
+ const value = member(target, prop);
370
+ const method = asFunction(value);
371
+ if (prop !== "run" || !method) return method ? method.bind(target) : value;
372
+ return (event, step) => method.call(target, event, wrap(step, { get(stepTarget, stepProp) {
373
+ const stepValue = member(stepTarget, stepProp);
374
+ const stepMethod = asFunction(stepValue);
375
+ if (stepProp !== "do" || !stepMethod) return stepValue;
376
+ return (...args) => {
377
+ const [stepName] = trapArgs(args);
378
+ return tracer.enterSpan(`Workflow ${workflowName}: ${stepName}`, (span) => {
379
+ span.setAttribute("workflow.name", workflowName);
380
+ span.setAttribute("workflow.step.name", stepName);
381
+ span.setAttribute("workflow.instance_id", event.instanceId);
382
+ return Reflect.apply(stepMethod, stepTarget, args);
383
+ });
384
+ };
385
+ } }));
386
+ } });
387
+ }
388
+ /**
314
389
  * Instrument a Workflow instance
315
390
  */
316
391
  function instrumentWorkflowInstance(workflowInstance, workflowName, workflowClass) {
@@ -374,11 +449,13 @@ function instrumentWorkflow(workflowClass, workflowName, config) {
374
449
  name: workflowName
375
450
  });
376
451
  const context$1 = setConfig(workflowConfig);
377
- return instrumentWorkflowInstance(context.with(context$1, () => {
452
+ const workflowInstance = context.with(context$1, () => {
378
453
  return new target(...args);
379
- }), workflowName, workflowClass);
454
+ });
455
+ if (platformNativeTracer && workflowConfig.nativeTracing !== "off") return nativeWorkflowInstance(workflowInstance, workflowName, platformNativeTracer);
456
+ return instrumentWorkflowInstance(workflowInstance, workflowName, workflowClass);
380
457
  } });
381
458
  }
382
459
 
383
460
  //#endregion
384
- export { instrumentDO as n, instrumentWorkflow as t };
461
+ export { RUNAWAY_ALARM_EXCEPTION as n, instrumentDO as r, instrumentWorkflow as t };
@@ -1,2 +1,2 @@
1
- import { n as instrumentDO, t as instrumentWorkflow } from "./handlers-BRfAgfP7.js";
2
- export { instrumentDO, instrumentWorkflow };
1
+ import { n as RUNAWAY_ALARM_EXCEPTION, r as instrumentDO, t as instrumentWorkflow } from "./handlers-CEAD5hVy.js";
2
+ export { RUNAWAY_ALARM_EXCEPTION, instrumentDO, instrumentWorkflow };
package/dist/handlers.js CHANGED
@@ -1,3 +1,3 @@
1
- import { n as instrumentDO, t as instrumentWorkflow } from "./handlers-4X_6aEtg.js";
1
+ import { n as RUNAWAY_ALARM_EXCEPTION, r as instrumentDO, t as instrumentWorkflow } from "./handlers-a5qrd5nR.js";
2
2
 
3
- export { instrumentDO, instrumentWorkflow };
3
+ export { RUNAWAY_ALARM_EXCEPTION, instrumentDO, instrumentWorkflow };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { a as instrumentQueueProducer, c as instrumentAI, d as instrumentKV, f as instrumentR2, i as instrumentAnalyticsEngine, l as instrumentBindings, n as instrumentRateLimiter, o as instrumentHyperdrive, p as instrumentServiceBinding, r as instrumentImages, s as instrumentVectorize, t as instrumentBrowserRendering, u as instrumentD1 } from "./bindings-BcSPZDWF.js";
2
- import { n as instrumentDO, t as instrumentWorkflow } from "./handlers-BRfAgfP7.js";
2
+ import { n as RUNAWAY_ALARM_EXCEPTION, r as instrumentDO, t as instrumentWorkflow } from "./handlers-CEAD5hVy.js";
3
3
  import { a as createWorkersLogger, c as getRequestLogger, i as WorkersLoggerOptions, l as getWorkflowLogger, n as ExecutionLogger, o as getActorLogger, r as ExecutionLoggerOptions, s as getQueueLogger, t as ExecutionLogSnapshot } from "./execution-logger-CCn9HGYC.js";
4
4
  import { n as isNativeTracingAvailable, t as getNativeTracerFromCtx } from "./native-tracing-DFaGAoR7.js";
5
5
  import { ConfigurationOption } from "autotel-edge";
@@ -27,7 +27,7 @@ export * from "autotel-edge";
27
27
  * })
28
28
  * ```
29
29
  */
30
- declare function instrument<E, Q = any, C = any>(handler: ExportedHandler<E, Q, C>, config: ConfigurationOption): ExportedHandler<E, Q, C>;
30
+ export declare function instrument<E, Q = any, C = any>(handler: ExportedHandler<E, Q, C>, config: ConfigurationOption): ExportedHandler<E, Q, C>;
31
31
  //#endregion
32
32
  //#region src/wrappers/wrap-module.d.ts
33
33
  /**
@@ -38,7 +38,7 @@ declare function instrument<E, Q = any, C = any>(handler: ExportedHandler<E, Q,
38
38
  * @param handler The worker handler to wrap
39
39
  * @returns Instrumented handler
40
40
  */
41
- declare function wrapModule<E, Q = any, C = any>(config: ConfigurationOption, handler: ExportedHandler<E, Q, C>): ExportedHandler<E, Q, C>;
41
+ export declare function wrapModule<E, Q = any, C = any>(config: ConfigurationOption, handler: ExportedHandler<E, Q, C>): ExportedHandler<E, Q, C>;
42
42
  //#endregion
43
43
  //#region src/wrappers/wrap-do.d.ts
44
44
  /**
@@ -49,7 +49,7 @@ declare function wrapModule<E, Q = any, C = any>(config: ConfigurationOption, ha
49
49
  * @param doClass The Durable Object class to wrap
50
50
  * @returns Instrumented Durable Object class
51
51
  */
52
- declare function wrapDurableObject<T extends DurableObject>(config: ConfigurationOption, doClass: new (state: DurableObjectState, env: any) => T): new (state: DurableObjectState, env: any) => T;
52
+ export declare function wrapDurableObject<T extends DurableObject>(config: ConfigurationOption, doClass: new (state: DurableObjectState, env: any) => T): new (state: DurableObjectState, env: any) => T;
53
53
  //#endregion
54
54
  //#region src/wrappers/define-worker-fetch.d.ts
55
55
  type DefineWorkerFetchOptions = WorkersLoggerOptions;
@@ -59,7 +59,7 @@ type WorkerFetchHandler<E> = (request: Request, env: E, ctx: ExecutionContext, l
59
59
  * - the handler is instrumented (spans, propagation, waitUntil export flush)
60
60
  * - the handler receives a request-scoped logger as its fourth argument
61
61
  */
62
- declare function defineWorkerFetch<E = unknown>(config: ConfigurationOption, handler: WorkerFetchHandler<E>, loggerOptions?: DefineWorkerFetchOptions): {
62
+ export declare function defineWorkerFetch<E = unknown>(config: ConfigurationOption, handler: WorkerFetchHandler<E>, loggerOptions?: DefineWorkerFetchOptions): {
63
63
  fetch: (request: Request, env: E, ctx: ExecutionContext) => Promise<Response>;
64
64
  };
65
65
  //#endregion
@@ -81,7 +81,7 @@ declare function defineWorkerFetch<E = unknown>(config: ConfigurationOption, han
81
81
  * **Note:** This is called automatically when the library is initialized with
82
82
  * `instrumentation.instrumentGlobalFetch: true` (default).
83
83
  */
84
- declare function instrumentGlobalFetch(): void;
84
+ export declare function instrumentGlobalFetch(): void;
85
85
  //#endregion
86
86
  //#region src/global/cache.d.ts
87
87
  /**
@@ -100,6 +100,6 @@ declare function instrumentGlobalFetch(): void;
100
100
  * **Note:** This is called automatically when the library is initialized with
101
101
  * `instrumentation.instrumentGlobalCache: true` (default).
102
102
  */
103
- declare function instrumentGlobalCache(): void;
103
+ export declare function instrumentGlobalCache(): void;
104
104
  //#endregion
105
- export { type DefineWorkerFetchOptions, type ExecutionLogSnapshot, type ExecutionLogger, type ExecutionLoggerOptions, type WorkerFetchHandler, type WorkersLoggerOptions, createWorkersLogger, defineWorkerFetch, getActorLogger, getNativeTracerFromCtx, getQueueLogger, getRequestLogger, getWorkflowLogger, instrument, instrumentAI, instrumentAnalyticsEngine, instrumentBindings, instrumentBrowserRendering, instrumentD1, instrumentDO, instrumentGlobalCache, instrumentGlobalFetch, instrumentHyperdrive, instrumentImages, instrumentKV, instrumentQueueProducer, instrumentR2, instrumentRateLimiter, instrumentServiceBinding, instrumentVectorize, instrumentWorkflow, isNativeTracingAvailable, wrapDurableObject, wrapModule };
105
+ export { type DefineWorkerFetchOptions, type ExecutionLogSnapshot, type ExecutionLogger, type ExecutionLoggerOptions, RUNAWAY_ALARM_EXCEPTION, type WorkerFetchHandler, type WorkersLoggerOptions, createWorkersLogger, getActorLogger, getNativeTracerFromCtx, getQueueLogger, getRequestLogger, getWorkflowLogger, instrumentAI, instrumentAnalyticsEngine, instrumentBindings, instrumentBrowserRendering, instrumentD1, instrumentDO, instrumentHyperdrive, instrumentImages, instrumentKV, instrumentQueueProducer, instrumentR2, instrumentRateLimiter, instrumentServiceBinding, instrumentVectorize, instrumentWorkflow, isNativeTracingAvailable };
package/dist/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  import { _ as trapArgs, a as asRecord, c as asString, f as member, g as readProperty, i as asNumber, n as asBoolean, r as asFunction, t as applyTrap } from "./values-BC6rdpGG.js";
2
2
  import { a as getWorkflowLogger, i as getRequestLogger, n as getActorLogger, r as getQueueLogger, t as createWorkersLogger } from "./execution-logger-6IZS5fEz.js";
3
- import { n as isNativeTracingAvailable, t as getNativeTracerFromCtx } from "./native-tracing-D0EyAtsF.js";
3
+ import { n as isNativeTracingAvailable, t as getNativeTracerFromCtx } from "./native-tracing-DjyDA54H.js";
4
4
  import { a as unwrap, o as wrap, r as proxyExecutionContext, t as toException } from "./exception-D3xOCdBW.js";
5
5
  import { t as workerTracer } from "./tracer-z8zwRBVS.js";
6
6
  import { a as instrumentKV, c as instrumentImages, d as instrumentHyperdrive, f as instrumentVectorize, i as instrumentD1, l as instrumentAnalyticsEngine, n as instrumentRateLimiter, o as instrumentR2, p as instrumentAI, r as instrumentBindings, s as instrumentServiceBinding, t as instrumentBrowserRendering, u as instrumentQueueProducer } from "./bindings-BK2AmtNc.js";
7
- import { n as instrumentDO, t as instrumentWorkflow } from "./handlers-4X_6aEtg.js";
8
- import { WorkerTracer, WorkerTracerProvider, createInitialiser, ensureGlobalContextManager, getActiveConfig, getServiceForPath, setConfig, shouldInstrumentPath, withNativeTracer } from "autotel-edge";
7
+ import { n as RUNAWAY_ALARM_EXCEPTION, r as instrumentDO, t as instrumentWorkflow } from "./handlers-a5qrd5nR.js";
8
+ import { WorkerTracer, WorkerTracerProvider, createInitialiser, createNativeTraceContext, ensureGlobalContextManager, getActiveConfig, getServiceForPath, runWithNativeTraceContext, setConfig, shouldInstrumentPath, withNativeTracer, withoutNativeTracer } from "autotel-edge";
9
9
  import { SpanKind, SpanStatusCode, context, propagation, trace } from "@opentelemetry/api";
10
10
  import { resourceFromAttributes } from "@opentelemetry/resources";
11
11
 
@@ -516,6 +516,7 @@ function createHandlerFlow(instrumentation) {
516
516
  };
517
517
  }
518
518
  let warnedMissingNative = false;
519
+ let warnedLocalNative = false;
519
520
  /**
520
521
  * Resolve whether this invocation should use Cloudflare's native tracer.
521
522
  *
@@ -532,6 +533,10 @@ function resolveNativeTracer(config, ctx, trigger) {
532
533
  warnedMissingNative = true;
533
534
  console.warn("[autotel-cloudflare] nativeTracing is 'on' but Cloudflare native tracing was not detected. Enable observability.traces in your Wrangler config (and use a recent compatibility_date). Falling back to the autotel OTLP exporter.");
534
535
  }
536
+ if (nativeTracer && mode === "auto" && !warnedLocalNative && config.spanProcessors.length > 0 && trigger instanceof Request && /^(localhost|127\.0\.0\.1|\[::1\])$/.test(new URL(trigger.url).hostname)) {
537
+ warnedLocalNative = true;
538
+ console.warn("[autotel-cloudflare] Cloudflare native tracing is on in wrangler dev, so spans go to the Local Explorer (/cdn-cgi/explorer), not your exporter. To stream to autotel-devtools, set NATIVE_TRACING=off in .dev.vars (or nativeTracing: \"off\").");
539
+ }
535
540
  return nativeTracer;
536
541
  }
537
542
  /**
@@ -547,7 +552,9 @@ function resolveNativeTracer(config, ctx, trigger) {
547
552
  function runWithNativeTracing(handlerFn, trigger, env, ctx, config, nativeTracer) {
548
553
  ensureGlobalContextManager();
549
554
  const nativeContext = withNativeTracer(nativeTracer, setConfig(config));
550
- return context.with(nativeContext, () => handlerFn(trigger, env, ctx));
555
+ const run = () => handlerFn(trigger, env, ctx);
556
+ const root = nativeTracer.getActiveSpan?.();
557
+ return context.with(nativeContext, () => root ? runWithNativeTraceContext(createNativeTraceContext(root, "handler", nativeTracer.correlationId), run) : run());
551
558
  }
552
559
  /**
553
560
  * Create handler proxy
@@ -559,7 +566,7 @@ function createHandlerProxy(_handler, handlerFn, initialiser, instrumentation) {
559
566
  const nativeTracer = resolveNativeTracer(config, ctx, trigger);
560
567
  if (nativeTracer) return runWithNativeTracing(handlerFn, trigger, env, ctx, config, nativeTracer);
561
568
  const instrumentedEnv = instrumentBindings(env);
562
- const configContext = setConfig(config);
569
+ const configContext = withoutNativeTracer(setConfig(config));
563
570
  initProvider(config);
564
571
  const flowFn = createHandlerFlow(instrumentation);
565
572
  return context.with(configContext, () => {
@@ -589,7 +596,7 @@ function createHandlerProxyWithConfig(_handler, handlerFn, initialiser, createIn
589
596
  const nativeTracer = resolveNativeTracer(config, ctx, trigger);
590
597
  if (nativeTracer) return runWithNativeTracing(handlerFn, trigger, env, ctx, config, nativeTracer);
591
598
  const instrumentedEnv = instrumentBindings(env);
592
- const configContext = setConfig(config);
599
+ const configContext = withoutNativeTracer(setConfig(config));
593
600
  initProvider(config);
594
601
  const flowFn = createHandlerFlow(createInstrumentation(config));
595
602
  return context.with(configContext, () => {
@@ -735,4 +742,4 @@ function defineWorkerFetch(config, handler, loggerOptions = {}) {
735
742
  }
736
743
 
737
744
  //#endregion
738
- export { createWorkersLogger, defineWorkerFetch, getActorLogger, getNativeTracerFromCtx, getQueueLogger, getRequestLogger, getWorkflowLogger, instrument, instrumentAI, instrumentAnalyticsEngine, instrumentBindings, instrumentBrowserRendering, instrumentD1, instrumentDO, instrumentGlobalCache, instrumentGlobalFetch, instrumentHyperdrive, instrumentImages, instrumentKV, instrumentQueueProducer, instrumentR2, instrumentRateLimiter, instrumentServiceBinding, instrumentVectorize, instrumentWorkflow, isNativeTracingAvailable, wrapDurableObject, wrapModule };
745
+ export { RUNAWAY_ALARM_EXCEPTION, createWorkersLogger, defineWorkerFetch, getActorLogger, getNativeTracerFromCtx, getQueueLogger, getRequestLogger, getWorkflowLogger, instrument, instrumentAI, instrumentAnalyticsEngine, instrumentBindings, instrumentBrowserRendering, instrumentD1, instrumentDO, instrumentGlobalCache, instrumentGlobalFetch, instrumentHyperdrive, instrumentImages, instrumentKV, instrumentQueueProducer, instrumentR2, instrumentRateLimiter, instrumentServiceBinding, instrumentVectorize, instrumentWorkflow, isNativeTracingAvailable, wrapDurableObject, wrapModule };
@@ -0,0 +1,63 @@
1
+ import { setDefaultNativeTracer } from "autotel-edge";
2
+ import { tracing } from "cloudflare:workers";
3
+
4
+ //#region src/native/native-tracing.ts
5
+ /**
6
+ * Cloudflare native tracing adapter
7
+ *
8
+ * Bridges Cloudflare Workers' built-in custom-span API
9
+ * (`ctx.tracing.enterSpan()` / `import { tracing } from "cloudflare:workers"`)
10
+ * to autotel-edge's runtime-agnostic {@link NativeTracer} seam.
11
+ *
12
+ * When a Worker has tracing enabled (`observability.traces.enabled = true` in
13
+ * Wrangler) the runtime exposes `ctx.tracing`. The handler wrappers detect it,
14
+ * wrap it as a {@link NativeTracer}, and install it into the active context with
15
+ * `withNativeTracer()`. From then on every autotel `span()` / `trace()` /
16
+ * `enterSpan()` call — including deep inside utility functions and libraries —
17
+ * automatically routes to Cloudflare's native tracer and nests inside the
18
+ * platform's trace waterfall (fetch / KV / R2 / D1 / handler spans), exported
19
+ * by Cloudflare to whichever destination is configured in Wrangler.
20
+ *
21
+ * Code outside any wrapper (Durable Object RPC methods, entrypoints, helpers)
22
+ * falls back to the module-level `tracing` export of `cloudflare:workers`,
23
+ * registered below as autotel-edge's default native tracer.
24
+ */
25
+ function readTracing(carrier) {
26
+ const tracing = carrier?.tracing;
27
+ return typeof tracing?.enterSpan === "function" ? tracing : void 0;
28
+ }
29
+ /**
30
+ * Returns `true` when Cloudflare native custom-span tracing is available on the
31
+ * given ExecutionContext (i.e. tracing is enabled for this Worker).
32
+ */
33
+ function isNativeTracingAvailable(ctx) {
34
+ return readTracing(ctx) !== void 0;
35
+ }
36
+ /**
37
+ * Wrap Cloudflare's `ctx.tracing` as an autotel {@link NativeTracer}, or return
38
+ * `null` when native tracing is unavailable on this context.
39
+ *
40
+ * @param correlationId Optional per-request id (e.g. the `cf-ray` header)
41
+ * surfaced as `ctx.correlationId` and a `correlation.id` span attribute, so
42
+ * logs, custom spans, and the Cloudflare dashboard share one queryable key
43
+ * today — before Cloudflare exposes in-code trace/span ids.
44
+ */
45
+ function getNativeTracerFromCtx(ctx, correlationId) {
46
+ const tracing = readTracing(ctx);
47
+ if (!tracing) return null;
48
+ return {
49
+ correlationId,
50
+ enterSpan: (name, callback) => tracing.enterSpan(name, callback),
51
+ getActiveSpan: () => tracing.getActiveSpan?.()
52
+ };
53
+ }
54
+ /**
55
+ * The isolate-wide native tracer from `cloudflare:workers`, or `null` when the
56
+ * runtime has no tracing API. Registered as the default so `trace()`/`span()`
57
+ * nest in the platform waterfall even where no wrapper installed one.
58
+ */
59
+ const platformNativeTracer = getNativeTracerFromCtx({ tracing });
60
+ setDefaultNativeTracer(platformNativeTracer);
61
+
62
+ //#endregion
63
+ export { isNativeTracingAvailable as n, platformNativeTracer as r, getNativeTracerFromCtx as t };
package/dist/native.js CHANGED
@@ -1,3 +1,3 @@
1
- import { n as isNativeTracingAvailable, t as getNativeTracerFromCtx } from "./native-tracing-D0EyAtsF.js";
1
+ import { n as isNativeTracingAvailable, t as getNativeTracerFromCtx } from "./native-tracing-DjyDA54H.js";
2
2
 
3
3
  export { getNativeTracerFromCtx, isNativeTracingAvailable };
package/dist/tail.d.ts ADDED
@@ -0,0 +1,110 @@
1
+ //#region src/tail.d.ts
2
+ /**
3
+ * `autotel-cloudflare/tail`: observability for Workers with no SDK in them.
4
+ *
5
+ * A Tail Worker receives every invocation of the Workers that list it in
6
+ * `tail_consumers`: outcome, uncaught exceptions, console output (with the
7
+ * exception behind each `console.error` arg), the fetch request and response
8
+ * status, the version that ran. This turns each invocation into one OTLP span
9
+ * and its console output into OTLP logs, and sends them to any OTLP endpoint
10
+ * (autotel-devtools, the autotel-mcp collector, a vendor). Issues, grouping,
11
+ * source maps and automations then work exactly as for an instrumented app.
12
+ *
13
+ * ```ts
14
+ * // tail-worker/src/index.ts
15
+ * import { createTailHandler } from 'autotel-cloudflare/tail';
16
+ * export default {
17
+ * tail: createTailHandler({ endpoint: (env) => env.OTLP_ENDPOINT }),
18
+ * };
19
+ * ```
20
+ * ```jsonc
21
+ * // the observed Worker's wrangler.jsonc: no code change
22
+ * { "tail_consumers": [{ "service": "my-tail-worker" }] }
23
+ * ```
24
+ *
25
+ * Dependency-free on purpose: a tail Worker runs for every invocation of
26
+ * everything it watches, so it should be as small as its job.
27
+ */
28
+ /** The subset of Cloudflare's `TraceItem` this reads. */
29
+ export interface TailItem {
30
+ event?: {
31
+ request?: {
32
+ method: string;
33
+ url: string;
34
+ headers?: Record<string, string>;
35
+ };
36
+ response?: {
37
+ status: number;
38
+ };
39
+ cron?: string;
40
+ scheduledTime?: number | Date;
41
+ queue?: string;
42
+ batchSize?: number;
43
+ rpcMethod?: string;
44
+ } | null;
45
+ eventTimestamp: number | null;
46
+ logs: Array<{
47
+ timestamp: number;
48
+ level: string;
49
+ message: unknown;
50
+ errorInfo?: Array<{
51
+ name: string;
52
+ message: string;
53
+ stack?: string;
54
+ } | null>;
55
+ }>;
56
+ exceptions: Array<{
57
+ timestamp: number;
58
+ name: string;
59
+ message: string;
60
+ stack?: string;
61
+ }>;
62
+ scriptName: string | null;
63
+ entrypoint?: string;
64
+ scriptVersion?: {
65
+ id?: string;
66
+ tag?: string;
67
+ };
68
+ tailAttributes?: Record<string, string | number | boolean>;
69
+ durableObjectId?: string;
70
+ outcome: string;
71
+ truncated?: boolean;
72
+ cpuTime?: number;
73
+ wallTime?: number;
74
+ }
75
+ export interface TailOptions {
76
+ /**
77
+ * `service.name` when the event has no `scriptName`, as in `wrangler dev`.
78
+ * In production every event names the Worker that produced it.
79
+ */
80
+ serviceName?: string;
81
+ /** Repeats of one log template per invocation before `autotel.LogFlood`. `0` disables. Default 100. */
82
+ logFloodThreshold?: number;
83
+ /** Alarm runs per Durable Object within `windowMs` before `autotel.RunawayAlarm`. `false` disables. */
84
+ runawayAlarm?: {
85
+ maxRuns: number;
86
+ windowMs: number;
87
+ } | false;
88
+ }
89
+ /** Collapse ids and numbers so one loop's lines share a template. */
90
+ export declare function logTemplate(message: unknown): string;
91
+ export interface OtlpPayloads {
92
+ traces: {
93
+ resourceSpans: unknown[];
94
+ };
95
+ logs: {
96
+ resourceLogs: unknown[];
97
+ };
98
+ }
99
+ /** Pure conversion: Cloudflare tail events → OTLP/JSON traces and logs. */
100
+ export declare function tailItemsToOtlp(items: TailItem[], options?: TailOptions, now?: number): OtlpPayloads;
101
+ export interface TailHandlerOptions<Env> extends TailOptions {
102
+ /** OTLP/HTTP base URL, e.g. `https://collector.example` (posts to `/v1/traces` and `/v1/logs`). */
103
+ endpoint: string | ((env: Env) => string | undefined);
104
+ headers?: Record<string, string> | ((env: Env) => Record<string, string> | undefined);
105
+ }
106
+ /** A Worker `tail` handler that ships every observed invocation as OTLP. */
107
+ export declare function createTailHandler<Env = Record<string, unknown>>(options: TailHandlerOptions<Env>): (events: TailItem[], env: Env, ctx: {
108
+ waitUntil(promise: Promise<unknown>): void;
109
+ }) => Promise<void>;
110
+ //#endregion