npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

autolemetry-cloudflare

v2.1.0

Published

The #1 OpenTelemetry package for Cloudflare Workers - complete bindings coverage, native CF OTel integration, advanced sampling

Downloads

215

Readme

autolemetry-cloudflare

The #1 OpenTelemetry package for Cloudflare Workers - complete bindings coverage, native CF OTel integration, advanced sampling, zero vendor lock-in.

npm version Bundle Size License: MIT

Features

  • Native Cloudflare OTel integration - Works with wrangler.toml destinations
  • Complete bindings coverage - KV, R2, D1, DO, AI, Vectorize, Hyperdrive, and more
  • Multiple API styles - instrument(), wrapModule(), wrapDurableObject(), functional
  • Advanced sampling - Adaptive tail sampling (10% baseline, 100% errors/slow)
  • Events integration - Product analytics with trace correlation
  • Zero vendor lock-in - OTLP compatible, works with any backend
  • Tree-shakeable - Import only what you need
  • TypeScript native - Full type safety

Installation

npm install autolemetry-cloudflare
# or
pnpm add autolemetry-cloudflare
# or
yarn add autolemetry-cloudflare

Quick Start

1. Configure Cloudflare Native OTel (wrangler.toml)

[observability.traces]
enabled = true
destinations = ["honeycomb"]  # Configure in CF dashboard
head_sampling_rate = 1.0      # Let autolemetry handle sampling

2. Instrument Your Worker

import { wrapModule, trace } from 'autolemetry-cloudflare'

// Zero-boilerplate function tracing
const processOrder = trace(async (orderId: string) => {
  const order = await env.ORDERS_KV.get(orderId)  // Auto-instrumented!
  return order
})

export default wrapModule(
  {
    service: { name: 'my-worker' },
    instrumentBindings: true,    // Auto-instrument KV, R2, D1, etc.
    sampling: 'adaptive'          // 10% baseline, 100% errors/slow
  },
  {
    async fetch(req, env, ctx) {
      return Response.json(await processOrder('123'))
    }
  }
)

API Styles

Style 1: wrapModule (Recommended)

Inspired by workers-honeycomb-logger:

import { wrapModule } from 'autolemetry-cloudflare'

const handler = {
  async fetch(req, env, ctx) {
    return new Response('Hello')
  }
}

export default wrapModule(
  { service: { name: 'my-worker' } },
  handler
)

Style 2: instrument

import { instrument } from 'autolemetry-cloudflare'

export default instrument(
  {
    async fetch(req, env, ctx) {
      return new Response('Hello')
    }
  },
  { service: { name: 'my-worker' } }
)

Style 3: Functional API (Unique)

Zero-boilerplate function tracing:

import { trace, span } from 'autolemetry-cloudflare'

// Automatic trace name inference
export const createUser = trace(async (data: UserData) => {
  return await db.insert(data)
})

// Factory pattern for context access
export const processPayment = trace(ctx => async (amount: number) => {
  ctx.setAttribute('amount', amount)

  await span('validate.card', () => validateCard())
  await span('charge.card', () => chargeCard(amount))

  return { success: true }
})

Complete Bindings Coverage

Auto-Instrumented Bindings

All bindings are automatically instrumented when instrumentBindings: true:

// KV
await env.MY_KV.get('key')           // → Span: "KV MY_KV: get"
await env.MY_KV.put('key', 'value')  // → Span: "KV MY_KV: put"

// R2
await env.MY_R2.get('file.txt')      // → Span: "R2 MY_R2: get"
await env.MY_R2.put('file.txt', data) // → Span: "R2 MY_R2: put"

// D1
await env.MY_D1.prepare('SELECT * FROM users').all()  // → Span: "D1 MY_D1: all"

// Durable Objects
await env.MY_DO.get(id).fetch(req)   // → Span: "DO MY_DO: fetch"

// Workers AI
await env.AI.run('@cf/meta/llama', { prompt: '...' })  // → Span: "AI: run"

// Vectorize
await env.VECTOR.query(vector)       // → Span: "Vectorize VECTOR: query"

// Service Bindings
await env.MY_SERVICE.fetch(req)      // → Span: "Service MY_SERVICE: fetch"

// Queue
await env.MY_QUEUE.send({ data })    // → Span: "Queue MY_QUEUE: send"

// Analytics Engine
await env.ANALYTICS.writeDataPoint({ ... })  // → Span: "Analytics: writeDataPoint"

Supported Bindings:

  • ✅ KV (get, put, delete, list, getWithMetadata)
  • ✅ R2 (head, get, put, delete, list, createMultipartUpload)
  • ✅ D1 (prepare, batch, exec, dump)
  • ✅ Durable Objects (fetch, alarm)
  • ✅ Workflows (get, create, getInstance)
  • ✅ Workers AI (run)
  • ✅ Vectorize (insert, query, getByIds, deleteByIds, upsert)
  • ✅ Hyperdrive (all queries)
  • ✅ Service Bindings (fetch)
  • ✅ Queue (send, sendBatch)
  • ✅ Analytics Engine (writeDataPoint)
  • ✅ Email (send, forward)

Sampling Strategies

Adaptive Sampling (Recommended)

import { SamplingPresets } from 'autolemetry-cloudflare/sampling'

