How the Motel OpenTelemetry Tracing System Captures Spans in Magnitude

Magnitude exports OpenTelemetry spans to the Motel collector via an OTLP JSON layer configured through environment variables, which ingests traces at http://127.0.0.1:27686/v1/traces and exposes them through a REST API for dashboard consumption.

The magnitudedev/magnitude repository implements distributed tracing using Motel, a local OpenTelemetry Protocol (OTLP) collector. When enabled, the system captures detailed execution spans from Effect-TS runtimes and makes them available for debugging through the ACN dashboard.

Enabling Tracing via Environment Configuration

Tracing activation begins with environment variable detection in the client-common package. The readOtelEndpoint function in packages/client-common/src/tracing.ts checks for specific flags to determine whether and where to export spans.

Set MAGNITUDE_OTEL=1 to enable the default local collector, or specify a custom endpoint using MAGNITUDE_OTEL_ENDPOINT or OTEL_EXPORTER_OTLP_TRACES_ENDPOINT:

// packages/client-common/src/tracing.ts
const readOtelEndpoint = () => {
  if (process.env.MAGNITUDE_OTEL) {
    return "http://127.0.0.1:27686"; // Default Motel URL
  }
  return process.env.MAGNITUDE_OTEL_ENDPOINT 
    || process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT 
    || null;
}

If no endpoint is configured, the tracing layer returns Layer.empty, effectively disabling span export without breaking the application.

Constructing the OTLP Export Layer

