Cloudflare Workers
Workers run in V8 isolates — there's no NODE_OPTIONS preload hook, so instrumentation is a small helper instead of a fully automatic agent. la init writes it for you.
Lightarch itself is built on Cloudflare Workers and uses this exact pattern to instrument its own services.
Setup
bash
la init --dsn la_live_YOUR_TOKEN_HEREIn a Workers project (detected via wrangler.toml / wrangler.jsonc), this writes la-instrumentation.ts — a fetch-based span helper — and .dev.vars for local development. For production, store the DSN as a secret:
bash
echo "la_live_YOUR_TOKEN_HERE" | wrangler secret put LA_DSNUse in your Worker
src/index.ts
import { newTrace, sendSpan } from './la-instrumentation'
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
const { traceId, spanId } = newTrace()
const start = Date.now()
const url = new URL(request.url)
try {
const response = await handleRequest(request, env)
sendSpan(ctx, {
name: `${request.method} ${url.pathname}`,
traceId, spanId, startMs: start, endMs: Date.now(),
attrs: { 'http.method': request.method, 'http.status_code': response.status },
isError: response.status >= 500,
})
return response
} catch (err) {
sendSpan(ctx, {
name: `${request.method} ${url.pathname}`,
traceId, spanId, startMs: start, endMs: Date.now(),
attrs: { 'error.message': (err as Error).message },
isError: true,
})
throw err
}
},
}ctx.waitUntil() inside the helper sends the span after your response is returned — it adds zero latency to your Worker's response time.Screenshot needed
Traces page showing a Cloudflare Worker trace: service='my-worker', spans for 'GET /api/users' with 12ms duration, environment='production'.
Prefer to write the helper by hand? Here's the whole thing
src/la-instrumentation.ts
const INGEST_URL = 'https://la-ingest-gateway.lightarch.workers.dev/v1/traces'
export function newTrace() {
const rand = (n: number) => {
const a = new Uint8Array(n); crypto.getRandomValues(a)
return Array.from(a).map(b => b.toString(16).padStart(2, '0')).join('')
}
return { traceId: rand(16), spanId: rand(8) }
}
export function sendSpan(ctx: ExecutionContext, span: {
name: string; traceId: string; spanId: string
startMs: number; endMs: number
attrs?: Record<string, string | number | boolean>; isError?: boolean
}): void {
ctx.waitUntil(
fetch(INGEST_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'x-la-dsn': LA_DSN },
body: JSON.stringify({ resourceSpans: [{ resource: { attributes: [
{ key: 'service.name', value: { stringValue: 'my-worker' } },
]}, scopeSpans: [{ spans: [{
traceId: span.traceId, spanId: span.spanId, name: span.name,
startTimeUnixNano: String(span.startMs * 1_000_000),
endTimeUnixNano: String(span.endMs * 1_000_000),
status: { code: span.isError ? 2 : 1 },
attributes: Object.entries(span.attrs ?? {}).map(([k, v]) => ({
key: k,
value: typeof v === 'number' ? { intValue: v }
: typeof v === 'boolean' ? { boolValue: v }
: { stringValue: String(v) },
})),
}]}]}]}),
}).catch(() => {})
)
}