wrapModule(
  {
    service: { name: 'my-worker' },
    sampling: {
      tailSampler: SamplingPresets.production()
      // 10% baseline, 100% errors, 100% slow requests (>1s)
    }
  },
  handler
)

Available Presets

// Development - 100% sampling
sampling: { tailSampler: SamplingPresets.development() }

// Production - 10% baseline, all errors, slow >1s
sampling: { tailSampler: SamplingPresets.production() }

// High traffic - 1% baseline, all errors, slow >1s
sampling: { tailSampler: SamplingPresets.highTraffic() }

// Debugging - errors only
sampling: { tailSampler: SamplingPresets.debugging() }

// Or use shorthand
sampling: 'adaptive'      // Same as SamplingPresets.production()
sampling: 'error-only'    // Same as SamplingPresets.debugging()

Custom Sampling

import { createCustomTailSampler } from 'autolemetry-cloudflare/sampling'

const customSampler = createCustomTailSampler((trace) => {
  const span = trace.localRootSpan

  // Always sample /api/* endpoints
  if (span.attributes['http.route']?.toString().startsWith('/api/')) {
    return true
  }

  // Sample all errors
  if (span.status.code === SpanStatusCode.ERROR) {
    return true
  }

  // Sample slow requests
  const duration = (span.endTime[0] - span.startTime[0]) / 1_000_000
  if (duration > 1000) {
    return true
  }

  return Math.random() < 0.1  // 10% of everything else
})

Durable Objects

Instrument Durable Object Class

import { wrapDurableObject } from 'autolemetry-cloudflare'

class Counter implements DurableObject {
  async fetch(request: Request) {
    // Auto-traced with span "Counter: fetch"
    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() {
    // Auto-traced with span "Counter: alarm"
    console.log('Alarm triggered')
  }
}

export default wrapDurableObject(
  { service: { name: 'counter-do' } },
  Counter
)

Events Integration

Track product events with automatic trace correlation:

import { publishEvent } from 'autolemetry-cloudflare/events'

wrapModule(
  {
    service: { name: 'my-worker' },
    // Configure event subscribers
    subscribers: [
      async (event) => {
        // Send to your analytics platform
        await fetch('https://analytics.example.com/events', {
          method: 'POST',
          body: JSON.stringify(event)
        })
      }
    ]
  },
  {
    async fetch(req, env, ctx) {
      // Track user events
      await publishEvent({
        name: 'order.completed',
        userId: '123',
        properties: {
          orderId: 'abc',
          amount: 99.99
        }
        // Automatically includes current trace ID
      })

      return new Response('OK')
    }
  }
)

Configuration

Complete Example

import { wrapModule, SamplingPresets } from 'autolemetry-cloudflare'

export default wrapModule(
  {
    // Service identification
    service: {
      name: 'my-worker',
      version: '1.0.0',
      namespace: 'production'
    },

    // Auto-instrument bindings
    instrumentBindings: true,

    // Global instrumentations
    instrumentation: {
      instrumentGlobalFetch: true,   // Trace all fetch() calls
      instrumentGlobalCache: true,   // Trace cache API
      disabled: false                // Set true to disable all tracing
    },

    // Sampling strategy
    sampling: {
      tailSampler: SamplingPresets.production()
    },

    // Handler-specific config
    handlers: {
      fetch: {
        postProcess: (span, { request, response }) => {
          // Add custom attributes
          const url = new URL(request.url)
          if (url.pathname.startsWith('/api/')) {
            span.setAttribute('api.endpoint', url.pathname)
          }
        }
      }
    }
  },
  handler
)

Dynamic Configuration

// Configuration can be a function
export default wrapModule(
  (env, trigger) => ({
    service: { name: env.SERVICE_NAME || 'my-worker' },
    exporter: {
      url: env.OTEL_ENDPOINT,
      headers: { 'x-api-key': env.API_KEY }
    },
    sampling: {
      tailSampler: env.ENVIRONMENT === 'production'
        ? SamplingPresets.production()
        : SamplingPresets.development()
    }
  }),
  handler
)

Entry Points (Tree-Shaking)

// Main export (everything)
import { wrapModule, trace, instrument } from 'autolemetry-cloudflare'

// Tree-shakeable entry points
import { instrumentKV, instrumentR2 } from 'autolemetry-cloudflare/bindings'
import { instrumentDO } from 'autolemetry-cloudflare/handlers'
import { SamplingPresets } from 'autolemetry-cloudflare/sampling'
import { publishEvent } from 'autolemetry-cloudflare/events'
import { createEdgeLogger } from 'autolemetry-cloudflare/logger'
import { createTraceCollector } from 'autolemetry-cloudflare/testing'

Testing

import { createTraceCollector, assertTraceCreated } from 'autolemetry-cloudflare/testing'

describe('my worker', () => {
  it('creates traces', async () => {
    const collector = createTraceCollector()

    await myFunction()

    assertTraceCreated(collector, 'myFunction')
  })
})

Examples

See apps/cloudflare-example for a complete working example with:

  • ✅ All bindings instrumented (KV, R2, D1, etc.)
  • ✅ Multiple handler types (fetch, scheduled, queue, email)
  • ✅ Durable Objects
  • ✅ Custom spans and attributes
  • ✅ Error handling
  • ✅ Sampling strategies
  • ✅ Events tracking

License

MIT © Jag Reehal

Links