autotel-cloudflare
Version:
The #1 OpenTelemetry package for Cloudflare Workers - complete bindings coverage, native CF OTel integration, advanced sampling
737 lines (729 loc) • 26.3 kB
JavaScript
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";
import { a as getWorkflowLogger, i as getRequestLogger, n as getActorLogger, r as getQueueLogger, t as createWorkersLogger } from "./execution-logger-6IZS5fEz.js";
import { n as isNativeTracingAvailable, t as getNativeTracerFromCtx } from "./native-tracing-D0EyAtsF.js";
import { a as unwrap, o as wrap, r as proxyExecutionContext, t as toException } from "./exception-D3xOCdBW.js";
import { t as workerTracer } from "./tracer-z8zwRBVS.js";
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-BzekkOXx.js";
import { n as instrumentDO, t as instrumentWorkflow } from "./handlers-avm65CVU.js";
import { WorkerTracer, WorkerTracerProvider, createInitialiser, ensureGlobalContextManager, getActiveConfig, getServiceForPath, setConfig, shouldInstrumentPath, withNativeTracer } from "autotel-edge";
import { SpanKind, SpanStatusCode, context, propagation, trace } from "@opentelemetry/api";
import { resourceFromAttributes } from "@opentelemetry/resources";
export * from "autotel-edge"
//#region src/global/fetch.ts
/**
* Global fetch() instrumentation for autotel-edge
*
* Automatically traces all outgoing fetch() calls with:
* - HTTP method, URL, status code
* - Request/response headers
* - Automatic context propagation
* - Error tracking
*/
/**
* Gather HTTP request attributes following OpenTelemetry semantic conventions
*/
function gatherRequestAttributes(request) {
const url = new URL(request.url);
const redactQuery = getActiveConfig()?.dataSafety?.redactQueryParams === true;
return {
"http.request.method": request.method.toUpperCase(),
"url.full": redactQuery ? `${url.origin}${url.pathname}` : request.url,
"url.scheme": url.protocol.replace(":", ""),
"server.address": url.host,
"url.path": url.pathname,
"url.query": redactQuery ? url.search ? "[REDACTED]" : "" : url.search,
"network.protocol.name": "http",
"user_agent.original": request.headers.get("user-agent") || void 0
};
}
/**
* Gather HTTP response attributes
*/
function gatherResponseAttributes(response) {
return {
"http.response.status_code": response.status,
"http.response.body.size": response.headers.get("content-length") || void 0
};
}
/**
* Instrument the global fetch function
*
* This wraps globalThis.fetch to automatically create spans for all outgoing HTTP requests.
*
* **Note:** This is called automatically when the library is initialized with
* `instrumentation.instrumentGlobalFetch: true` (default).
*/
function instrumentGlobalFetch() {
const originalFetch = globalThis.fetch;
globalThis.fetch = function fetch(input, init) {
const request = new Request(input, init);
if (!request.url.startsWith("http")) return originalFetch(input, init);
const config = getActiveConfig();
if (!config) return originalFetch(input, init);
const tracer = workerTracer("autotel-edge");
const url = new URL(request.url);
const spanName = `${request.method} ${url.host}`;
return tracer.startActiveSpan(spanName, {
kind: SpanKind.CLIENT,
attributes: gatherRequestAttributes(request)
}, async (span) => {
try {
if (typeof config.fetch?.includeTraceContext === "function" ? config.fetch.includeTraceContext(request) : config.fetch?.includeTraceContext ?? true) propagation.inject(context.active(), request.headers, { set: (headers, key, value) => {
if (typeof value === "string") headers.set(key, value);
} });
const response = await originalFetch(request);
span.setAttributes(gatherResponseAttributes(response));
if (response.ok) span.setStatus({ code: SpanStatusCode.OK });
else span.setStatus({ code: SpanStatusCode.ERROR });
return response;
} catch (error) {
span.recordException(toException(error));
span.setStatus({
code: SpanStatusCode.ERROR,
message: error instanceof Error ? error.message : String(error)
});
throw error;
} finally {
span.end();
}
});
};
}
//#endregion
//#region src/global/cache.ts
/**
* Global Cache API instrumentation for Cloudflare Workers
*
* Automatically traces cache operations:
* - cache.match() - Read from cache
* - cache.put() - Write to cache
* - cache.delete() - Delete from cache
*/
/**
* Sanitize URL for span attributes (remove query params that might contain sensitive data)
*/
function sanitizeURL(url) {
const u = new URL(url);
return `${u.protocol}//${u.host}${u.pathname}`;
}
/**
* Instrument a cache method (match, put, delete)
*/
function instrumentCacheMethod(fn, cacheName, operation) {
return wrap(fn, { async apply(target, thisArg, argArray) {
const tracer = workerTracer("autotel-edge");
const firstArg = argArray[0];
const url = firstArg instanceof Request ? firstArg.url : typeof firstArg === "string" ? firstArg : void 0;
const spanName = `Cache ${cacheName}.${operation}`;
return tracer.startActiveSpan(spanName, {
kind: SpanKind.CLIENT,
attributes: {
"cache.name": cacheName,
"cache.operation": operation,
"cache.key": url ? sanitizeURL(url) : void 0
}
}, async (span) => {
try {
const result = await target.apply(thisArg, argArray);
if (operation === "match") span.setAttribute("cache.hit", !!result);
span.setStatus({ code: SpanStatusCode.OK });
return result;
} catch (error) {
span.recordException(toException(error));
span.setStatus({
code: SpanStatusCode.ERROR,
message: error instanceof Error ? error.message : String(error)
});
throw error;
} finally {
span.end();
}
});
} });
}
/**
* Instrument a Cache instance
*/
function instrumentCache(cache, cacheName) {
return wrap(cache, { get(target, prop) {
const value = member(target, prop);
const method = asFunction(value);
if ((prop === "match" || prop === "put" || prop === "delete") && method !== void 0) return instrumentCacheMethod(method.bind(target), cacheName, prop);
if (method !== void 0) return method.bind(target);
return value;
} });
}
/**
* Instrument caches.open()
*/
function instrumentCachesOpen(openFn) {
return wrap(openFn, { async apply(target, thisArg, argArray) {
const cacheName = argArray[0];
return instrumentCache(await applyTrap(target, thisArg, argArray), cacheName);
} });
}
/**
* Instrument the global caches API
*
* This wraps globalThis.caches to automatically create spans for all cache operations.
*
* **Note:** This is called automatically when the library is initialized with
* `instrumentation.instrumentGlobalCache: true` (default).
*/
function instrumentGlobalCache() {
globalThis.caches = wrap(caches, { get(target, prop) {
if (prop === "default") return instrumentCache(target.default, "default");
else if (prop === "open") {
const openFn = member(target, prop);
if (typeof openFn === "function") return instrumentCachesOpen(openFn.bind(target));
}
return member(target, prop);
} });
}
//#endregion
//#region src/wrappers/instrument.ts
/**
* Handler instrumentation for Cloudflare Workers
*
* Note: This file uses Cloudflare Workers types (ExportedHandler, Request, Response, etc.)
* which are globally available via @cloudflare/workers-types when listed in tsconfig.json.
* These types are devDependencies only - they're not runtime dependencies.
* At runtime, Cloudflare Workers runtime provides the actual implementations.
*
* Provides automatic OpenTelemetry tracing for:
* - HTTP handlers (fetch)
* - Scheduled/cron handlers
* - Queue handlers (with message tracking)
* - Email handlers
* - Auto-instrumentation of Cloudflare bindings (KV, R2, D1, Service Bindings)
* - Global fetch and cache instrumentation
* - Post-processor support for span customization
* - Tail sampling support
* - Cold start tracking
*/
const headersGetter = {
get: (carrier, key) => carrier.get(key) ?? void 0,
keys: (carrier) => [...carrier.keys()]
};
/**
* Create fetch handler instrumentation with config support for postProcess
*/
/**
* Extract Cloudflare-specific attributes from a request
*/
function extractCfAttributes(request) {
const cf = asRecord(member(request, "cf"));
if (!cf) return {};
const attrs = {};
const set = (key, value) => {
const scalar = asString(value) ?? asNumber(value) ?? asBoolean(value);
if (scalar !== void 0) attrs[key] = scalar;
};
set("cloudflare.colo", cf.colo);
const ray = request.headers.get("cf-ray");
if (ray) attrs["cloudflare.ray_id"] = ray;
set("cloudflare.country", cf.country);
set("cloudflare.city", cf.city);
set("cloudflare.region", cf.region);
set("cloudflare.continent", cf.continent);
set("cloudflare.timezone", cf.timezone);
set("cloudflare.latitude", cf.latitude);
set("cloudflare.longitude", cf.longitude);
set("cloudflare.asn", cf.asn);
set("cloudflare.as_organization", cf.asOrganization);
set("cloudflare.http_protocol", cf.httpProtocol);
set("cloudflare.tls_version", cf.tlsVersion);
set("cloudflare.client_tcp_rtt", cf.clientTcpRtt);
return attrs;
}
function createFetchInstrumentation(config) {
return {
getInitialSpanInfo: (request) => {
const url = new URL(request.url);
const routeService = getServiceForPath(url.pathname, config.handlers.fetch.routes);
const cfAttrs = readProperty(config, "extractCfAttributes") === false ? {} : extractCfAttributes(request);
return {
name: `${request.method} ${url.pathname}`,
options: {
kind: SpanKind.SERVER,
attributes: {
"http.request.method": request.method,
"url.full": request.url,
...routeService ? {
"service.name": routeService,
"autotel.route.service": routeService
} : {},
...cfAttrs
}
},
context: propagation.extract(context.active(), request.headers, headersGetter)
};
},
getAttributesFromResult: (response) => ({ "http.response.status_code": response.status }),
executionSucces: (span, trigger, result) => {
if (result.status >= 500) span.setStatus({ code: SpanStatusCode.ERROR });
if (config.handlers.fetch.postProcess) {
const readableSpan = span;
config.handlers.fetch.postProcess(span, {
request: trigger,
response: result,
readable: readableSpan
});
}
}
};
}
/**
* Scheduled handler instrumentation
*/
const scheduledInstrumentation = { getInitialSpanInfo: (event) => {
return {
name: `scheduledHandler ${event.cron || "unknown"}`,
options: {
kind: SpanKind.INTERNAL,
attributes: {
"faas.trigger": "timer",
"faas.cron": event.cron || "unknown",
"faas.scheduled_time": new Date(event.scheduledTime).toISOString()
}
}
};
} };
/**
* Tracks message status counts for queue processing
*/
var MessageStatusCount = class {
succeeded = 0;
failed = 0;
implicitly_acked = 0;
implicitly_retried = 0;
total;
constructor(total) {
this.total = total;
}
ack() {
this.succeeded = this.succeeded + 1;
}
ackRemaining() {
this.implicitly_acked = this.total - this.succeeded - this.failed;
this.succeeded = this.total - this.failed;
}
retry() {
this.failed = this.failed + 1;
}
retryRemaining() {
this.implicitly_retried = this.total - this.succeeded - this.failed;
this.failed = this.total - this.succeeded;
}
toAttributes() {
return {
"queue.messages_count": this.total,
"queue.messages_success": this.succeeded,
"queue.messages_failed": this.failed,
"queue.batch_success": this.succeeded === this.total,
"queue.implicitly_acked": this.implicitly_acked,
"queue.implicitly_retried": this.implicitly_retried
};
}
};
/**
* Add event to active span
*/
function addQueueEvent(name, msg, delaySeconds) {
const attrs = {};
if (msg) {
attrs["queue.message_id"] = msg.id;
attrs["queue.message_timestamp"] = msg.timestamp.toISOString();
if ("attempts" in msg && typeof msg.attempts === "number") attrs["queue.message_attempts"] = msg.attempts;
}
if (delaySeconds !== void 0) attrs["queue.retry_delay_seconds"] = delaySeconds;
trace.getActiveSpan()?.addEvent(name, attrs);
}
/**
* Proxy a queue message to track ack/retry operations
*/
function proxyQueueMessage(msg, count) {
return wrap(msg, { get: (target, prop) => {
const messageFn = asFunction(member(target, prop));
if (prop === "ack" && messageFn) return new Proxy(messageFn, { apply: (fnTarget) => {
addQueueEvent("messageAck", msg);
count.ack();
fnTarget.apply(msg, []);
} });
else if (prop === "retry" && messageFn) return new Proxy(messageFn, { apply: (fnTarget, _thisArg, args) => {
const retryOptions = trapArgs(args)[0];
const delaySeconds = retryOptions?.delaySeconds;
addQueueEvent("messageRetry", msg, delaySeconds);
if (retryOptions?.contentType) {
const span = trace.getActiveSpan();
if (span) span.setAttribute("queue.message.content_type", retryOptions.contentType);
}
count.retry();
return applyTrap(fnTarget, msg, args);
} });
else return member(target, prop);
} });
}
/**
* Proxy MessageBatch to track ackAll/retryAll operations
*/
function proxyMessageBatch(batch, count) {
return wrap(batch, { get: (target, prop) => {
if (prop === "messages") return wrap(target.messages, { get: (target, prop) => {
const index = asString(prop);
if (index !== void 0 && !Number.isNaN(Number.parseInt(index))) return proxyQueueMessage(member(target, prop), count);
return member(target, prop);
} });
const batchFn = asFunction(member(target, prop));
if (prop === "ackAll" && batchFn) return new Proxy(batchFn, { apply: (fnTarget) => {
addQueueEvent("ackAll");
count.ackRemaining();
fnTarget.apply(batch, []);
} });
else if (prop === "retryAll" && batchFn) return new Proxy(batchFn, { apply: (fnTarget, _thisArg, args) => {
const delaySeconds = trapArgs(args)[0]?.delaySeconds;
addQueueEvent("retryAll", void 0, delaySeconds);
count.retryRemaining();
applyTrap(fnTarget, batch, args);
} });
return member(target, prop);
} });
}
/**
* Queue handler instrumentation with message tracking
*/
var QueueInstrumentation = class {
count;
getInitialSpanInfo(batch) {
return {
name: `queueHandler ${batch.queue || "unknown"}`,
options: {
kind: SpanKind.CONSUMER,
attributes: {
"faas.trigger": "pubsub",
"queue.name": batch.queue || "unknown"
}
}
};
}
instrumentTrigger(batch) {
this.count = new MessageStatusCount(batch.messages.length);
return proxyMessageBatch(batch, this.count);
}
executionSucces(span, _trigger, _result) {
if (this.count) {
this.count.ackRemaining();
span.setAttributes(this.count.toAttributes());
}
}
executionFailed(span, _trigger, _error) {
if (this.count) {
this.count.retryRemaining();
span.setAttributes(this.count.toAttributes());
}
}
};
/**
* Converts email headers into OpenTelemetry attributes.
* When dataSafety.emailHeaderAllowlist is configured, only allowed headers are captured.
*/
function headerAttributes(message) {
const attrs = {};
if (message.headers instanceof Headers) {
const allowlist = getActiveConfig()?.dataSafety?.emailHeaderAllowlist;
for (const [key, value] of message.headers.entries()) {
if (allowlist && !allowlist.includes(key.toLowerCase())) continue;
attrs[`email.header.${key}`] = value;
}
}
return attrs;
}
/**
* Email handler instrumentation
*/
const emailInstrumentation = { getInitialSpanInfo: (message) => {
const attributes = {
"faas.trigger": "other",
"messaging.destination.name": message.to || "unknown"
};
if ("headers" in message && message.headers instanceof Headers) {
const messageId = message.headers.get("Message-Id");
if (messageId) attributes["rpc.message.id"] = messageId;
Object.assign(attributes, headerAttributes(message));
}
return {
name: `emailHandler ${message.to || "unknown"}`,
options: {
kind: SpanKind.CONSUMER,
attributes
}
};
} };
/**
* Export spans after request completes
*/
async function exportSpans(traceId, tracker, ctx) {
const tracer = trace.getTracer("autotel-edge");
if (tracer instanceof WorkerTracer) try {
const ctxWithScheduler = ctx;
if (ctxWithScheduler.scheduler) await ctxWithScheduler.scheduler.wait(1);
await tracker?.wait();
await tracer.forceFlush(traceId);
} catch (error) {
console.error("[autotel-edge] Failed to export spans:", error);
}
}
/**
* Create handler flow with instrumentation
*/
function createHandlerFlow(instrumentation) {
return (handlerFn, [trigger, env, context$1]) => {
const { ctx: proxiedCtx, tracker } = proxyExecutionContext(context$1);
const tracer = workerTracer("autotel-edge");
const { name, options, context: spanContext } = instrumentation.getInitialSpanInfo(trigger);
if (options.attributes) options.attributes["faas.coldstart"] = coldStart;
else options.attributes = { "faas.coldstart": coldStart };
coldStart = false;
const parentContext = spanContext || context.active();
const instrumentedTrigger = instrumentation.instrumentTrigger ? instrumentation.instrumentTrigger(trigger) : trigger;
return tracer.startActiveSpan(name, options, parentContext, async (span) => {
try {
const result = await handlerFn(instrumentedTrigger, env, proxiedCtx);
if (instrumentation.getAttributesFromResult) {
const attributes = instrumentation.getAttributesFromResult(result);
span.setAttributes(attributes);
}
span.setStatus({ code: SpanStatusCode.OK });
if (instrumentation.executionSucces) instrumentation.executionSucces(span, trigger, result);
return result;
} catch (error) {
span.recordException(toException(error));
span.setStatus({
code: SpanStatusCode.ERROR,
message: error instanceof Error ? error.message : String(error)
});
if (instrumentation.executionFailed) instrumentation.executionFailed(span, trigger, error);
throw error;
} finally {
span.end();
context$1.waitUntil(exportSpans(span.spanContext().traceId, tracker, context$1));
}
});
};
}
let warnedMissingNative = false;
/**
* Resolve whether this invocation should use Cloudflare's native tracer.
*
* - `nativeTracing: 'off'` → never use native (always autotel's OTLP pipeline).
* - `nativeTracing: 'on'` → require native; warn once and fall back if absent.
* - `nativeTracing: 'auto'` (default) → use native when `ctx.tracing` exists.
*/
function resolveNativeTracer(config, ctx, trigger) {
const mode = config.nativeTracing ?? "auto";
if (mode === "off") return null;
const nativeTracer = getNativeTracerFromCtx(ctx, (trigger instanceof Request ? trigger.headers.get("cf-ray") : null) ?? crypto.randomUUID());
if (!nativeTracer && mode === "on" && !warnedMissingNative) {
warnedMissingNative = true;
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.");
}
return nativeTracer;
}
/**
* Run a handler in native-tracing mode.
*
* Cloudflare already creates the root handler span and instruments platform
* operations (fetch / KV / R2 / D1 / DO), so autotel defers entirely: no
* binding proxies (avoids duplicate spans), no provider/exporter, no manual
* flush. We install the native tracer into the active context so that the
* user's `span()` / `trace()` / `enterSpan()` calls nest inside Cloudflare's
* native waterfall and are exported by the platform.
*/
function runWithNativeTracing(handlerFn, trigger, env, ctx, config, nativeTracer) {
ensureGlobalContextManager();
const nativeContext = withNativeTracer(nativeTracer, setConfig(config));
return context.with(nativeContext, () => handlerFn(trigger, env, ctx));
}
/**
* Create handler proxy
*/
function createHandlerProxy(_handler, handlerFn, initialiser, instrumentation) {
return (trigger, env, ctx) => {
const config = initialiser(env, trigger);
if (config.instrumentation.disabled) return handlerFn(trigger, env, ctx);
const nativeTracer = resolveNativeTracer(config, ctx, trigger);
if (nativeTracer) return runWithNativeTracing(handlerFn, trigger, env, ctx, config, nativeTracer);
const instrumentedEnv = instrumentBindings(env);
const configContext = setConfig(config);
initProvider(config);
const flowFn = createHandlerFlow(instrumentation);
return context.with(configContext, () => {
return flowFn(handlerFn, [
trigger,
instrumentedEnv,
ctx
]);
});
};
}
/**
* Create handler proxy with dynamic instrumentation (for fetch with postProcess)
*/
function createHandlerProxyWithConfig(_handler, handlerFn, initialiser, createInstrumentation) {
return (trigger, env, ctx) => {
const config = initialiser(env, trigger);
if (config.instrumentation.disabled) return handlerFn(trigger, env, ctx);
if (trigger instanceof Request) {
const pathname = new URL(trigger.url).pathname;
const fetchCfg = config.handlers.fetch;
if (!shouldInstrumentPath(pathname, {
include: fetchCfg.include,
exclude: fetchCfg.exclude
})) return handlerFn(trigger, env, ctx);
}
const nativeTracer = resolveNativeTracer(config, ctx, trigger);
if (nativeTracer) return runWithNativeTracing(handlerFn, trigger, env, ctx, config, nativeTracer);
const instrumentedEnv = instrumentBindings(env);
const configContext = setConfig(config);
initProvider(config);
const flowFn = createHandlerFlow(createInstrumentation(config));
return context.with(configContext, () => {
return flowFn(handlerFn, [
trigger,
instrumentedEnv,
ctx
]);
});
};
}
let providerInitialized = false;
let coldStart = true;
/**
* Initialize the tracer provider
*/
function initProvider(config) {
if (providerInitialized) return;
if (config.instrumentation.instrumentGlobalFetch) instrumentGlobalFetch();
if (config.instrumentation.instrumentGlobalCache) instrumentGlobalCache();
propagation.setGlobalPropagator(config.propagator);
const resource = resourceFromAttributes({
"service.name": config.service.name,
"service.version": config.service.version,
"service.namespace": config.service.namespace,
"cloud.provider": "cloudflare",
"cloud.platform": "cloudflare.workers",
"telemetry.sdk.name": "autotel-edge",
"telemetry.sdk.language": "js"
});
new WorkerTracerProvider(config.spanProcessors, resource).register();
workerTracer("autotel-edge").setHeadSampler(config.sampling.headSampler);
providerInitialized = true;
}
/**
* Instrument a Cloudflare Workers handler
*
* @example
* ```typescript
* import { instrument } from 'autotel-edge'
*
* const handler = {
* async fetch(request, env, ctx) {
* return new Response('Hello World')
* }
* }
*
* export default instrument(handler, {
* exporter: {
* url: env.OTLP_ENDPOINT,
* headers: { 'x-api-key': env.API_KEY }
* },
* service: { name: 'my-worker' }
* })
* ```
*/
function instrument(handler, config) {
const initialiser = createInitialiser(config);
if (handler.fetch) handler.fetch = createHandlerProxyWithConfig(handler, unwrap(handler.fetch), initialiser, createFetchInstrumentation);
if (handler.scheduled) handler.scheduled = createHandlerProxy(handler, unwrap(handler.scheduled), initialiser, scheduledInstrumentation);
if (handler.queue) handler.queue = createHandlerProxy(handler, unwrap(handler.queue), initialiser, new QueueInstrumentation());
if (handler.email) handler.email = createHandlerProxy(handler, unwrap(handler.email), initialiser, emailInstrumentation);
return handler;
}
//#endregion
//#region src/wrappers/wrap-module.ts
/**
* workers-honeycomb-logger style wrapper API
*
* @example
* ```typescript
* import { wrapModule } from 'autotel-cloudflare'
*
* const handler = {
* async fetch(req, env, ctx) {
* return new Response('Hello')
* }
* }
*
* export default wrapModule(
* { service: { name: 'my-worker' } },
* handler
* )
* ```
*/
/**
* Wrap a Cloudflare Workers module-style handler
* Alternative API style inspired by workers-honeycomb-logger
*
* @param config Configuration (can be static object or function)
* @param handler The worker handler to wrap
* @returns Instrumented handler
*/
function wrapModule(config, handler) {
return instrument(handler, config);
}
//#endregion
//#region src/wrappers/wrap-do.ts
/**
* Durable Object wrapper
*
* @example
* ```typescript
* import { wrapDurableObject } from 'autotel-cloudflare'
*
* class Counter implements DurableObject {
* async fetch(request: Request) {
* return new Response('count')
* }
* }
*
* export default wrapDurableObject({ service: { name: 'counter-do' } }, Counter)
* ```
*/
/**
* Wrap a Durable Object class with instrumentation
* Alternative API style inspired by workers-honeycomb-logger
*
* @param config Configuration (can be static object or function)
* @param doClass The Durable Object class to wrap
* @returns Instrumented Durable Object class
*/
function wrapDurableObject(config, doClass) {
return instrumentDO(doClass, config);
}
//#endregion
//#region src/wrappers/define-worker-fetch.ts
/**
* Wrap a Workers fetch handler so:
* - the handler is instrumented (spans, propagation, waitUntil export flush)
* - the handler receives a request-scoped logger as its fourth argument
*/
function defineWorkerFetch(config, handler, loggerOptions = {}) {
const wrapped = instrument({ fetch(request, env, ctx) {
return handler(request, env, ctx, createWorkersLogger(request, loggerOptions));
} }, config);
return { fetch(request, env, ctx) {
return Promise.resolve(wrapped.fetch(request, env, ctx));
} };
}
//#endregion
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 };