# Observability Hook Structure in @cloudflare/computer: Adapting It for Cloudflare Runtime Tracing

> Explore the @cloudflare/computer observability hook structure and learn how to adapt it for Cloudflare runtime tracing with the createCloudflareObserver factory. Enhance your application visibility.

- Repository: [Cloudflare/computer](https://github.com/cloudflare/computer)
- Tags: deep-dive
- Published: 2026-08-14

---

**The @cloudflare/computer package provides a span-oriented instrumentation API through the `WorkspaceObserver` interface that seamlessly bridges to the Cloudflare runtime's native `Tracing` API using the `createCloudflareObserver` factory.**

The **observability hook structure** in `@cloudflare/computer` offers a minimal, façade-based approach to distributed tracing that works across production Workers, Durable Objects, and local test environments without code changes. This article examines the core abstractions, their implementation in the source code, and the exact adapter pattern used for Cloudflare runtime tracing.

## Core Observability Abstractions

The foundation lives in [[`packages/computer/src/observe.ts`](https://github.com/cloudflare/computer/blob/main/packages/computer/src/observe.ts)](https://github.com/cloudflare/computer/blob/main/packages/computer/src/observe.ts), which exports four key constructs:

### WorkspaceSpan

A minimal span façade exposing only `setAttribute(key, value)`. This deliberate constraint mirrors the Cloudflare runtime's `Span` surface, letting adapters forward attributes without importing the full OpenTelemetry API.

### WorkspaceObserver

The central hook interface with a single method:

```typescript
span(name: string, attributes: Record<string, unknown>, run: (span: WorkspaceSpan) => T): T

```

- Starts a span named `name`
- Seeds it with `attributes`
- Executes `run` with the span
- Returns the callback's result (or promise) unchanged

### noopObserver

The default no-op implementation when no observer is supplied. It invokes the callback directly with a span whose `setAttribute` is a no-op, ensuring zero overhead when tracing is disabled.

### withSpan

The primary helper used throughout the workspace core:

```typescript
withSpan<T>(
  observer: WorkspaceObserver,
  name: string,
  attributes: Record<string, unknown>,
  run: (span: WorkspaceSpan) => T,
  finalize?: (span: WorkspaceSpan) => void
): T

```

This helper:
- Invokes `observer.span(...)`
- Automatically records error attributes via `recordError` if the callback throws
- Optionally runs `finalize` to add post-hoc attributes (byte counts, exit codes, etc.)

## Span Structure and Nesting

Spans follow logical operation nesting. An `exec` span contains child `sync.push` and `sync.pull` spans. The design accepts any context implementation respecting the `WorkspaceObserver.span` contract—whether the built-in Cloudflare adapter, an OpenTelemetry bridge, or custom implementations.

## Cloudflare Runtime Adapter

The bridge to Cloudflare's native tracing lives in [[`packages/computer/src/observe/cloudflare.ts`](https://github.com/cloudflare/computer/blob/main/packages/computer/src/observe/cloudflare.ts)](https://github.com/cloudflare/computer/blob/main/packages/computer/src/observe/cloudflare.ts). It maps the generic observer to the runtime's `Tracing` API (`tracing.enterSpan(name, callback)`).

### Adapter Mechanics

| Aspect | Implementation |
|--------|---------------|
| **Factory** | `createCloudflareObserver({ tracing })` returns `WorkspaceObserver` |
| **Graceful degradation** | Returns pass-through observer if `tracing` is `undefined` |
| **Span creation** | Wraps `tracing.enterSpan` with `WorkspaceSpan` façade |
| **Attribute forwarding** | `applyAttributes` copies seed attributes, omitting `undefined` values |
| **Name limits** | Runtime enforces 64-byte names; workspace stays within bounds |

### Production Usage

```typescript
// Inside a Worker or Durable Object
import { tracing } from "cloudflare:workers";
import { createCloudflareObserver } from "@cloudflare/computer/observe/cloudflare";
import { withSpan } from "@cloudflare/computer/observe";

const observer = createCloudflareObserver({ tracing });

async function runCommand(cmd: string) {
  return withSpan(
    observer,
    "workspace.runtime.exec",
    { "command": cmd },
    async (span) => {
      const result = await execCommand(cmd);
      span.setAttribute("exitCode", result.code);
      span.setAttribute("bytesWritten", result.stdout.length);
      return result;
    }
  );
}

```

### Test Environment Compatibility

The same code runs in Node-based unit tests without modification:

```typescript
// Tracing unavailable in test environment
const observer = createCloudflareObserver({ tracing: undefined });
// observer behaves like noopObserver; no conditional branching needed

```

### Attribute Forwarding Details

The `applyAttributes` helper in [[`cloudflare.ts`](https://github.com/cloudflare/computer/blob/main/cloudflare.ts)](https://github.com/cloudflare/computer/blob/main/packages/computer/src/observe/cloudflare.ts) handles the translation:

```typescript
function applyAttributes(span: Span, attributes: Record<string, unknown>): void {
  for (const [key, value] of Object.entries(attributes)) {
    if (value !== undefined) {
      span.setAttribute(key, value);
    }
  }
}

```

This filtering prevents `undefined` values from reaching the runtime API, which may reject non-serializable attributes.

## Implementing Custom Observers

To adapt a different tracing system (OpenTelemetry, Datadog, etc.), implement `WorkspaceObserver`:

```typescript
const myCustomObserver: WorkspaceObserver = {
  span(name, attributes, run) {
    // Start your tracing system's span
    return tracer.startActiveSpan(name, (otelSpan) => {
      // Apply seed attributes
      Object.entries(attributes).forEach(([k, v]) => {
        if (v !== undefined) otelSpan.setAttribute(k, v);
      });
      
      // Wrap in WorkspaceSpan façade
      const workspaceSpan: WorkspaceSpan = {
        setAttribute: (k, v) => otelSpan.setAttribute(k, v)
      };
      
      try {
        return run(workspaceSpan);
      } catch (err) {
        otelSpan.recordException(err);
        throw err;
      } finally {
        otelSpan.end();
      }
    });
  }
};

```

Pass this implementation wherever the workspace expects an observer. The core library depends only on the contract, never the concrete backend.

## Summary

- **The observability hook** centers on `WorkspaceObserver` with a single `span` method and `WorkspaceSpan` with `setAttribute`, defined in [`observe.ts`](https://github.com/cloudflare/computer/blob/main/observe.ts)
- **`withSpan`** is the ergonomic helper that drives instrumentation across the workspace, handling errors and finalization automatically
- **`createCloudflareObserver`** in [`observe/cloudflare.ts`](https://github.com/cloudflare/computer/blob/main/observe/cloudflare.ts) bridges to the runtime's `Tracing.enterSpan` API with zero-cost fallback when tracing is unavailable
- The **64-byte name limit** is respected by workspace conventions; no truncation logic is required
- **Environment portability** is built-in: identical code runs in production Workers and Node tests through graceful degradation

## Frequently Asked Questions

### What is the WorkspaceObserver interface in @cloudflare/computer?

`WorkspaceObserver` is a minimal hook interface containing one method: `span(name, attributes, run)`. It starts a named span, seeds it with attributes, executes the provided callback with a `WorkspaceSpan` façade, and returns the callback's result unchanged. This abstraction lets the workspace core emit traces without depending on any specific tracing backend.

### How does the Cloudflare runtime adapter handle missing tracing?

The `createCloudflareObserver` factory checks if the `tracing` parameter is `undefined`. If so, it returns a pass-through observer identical to `noopObserver`—the callback runs normally with a no-op span, incurring no overhead. This eliminates conditional branching in consuming code between production and test environments.

### Can I use the observability hook with OpenTelemetry instead of Cloudflare's native tracing?

Yes. Implement the `WorkspaceObserver` interface yourself, delegating `span` calls to your OpenTelemetry tracer's `startActiveSpan`. Wrap the Otel span in a `WorkspaceSpan` façade that forwards `setAttribute`. The workspace core only cares about the contract, so any compliant implementation works interchangeably.

### Where are the span names defined in the workspace?

Span names are hardcoded throughout the workspace core using dot-notation conventions like `"workspace.runtime.exec"`, `"workspace.sync.push"`, and `"workspace.fs.read"`. These names stay well under the Cloudflare runtime's 64-byte limit, as verified in the adapter implementation.