Cloudflare Workers
autotel-cloudflare provides feature-targeted Cloudflare instrumentation and covers every binding (KV, R2, D1, Durable Objects, Workers AI, Vectorize, Hyperdrive, Service Bindings, Queues, Analytics Engine, Email) with automatic instrumentation. It also provides tail sampling and three API styles compatible with @microlabs/otel-cf-workers and workers-honeycomb-logger.
Built on autotel-edge. Total bundle ~45KB.
Installation
Section titled “Installation”npm install autotel-cloudflarewrangler.toml setup
Section titled “wrangler.toml setup”Pair Cloudflare's native OTel destinations with autotel's sampling. Set
head_sampling_rate = 1.0 so autotel handles tail sampling itself:
[observability.traces]enabled = truedestinations = ["honeycomb"] # configure in CF dashboardhead_sampling_rate = 1.0If you'd rather export OTLP yourself, skip the [observability] block and pass
an exporter.url to wrapModule() (see Configuration).
Quick start
Section titled “Quick start”import { wrapModule, trace } from 'autotel-cloudflare';
const processOrder = trace(async (orderId: string, kv: KVNamespace) => { const order = await kv.get(orderId); // auto-instrumented return order;});
export default wrapModule( { service: { name: 'my-worker' }, instrumentBindings: true, sampling: 'adaptive', }, { async fetch(req, env, ctx) { return Response.json(await processOrder('123', env.ORDERS_KV)); }, },);Every call to processOrder produces a span; every env.ORDERS_KV.get(...)
inside it produces a child span automatically.
API styles
Section titled “API styles”Pick the style that matches the ecosystem you came from. All three accept the same config object.
wrapModule(config, handler) — recommended
Section titled “wrapModule(config, handler) — recommended”Compatible with workers-honeycomb-logger. Config first, handler second:
import { wrapModule } from 'autotel-cloudflare';
export default wrapModule( { service: { name: 'my-worker' } }, { async fetch(req, env, ctx) { return new Response('Hello'); }, async scheduled(event, env, ctx) { /* cron */ }, async queue(batch, env, ctx) { /* queue consumer */ }, async email(message, env, ctx) { /* email handler */ }, },);instrument(handler, config)
Section titled “instrument(handler, config)”Compatible with @microlabs/otel-cf-workers. Handler first, config second:
import { instrument } from 'autotel-cloudflare';
export default instrument( { async fetch(req, env, ctx) { return new Response('Hello'); } }, { service: { name: 'my-worker' } },);Functional trace() / span()
Section titled “Functional trace() / span()”For inner business logic. Combine with wrapModule for full coverage:
import { trace, span } from 'autotel-cloudflare';
export const processPayment = trace((ctx) => async (amount: number) => { ctx.setAttribute('amount', amount); await span({ name: 'validate.card' }, () => validateCard()); await span({ name: 'charge.card' }, () => chargeCard(amount)); return { success: true };});Fetch route controls
Section titled “Fetch route controls”Filter which routes are traced and map URL patterns to per-route service names. Useful for an edge gateway that forwards to multiple downstreams:
export default wrapModule( { service: { name: 'edge-gateway' }, handlers: { fetch: { include: ['/api/**'], exclude: ['/api/internal/**', '/health'], routes: { '/api/auth/**': { service: 'auth-service' }, '/api/**': { service: 'api-service' }, }, postProcess: (span, { request, response }) => { const url = new URL(request.url); span.setAttribute('api.endpoint', url.pathname); }, }, }, }, handler,);Bindings coverage
Section titled “Bindings coverage”All bindings are auto-instrumented when instrumentBindings: true. Each binding
operation produces a span named <Binding> <NAME>: <op> (e.g.
KV ORDERS_KV: get).
| Binding | Operations |
| ------------------- | ---------------------------------------------------------------- |
| KV | get, put, delete, list, getWithMetadata |
| R2 | head, get, put, delete, list, createMultipartUpload |
| D1 | prepare, batch, exec, dump |
| Durable Objects | fetch, alarm (use wrapDurableObject for the class itself) |
| Workflows | get, create, getInstance |
| Workers AI | run |
| Vectorize | insert, query, getByIds, deleteByIds, upsert |
| Hyperdrive | All queries |
| Service Binding | fetch |
| Queue | send, sendBatch |
| Analytics Engine| writeDataPoint |
| Email | send, forward |
Global instrumentations are enabled with instrumentation.instrumentGlobalFetch
and instrumentation.instrumentGlobalCache.
Durable Objects
Section titled “Durable Objects”Wrap the DO class itself so its fetch and alarm methods become root spans:
import { wrapDurableObject } from 'autotel-cloudflare';
class Counter implements DurableObject { async fetch(request: Request) { const count = (await this.state.storage.get('count')) ?? 0; await this.state.storage.put('count', count + 1); return new Response(String(count + 1)); }
async alarm() { // span "Counter: alarm" }}
export const CounterDO = wrapDurableObject( { service: { name: 'counter-do' } }, Counter,);Queues, scheduled, email handlers
Section titled “Queues, scheduled, email handlers”wrapModule instruments every handler defined on the module. No extra setup
needed:
export default wrapModule( { service: { name: 'consumer' } }, { async queue(batch, env, ctx) { // root span "queue.consumer"; each message processed inside for (const msg of batch.messages) { await processMessage(msg.body); msg.ack(); } }, async scheduled(event, env, ctx) { // root span "scheduled.cron" }, async email(message, env, ctx) { // root span "email.received" }, },);Sampling
Section titled “Sampling”Tail sampling decides after the trace has been recorded, so you can keep 100% of errors and slow requests while sampling the rest at any rate.
Presets
Section titled “Presets”import { SamplingPresets } from 'autotel-cloudflare/sampling';
wrapModule( { service: { name: 'my-worker' }, sampling: { tailSampler: SamplingPresets.production() }, // shorthand: sampling: 'adaptive' }, handler,);| Preset | Behaviour |
| ---------------------------------- | ---------------------------------------- |
| SamplingPresets.development() | 100% sampling |
| SamplingPresets.production() | 10% baseline + 100% errors + slow >1s |
| SamplingPresets.highTraffic() | 1% baseline + 100% errors + slow >1s |
| SamplingPresets.debugging() | Errors only |
Shorthand strings: 'adaptive' → production preset, 'error-only' → debugging
preset.
Custom sampler
Section titled “Custom sampler”import { createCustomTailSampler } from 'autotel-cloudflare/sampling';import { SpanStatusCode } from '@opentelemetry/api';
const sampler = createCustomTailSampler((trace) => { const span = trace.localRootSpan; if (span.attributes['http.route']?.toString().startsWith('/api/')) return true; if (span.status.code === SpanStatusCode.ERROR) return true; const durationMs = (span.endTime[0] - span.startTime[0]) / 1_000_000; if (durationMs > 1000) return true; return Math.random() < 0.1;});Request logger
Section titled “Request logger”createWorkersLogger() returns a request-scoped logger pre-populated with
http.method, url.path, cf-ray, traceparent, and CF context:
import { createWorkersLogger, wrapModule } from 'autotel-cloudflare';
export default wrapModule( { service: { name: 'checkout-worker' } }, { async fetch(request) { const log = createWorkersLogger(request, { headers: ['x-request-id'], });
log.set({ checkout: { stage: 'validated' } }); log.info('checkout.started'); log.emitNow({ status: 200 });
return new Response('ok'); }, },);Service bindings
Section titled “Service bindings”Outgoing env.MY_SERVICE.fetch(...) calls are auto-instrumented and propagate
W3C trace headers automatically. No extra configuration required.
Events
Section titled “Events”Emit product/business events through a request-scoped subscribers helper with active trace context attached:
import { wrapModule } from 'autotel-cloudflare';import { getEdgeSubscribers } from 'autotel-cloudflare/events';
export default wrapModule( { service: { name: 'my-worker' }, subscribers: [ async (event) => { await fetch('https://analytics.example.com/events', { method: 'POST', body: JSON.stringify(event), }); }, ], }, { async fetch(req, env, ctx) { const subscribers = getEdgeSubscribers(ctx); await subscribers.trackEvent('order.completed', { orderId: 'abc', amount: 99.99, }); return new Response('OK'); }, },);Configuration
Section titled “Configuration”Full surface, including dynamic config that closes over env:
export default wrapModule( (env, trigger) => ({ service: { name: env.SERVICE_NAME ?? 'my-worker', version: '1.0.0', namespace: env.ENVIRONMENT, }, instrumentBindings: true, instrumentation: { instrumentGlobalFetch: true, instrumentGlobalCache: true, disabled: false, }, sampling: { tailSampler: env.ENVIRONMENT === 'production' ? SamplingPresets.production() : SamplingPresets.development(), }, exporter: { url: env.OTEL_ENDPOINT, headers: { 'x-api-key': env.API_KEY }, }, }), handler,);Testing
Section titled “Testing”autotel-cloudflare does not expose dedicated runtime testing helpers. Use your existing Worker test runner (Vitest/Miniflare/Wrangler) and assert on responses plus exporter output in integration tests.
Entry points
Section titled “Entry points”| Import | Contents |
| ------------------------------- | --------------------------------------- |
| autotel-cloudflare | Functional API, handler wrappers, bindings instrumentation |
| autotel-cloudflare/bindings | instrumentKV, instrumentR2, etc. |
| autotel-cloudflare/handlers | instrumentDO, handler wrappers |
| autotel-cloudflare/logger | createEdgeLogger, createWorkersLogger, runWithLogLevel |
| autotel-cloudflare/sampling | SamplingPresets, createCustomTailSampler, etc. |
| autotel-cloudflare/events | getEdgeSubscribers, createEdgeSubscribers |
| autotel-cloudflare/testing | Placeholder entrypoint (currently no runtime helpers) |
autotel-cloudflare now uses shared route-matching utilities from
autotel-edge, so include/exclude glob behavior is consistent across edge
integrations.
Examples
Section titled “Examples”cloudflare-example— KV, R2, D1, AI, Queues, Durable Objects, scheduled, email, custom sampling.
Related
Section titled “Related”- Edge Runtimes — vendor-agnostic foundation
- Adapters —
autotel-adapters/cloudflare