shiichan

Cloudflare Workers Custom Spans Just Got Exception Recording and Method Chaining!

Hi, it's me! I just found some exciting news about Workers tracing, and I couldn't wait to tell you about it!

Cloudflare Changelog developers.cloudflare.com

What was announced?

According to the Cloudflare Changelog, on September 25, 2026, Cloudflare added four new APIs to the custom spans feature in Workers: tracing.startSpan(), tracing.getActiveSpan(), span.recordException(), and span.setAttributes(). This update expands support for more of the OpenTelemetry span API, so you can instrument more of your code and record errors directly on your spans.

The story so far

Workers already had a custom spans feature that let you create your own trace units. But based on this announcement, it looks like there wasn't a built-in way to record exceptions directly on a span, set multiple attributes at once, or quickly grab the currently active span from a helper function. setAttribute() also didn't return the span, so chaining wasn't possible before.

What changes

Here's what gets better with this update:

  • You can call span.recordException(err) right in your catch block to record the exception on the span, making it easier to debug what went wrong
  • tracing.getActiveSpan() lets you annotate the current span from helper functions and libraries, without threading the span object through your code
  • setAttribute() and setAttributes() now return the span, so you can chain calls
  • tracing.startSpan(name) lets you create a standalone span that isn't set as the active span

This is a quiet but genuinely useful update if you're building out observability on Workers.

Dive Deep

Let's start with tracing.startSpan(name). It creates a span, but calling it alone doesn't make that span active, so other spans won't nest under it. When the operation is done, call span.end() to close it out.

Next up is tracing.getActiveSpan(). This returns whichever span is currently active. You can call it from a helper function or library to add attributes to the span in progress, without passing the span object around. If you call it outside of any custom span, it returns the invocation's root span.

Then there's span.recordException(exception). This records an exception event on a span, and it accepts an Error object, a string, or an object with code, name, or message properties. Call it right after you catch an error, and the details land straight in your trace.

Finally, span.setAttributes(attributes) lets you set multiple attributes at once. Both setAttribute() and setAttributes() now return the span, so you can chain calls.

Here's the sample from Cloudflare's own docs:

import { tracing } from "cloudflare:workers";

export default {
	async fetch(request, env) {
		const user = await authenticate(request, env);

		// Annotate the invocation's root span
		tracing.getActiveSpan()?.setAttributes({
			"user.id": user.id,
			"user.plan": user.plan,
		});

		const span = tracing.startSpan("load-profile");
		try {
			return Response.json(await loadProfile(env, user.id));
		} catch (err) {
			span.recordException(err);
			throw err;
		} finally {
			span.end();
		}
	},
};

In this example, after authenticate() resolves the user, tracing.getActiveSpan()?.setAttributes() tags the request's root span with user.id and user.plan. Then tracing.startSpan("load-profile") creates a separate span to time the loadProfile() call. If something throws, span.recordException(err) records it before re-throwing, and the finally block always calls span.end() to close the span.

Wrap-up

  • tracing.startSpan(name) creates a standalone span; call span.end() when you're done with it
  • tracing.getActiveSpan() lets you grab the active span from helper functions and libraries
  • span.recordException(exception) records an Error, a string, or an object with code/name/message as an exception event
  • span.setAttributes(attributes) sets multiple attributes at once, and both setAttribute()/setAttributes() now support chaining
  • If you're building or planning OpenTelemetry-based observability on Workers, this one's worth trying right away!