otel-cf-workers

repository·main·Indexed 19 days ago

https://github.com/evanderkoogh/otel-cf-workers

An OpenTelemetry compatible library for instrumenting and exporting traces from Cloudflare Workers and Durable Objects. It provides auto-instrumentation for common bindings (KV, Queue, D1, etc.), globals (fetch, caches), and supported triggers including HTTP, Email, Cron, and Durable Object alarms. The library supports head and tail sampling, custom span processing via PostProcessor, and OTLP-compatible exporters.

Tokens
12.3K
Snippets
44
Records
53
Agent score
64%

What's inside @microlabs/otel-cf-workers

  1. Configure Sampling (Head vs Tail)

    main

    Sampling controls which traces are stored to manage costs and volume.

    Head Sampling

    Performed at the start of a trace. It signals to downstream systems whether a trace should be sampled.

    • Configuration: Use a standard OpenTelemetry Sampler or an object with ratio (0 to 1) and acceptRemote (boolean).
    • Default: AlwaysOnSampler (samples everything).

    Tail Sampling

    Performed at the end of a trace. This allows you to capture traces based on their outcome (e.g., only if an error occurred), even if they weren't head-sampled.

    • Default: Samples traces that were head-sampled OR if the local root span is marked as an error.
    // Head Sampling configuration object
    const headSampler = {
    	acceptRemote: false,
    	ratio: 0.5 // Samples 50% of requests
    }
    
    // Tail Sampling logic example
    const tailSampler = (traceInfo: LocalTrace): boolean => {
    	const localRootSpan = traceInfo.localRootSpan as unknown as ReadableSpan
    	return (localRootSpan.spanContext().traceFlags & TraceFlags.SAMPLED) === TraceFlags.SAMPLED
    }
  2. Install @microlabs/otel-cf-workers

    main

    To get started with OpenTelemetry in Cloudflare Workers, install the @microlabs/otel-cf-workers package along with the @opentelemetry/api package. You must also configure your Honeycomb API key as a secret in Wrangler and enable Node.js compatibility in your wrangler.toml.

    npm install @microlabs/otel-cf-workers @opentelemetry/api
    npx wrangler secret put HONEYCOMB_API_KEY

    In your wrangler.toml, add:

    compatibility_flags = [ "nodejs_compat" ]
  3. Auto-instrument Durable Objects with instrumentDO()

    main

    To instrument a Durable Object, wrap the class itself using the instrumentDO function instead of wrapping a handler.

    import { instrumentDO, PartialTraceConfig } from '@microlabs/otel-cf-workers'
    
    const config: ResolveConfigFn = (env: Env, _trigger) => {
    	return {
    		exporter: {
    			url: 'https://api.honeycomb.io/v1/traces',
    			headers: { 'x-honeycomb-team': env.HONEYCOMB_API_KEY },
    		},
    		service: { name: 'greetings-do' },
    	}
    }
    
    class OtelDO implements DurableObject {
    	async fetch(request: Request): Promise<Response> {
    		return new Response('Hello World!')
    	}
    }
    
    const TestOtelDO = instrumentDO(OtelDO, config)
    
    export { TestOtelDO }
  4. Auto-instrument Workers with instrument()

    main

    To automatically instrument your Worker's handler, global fetch, caches, and supported bindings (like KV), wrap your handler with the instrument function. This provides automatic tracing for the handler's lifecycle and outbound requests.

    import { trace } from '@opentelemetry/api'
    import { instrument, ResolveConfigFn } from '@microlabs/otel-cf-workers'
    
    export interface Env {
    	HONEYCOMB_API_KEY: string
    	OTEL_TEST: KVNamespace
    }
    
    const handler = {
    	async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
    		await fetch('https://cloudflare.com')
    
    		const greeting = "G'day World"
    		tace.getActiveSpan()?.setAttribute('greeting', greeting)
    		ctx.waitUntil(fetch('https://workers.dev'))
    		return new Response(`${greeting}! `)
    	},
    }
    
    const config: ResolveConfigFn = (env: Env, _trigger) => {
    	return {
    		exporter: {
    			url: 'https://api.honeycomb.io/v1/traces',
    			headers: { 'x-honeycomb-team': env.HONEYCOMB_API_KEY },
    		},
    		service: { name: 'greetings' },
    	}
    }
    
    export default instrument(handler, config)
  5. Deploy Worker to Cloudflare

    main

    Once your CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID are configured in Replit secrets, you can publish your Worker to the Cloudflare global network (defaulting to a *.workers.dev subdomain) by running:

    npm run deploy

    To configure the Replit Run button to perform a deployment instead of running in dev mode, update the replit-run-command key in package.json to npm run deploy.

  6. Configure Cloudflare credentials for deployment

    main

    To deploy your Worker to the Cloudflare global network from Replit, you must add two specific secrets to your Replit environment:

    1. Cloudflare API Token

      • Create a token in the Cloudflare Dashboard using the Edit Cloudflare Workers template.
      • Ensure Account Resources is set to All accounts (or a specific one) and Zone Resources is set to All zones.
      • Add this to Replit secrets as CLOUDFLARE_API_TOKEN.
    2. Cloudflare Account ID

      • Find your Account ID on the right side of the Workers overview page in the Cloudflare dashboard.
      • Add this to Replit secrets as CLOUDFLARE_ACCOUNT_ID.

    Note: You may need to restart the Shell tab in Replit for these secrets to become available.

  7. Develop and run in Replit

    main

    After initialization, clicking the Run button deploys your Worker in dev mode, making it available on a Replit subdomain.

    Note on Sleeping: Replit may go to sleep after inactivity. To prevent this, enable the Always On power-up in your Replit workspace settings.

    Persistent Data Development

    To run your Worker with data persisted in the data folder of your Repl, run the following command in the Shell tab:

    npm run start-persist

    To make the Run button always use persistent mode, update the replit-run-command key in package.json to npm run start-persist.

  8. Create custom spans using OpenTelemetry API

    main

    You can add application-specific information by interacting with the active span or creating new spans using the standard @opentelemetry/api.

    // Adding attributes to the current active span
    import { trace } from '@opentelemetry/api'
    
    const handler = {
    	async fetch(request: Request) {
    		const span = trace.getActiveSpan()
    		if(span) span.setAttributes('name', 'value')
    		....
    	}
    }
    
    // Creating a new child span
    import { trace } from '@opentelemetry/api'
    
    const handler = {
    	async fetch(request: Request) {
    		const tracer = trace.getTracer('my_own_tracer_name')
    		return tracer.startActiveSpan('name', (span) => {
    			const response = await doSomethingAwesome
    			span.end()
    			return response
    		})
    	},
    }
  9. Instrument a Cloudflare Worker with OpenTelemetry

    main

    You can wrap a standard Cloudflare Worker handler using the instrument function. This wraps the worker in an OpenTelemetry span and allows you to configure an exporter (e.g., Honeycomb) via a configuration function.

    Use the ResolveConfigFn type to define a configuration function that receives the worker's env and returns an object containing exporter settings (URL and headers) and service settings (service name).

    import { instrument, ResolveConfigFn } from '@microlabs/otel-cf-workers'
    import { trace } from '@opentelemetry/api'
    
    export interface Env {
    	HONEYCOMB_API_KEY: string
    }
    
    const handler = {
    	async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
    		// Your worker logic here
    		return await fetch(request)
    	},
    }
    
    const config: ResolveConfigFn = (env: Env, _trigger: any) => {
    	return {
    		exporter: {
    			url: 'https://api.honeycomb.io/v1/traces',
    			headers: { 'x-honeycomb-team': env.HONEYCOMB_API_KEY },
    		},
    		service: { name: 'my-service-name' },
    	}
    }
    
    export default instrument(handler, config)
  10. Configure sampling with SamplingConfig

    main

    The SamplingConfig allows you to control which traces are recorded using head-based and tail-based sampling.

    • headSampler: A standard OpenTelemetry Sampler or a ParentRatioSamplingConfig (which allows specifying a ratio and whether to acceptRemote parents).
    • tailSampler: A TailSampleFn used for decisions made after spans have been collected.
    export interface SamplingConfig<HS extends HeadSamplerConf = HeadSamplerConf> {
    	headSampler?: HS
    	tailSampler?: TailSampleFn
    }
    
    // ParentRatioSamplingConfig example
    const sampling: SamplingConfig = {
      headSampler: {
        ratio: 0.5,
        acceptRemote: true
      }
    };