When tracing is enabled, makeTracingLayer constructs an Otlp.layerJson that serializes spans to JSON and transmits them over HTTP. This layer is defined in packages/client-common/src/tracing.ts and performs two critical functions:

  • Configures the exporter: Points to the resolved endpoint (defaulting to http://127.0.0.1:27686) and sets the service name (e.g., magnitude-cli)
  • Replaces the console logger: Substitutes the default Effect logger with a no-op implementation to ensure logs flow exclusively through the OTLP pipeline
// Layer construction from packages/client-common/src/tracing.ts
const makeTracingLayer = (otelEndpoint: string) => {
  return Otlp.layerJson({ 
    baseUrl: otelEndpoint,
    resource: { serviceName: "magnitude-cli" }
  }).pipe(
    Layer.merge(Logger.replace(Logger.defaultLogger, Logger.none))
  );
};

The resulting TracingLayer (the default export from the module) can be provided to any Effect runtime stack.

Integrating Tracing into the Runtime

Components throughout the Magnitude ecosystem—including the CLI, desktop client, and ACN daemon—activate tracing by adding TracingLayer to their Effect program layers. This single integration point enables span capture across the entire application surface.

import { TracingLayer } from "@magnitudedev/client-common";

// Apply to any Effect runtime
const program = Effect.all([
  // ... application effects
]).pipe(
  Effect.provide(TracingLayer)  // Activates Motel export
);

Once provided, all Effect.log* calls, Effect.logSpan invocations, and custom Tracer API usage automatically generate spans that route through the configured OTLP exporter rather than stdout.

Capturing and Ingesting Spans

The Motel collector operates as a local HTTP server bound to http://127.0.0.1:27686. It exposes the standard OTLP endpoint at /v1/traces, receiving JSON-encoded span batches from the Effect runtime. Motel stores these traces in memory, making them immediately queryable without persistent storage overhead.

Throughout the codebase—particularly in packages/agent and related modules—execution flows generate telemetry through Effect's logging utilities:

// Example span generation (conceptual)
Effect.gen(function* () {
  yield* Effect.log("Processing RPC request");
  yield* Effect.logSpan("rpc-handler", Effect.sleep("100 millis"));
});

These calls serialize into OTLP-compliant payloads and transmit to Motel's ingestion endpoint via HTTP POST requests.

Querying Traces via the Dashboard

The ACN dashboard server consumes captured spans through Motel's REST API. The listRpcTraces function in packages/acn-dashboard/src/server.ts queries /api/traces/search and filters results to isolate RpcServer. operations, converting raw telemetry into the internal RpcTraceSummary shape.

// packages/acn-dashboard/src/server.ts
const listRpcTraces = async () => {
  const response = await fetch(
    `${MOTEL_BASE_URL}/api/traces/search`
  );
  const traces = await response.json();
  return traces
    .filter(t => t.name.startsWith("RpcServer."))
    .map(toRpcTraceSummary);
};

This transformation extracts trace IDs, RPC method names, duration in milliseconds, and error counts, presenting actionable debugging data in the dashboard UI.

Configuration Examples

To enable Motel OpenTelemetry tracing in your Magnitude deployment:


# Use the default local collector

export MAGNITUDE_OTEL=1

# Or specify a remote OTLP endpoint

export MAGNITUDE_OTEL_ENDPOINT=http://otel.my-company.com:4318

Then provide the layer in your application entry point:

import { TracingLayer } from "@magnitudedev/client-common";

const runtime = Effect.runPromise(
  Effect.all([
    // ... your effects
  ]).provide(TracingLayer)
);

To fetch traces programmatically from the dashboard server:

import { listRpcTraces } from "@magnitudedev/acn-dashboard";

async function showRecentRpcTraces() {
  const traces = await listRpcTraces();
  console.table(traces.map(t => ({
    id: t.traceId,
    rpc: t.rpcName,
    durationMs: t.durationMs,
    errors: t.errorCount,
  })));
}

Key Source Files

  • packages/client-common/src/tracing.ts – Defines readOtelEndpoint, makeTracingLayer, and the default TracingLayer export
  • packages/acn/src/tracing.ts – ACN daemon-specific tracing configuration (mirrors client-common implementation)
  • packages/acn-dashboard/src/server.ts – Implements listRpcTraces and toRpcTraceSummary for dashboard queries
  • packages/acn-protocol/src/boundary/ – RPC contract definitions annotated with tracing metadata
  • packages/agent/ – Runtime span generation via Effect.log* and custom tracer calls

Summary

  • Environment-driven configuration: The readOtelEndpoint function activates tracing when MAGNITUDE_OTEL=1 is set, defaulting to the local Motel instance at http://127.0.0.1:27686.
  • OTLP layer construction: makeTracingLayer creates an Otlp.layerJson that serializes spans and suppresses console output, ensuring clean telemetry export.
  • Runtime integration: Applications provide TracingLayer to their Effect stacks, enabling automatic span capture across CLI, desktop, and ACN components.
  • Motel ingestion: The collector receives spans at /v1/traces, storing them in memory for immediate querying via the dashboard's /api/traces/search endpoint.
  • Dashboard visualization: The ACN server filters RPC-specific traces and converts them into RpcTraceSummary objects for debugging.

Frequently Asked Questions

How do I configure a custom OpenTelemetry endpoint instead of Motel?

Set the MAGNITUDE_OTEL_ENDPOINT or OTEL_EXPORTER_OTLP_TRACES_ENDPOINT environment variable to your OTLP-compatible collector URL. The readOtelEndpoint function in packages/client-common/src/tracing.ts prioritizes these over the default Motel URL when MAGNITUDE_OTEL is not explicitly enabled.

What happens if tracing is not enabled?

When no endpoint is configured, makeTracingLayer returns Layer.empty, which passes spans through without exporting them. The application continues to function normally, with telemetry simply not transmitted to Motel.

Does Motel persist traces to disk?

No. According to the implementation, Motel keeps trace data in memory only. It runs as a local HTTP server (typically at http://127.0.0.1:27686) and provides a transient store for development and debugging sessions rather than long-term retention.

Where are RPC-specific spans filtered in the codebase?

The listRpcTraces function in packages/acn-dashboard/src/server.ts queries Motel's /api/traces/search endpoint and filters for spans where the operation name starts with RpcServer.. It then applies toRpcTraceSummary to transform the raw OpenTelemetry data into a UI-friendly format containing trace IDs, method names, durations, and error counts.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →