[READ-ONLY] Mirror of https://github.com/elsbrock/cf-worker-otel. Lightweight OTLP metrics client for Cloudflare Workers
0

Configure Feed

Select the types of activity you want to include in your feed.

TypeScript 100.0%
14 5 1

Clone this repository

https://tangled.org/else42.tngl.sh/cf-worker-otel https://tangled.org/did:plc:g23vn7pu67qpkb3jh3ihafq7
git@tangled.org:else42.tngl.sh/cf-worker-otel git@tangled.org:did:plc:g23vn7pu67qpkb3jh3ihafq7

For self-hosted knots, clone URLs may differ based on your setup.



README.md

cf-worker-otel#

Push metrics from Cloudflare Workers to Prometheus, Grafana Cloud, or any OTLP-compatible backend — without pulling in the full OpenTelemetry SDK.

  • ~200 lines, zero runtime dependencies
  • Counters, gauges, and histograms
  • OTLP/HTTP JSON with delta temporality
  • Built for waitUntil() — never blocks your response
  • No-ops silently without config — safe to always instrument

Install#

npm install @else42/cf-worker-otel

Usage#

Cloudflare Worker#

import { createMetrics } from "@else42/cf-worker-otel";

export default {
  async fetch(request, env, ctx) {
    const metrics = createMetrics({
      serviceName: "my-worker",
      endpoint: env.OTLP_ENDPOINT,
      token: env.OTLP_AUTH_TOKEN,
    });
    const start = Date.now();

    const response = await handleRequest(request);

    metrics.counter("http_requests_total", 1, {
      method: request.method,
      status: String(response.status),
    });
    metrics.histogram("http_request_duration_ms", Date.now() - start);

    ctx.waitUntil(metrics.flush());
    return response;
  },
};

SvelteKit on Cloudflare#

import { createMetrics } from "@else42/cf-worker-otel";
import { sequence } from "@sveltejs/kit/hooks";
import type { Handle } from "@sveltejs/kit";

const metricsHandle: Handle = async ({ event, resolve }) => {
  const env = event.platform?.env;
  const metrics = createMetrics({
    serviceName: "my-app",
    endpoint: env?.OTLP_ENDPOINT,
    token: env?.OTLP_AUTH_TOKEN,
  });
  const start = Date.now();
  let status = "500";
  try {
    const response = await resolve(event);
    status = String(response.status);
    return response;
  } finally {
    metrics.counter("http_requests_total", 1, {
      method: event.request.method,
      status,
      route: event.route.id ?? "unknown",
    });
    metrics.histogram("http_request_duration_ms", Date.now() - start);
    event.platform?.context?.waitUntil(metrics.flush());
  }
};

export const handle = sequence(metricsHandle, yourAppHandle);

API#

createMetrics(config)#

Creates a per-request metrics collector. Call once per invocation, flush at the end.

Option Type Required Description
serviceName string yes Maps to service.name in OTLP
endpoint string no OTLP HTTP endpoint URL
token string no Bearer token for the Authorization header
defaultAttributes Record<string, string> no Merged into every data point
histogramBounds number[] no Custom bucket boundaries (default: HTTP ms)

If endpoint or token is missing, flush() does nothing — safe to instrument unconditionally.

Metric types#

counter(name, value, attributes?) — Monotonic counter. Same name + attributes are aggregated within the request.

gauge(name, value, attributes?) — Point-in-time value. Last write wins per attribute set.

histogram(name, value, attributes?) — Single observation placed into a bucket.
Default bounds: [5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000, 10000] ms.

flush() — Serializes to OTLP JSON, POSTs via fetch(). Pass to ctx.waitUntil().

Prometheus setup#

Enable the OTLP receiver (v2.47+):

--web.enable-otlp-receiver

For delta temporality (v3+):

--enable-feature=otlp-deltatocumulative

The receiver listens at POST /api/v1/otlp/v1/metrics on the Prometheus port.

How it works#

Each createMetrics() call creates an isolated collector. Metrics accumulate in memory during request handling, then serialize to a single OTLP/HTTP JSON payload and POST in waitUntil(). One fetch per worker invocation — no batching across requests, no persistent state.

Delta temporality means each push contains only what happened during this request. Prometheus converts deltas to cumulative counters server-side.

License#

MIT