How OpenTelemetry Tracing Is Configured in Paperclip and Which Spans Are Auto-Instrumented

Paperclip's tracing is optional and activates only when OpenTelemetry packages are installed and OTEL_EXPORTER_OTLP_ENDPOINT is set, with automatic instrumentation for HTTP, PostgreSQL, and custom startup spans.

OpenTelemetry tracing in Paperclip provides observability for the control-plane service without forcing a hard dependency on the OpenTelemetry SDK. This article explains the bootstrap mechanism, exporter configuration, and which operations generate spans based on the source code in paperclipai/paperclip.

Bootstrap Architecture in instrumentation.ts

The tracing lifecycle begins in [server/src/instrumentation.ts](https://github.com/paperclipai/paperclip/blob/master/server/src/instrumentation.ts). At startup, Paperclip attempts to require("@opentelemetry/api") (lines 100–112). If the package is missing, the server falls back to a no-op tracer and logs a warning—allowing the application to run without telemetry. When available, the full SDK loads via @opentelemetry/sdk-node alongside @opentelemetry/auto-instrumentations-node (line 368).

This conditional loading pattern ensures that:

  • Development environments without OTLP collectors start without errors
  • Production deployments opt into tracing via environment variables
  • Plugin authors can import trace types safely even when OTEL is absent

OTLP Exporter Selection

Paperclip supports three exporter protocols based on the OTEL_EXPORTER_OTLP_ENDPOINT value (lines 13–15, 316–322):

Protocol Package Used Use Case
grpc (default) @opentelemetry/exporter-trace-otlp-grpc High-throughput, gRPC-native collectors
http/protobuf @opentelemetry/exporter-trace-otlp-proto Proxied or firewall-restricted environments
http/json @opentelemetry/exporter-trace-otlp-http Debugging, human-readable payloads

The SDK attaches resource attributes using @opentelemetry/resources and @opentelemetry/semantic-conventions, including service name, version, and other standard identifiers (lines 321–326).

Auto-Instrumented Spans and Disabled Modules

The auto-instrumentations-node package provides broad coverage, but Paperclip selectively disables heavy modules that don't benefit the control-plane service (inside instrumentation.ts):

{
  "@opentelemetry/instrumentation-fs": { enabled: false },
  "@opentelemetry/instrumentation-dns": { enabled: false },
  "@opentelemetry/instrumentation-net": { enabled: false },
}

With these disabled, the following spans are auto-instrumented:

  • HTTP server – Express request handling for incoming API calls
  • HTTP client – Outbound http/https requests made by the server
  • PostgreSQL – Database queries through the pg driver (Paperclip's primary data layer)
  • Core Node APIs – Timers, async hooks, and other runtime operations in the default node instrumentation set

This curated set focuses telemetry on request flow and database performance while reducing noise from low-level network operations.

Custom Startup Timing Spans

Beyond auto-instrumentation, Paperclip records internal initialization phases in [packages/adapter-utils/src/acpx-engine/startup-timing.ts](https://github.com/paperclipai/paperclip/blob/master/packages/adapter-utils/src/acpx-engine/startup-timing.ts). This helper creates spans for:

  • Database connection establishment
  • Plugin loading and initialization
  • Other boot-time operations

The wrapper is deliberately typed to a minimal Span subset from @opentelemetry/api, ensuring it compiles and runs even when the full OTEL packages are absent. This design lets plugin code remain telemetry-aware without requiring the SDK as a dependency.

Environment Configuration and Startup

Enable tracing by setting these environment variables before starting the server:

export OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4317"
export OTEL_SERVICE_NAME="paperclip"

# start the server – instrumentation auto-boots

pnpm dev

Without OTEL_EXPORTER_OTLP_ENDPOINT, the instrumentation module skips SDK initialization entirely and uses the no-op tracer.

Manual Span Creation

Application code can create custom spans using the OpenTelemetry API:

import { trace, SpanStatusCode } from "@opentelemetry/api";

export async function doWork() {
  const tracer = trace.getTracer("paperclip-work");
  const span = tracer.startSpan("doWork");

  try {
    // business logic here …
  } catch (err) {
    span.setStatus({ code: SpanStatusCode.ERROR, message: String(err) });
    throw err;
  } finally {
    span.end();
  }
}

The @opentelemetry/api package provides these primitives; they operate as no-ops when the SDK isn't loaded.

Reading Trace Context for Startup Measurements

The startup timing helper demonstrates context-aware span creation:

import { context, trace } from "@opentelemetry/api";

export function measureStartupStep(name: string, fn: () => Promise<void>) {
  const tracer = trace.getTracer("paperclip-startup");
  const span = tracer.startSpan(name, undefined, context.active());
  return fn()
    .catch((e) => {
      span.setStatus({ code: SpanStatusCode.ERROR, message: String(e) });
      throw e;
    })
    .finally(() => span.end());
}

This pattern preserves trace continuity across asynchronous initialization steps.

Test Coverage

The tracing configuration is validated in [server/src/__tests__/instrumentation.test.ts](https://github.com/paperclipai/paperclip/blob/master/server/src/__tests__/instrumentation.test.ts), which confirms:

  • SDK loads without errors when dependencies are present
  • Exporter selection resolves correctly for each protocol
  • Spans can be created, modified, and ended through the full lifecycle

These tests ensure that tracing remains functional across dependency updates and configuration changes.

Summary

  • Optional activation – OpenTelemetry tracing requires both package installation and OTEL_EXPORTER_OTLP_ENDPOINT to be set
  • Central bootstrap – server/src/instrumentation.ts handles SDK initialization, protocol selection, and auto-instrumentation tuning
  • Curated auto-instrumentation – HTTP/Express, PostgreSQL, and core Node APIs are instrumented; fs, DNS, and net are disabled
  • Custom startup spans – packages/adapter-utils/src/acpx-engine/startup-timing.ts provides boot-phase telemetry with minimal API coupling
  • Protocol flexibility – OTLP/gRPC, HTTP/Protobuf, and HTTP/JSON exporters are supported via environment configuration
  • Graceful degradation – No-op tracers allow the application to run without OpenTelemetry installed or configured

Frequently Asked Questions

Does Paperclip require OpenTelemetry to run?

No. Paperclip uses a no-op tracer fallback when @opentelemetry/api is missing or OTEL_EXPORTER_OTLP_ENDPOINT is unset. The server logs a warning and continues normal operation. This design is verified in server/src/__tests__/instrumentation.test.ts.

Which database operations generate spans?

All queries through the pg PostgreSQL driver generate spans via auto-instrumentation. This covers connection acquisition, query execution, and result processing. The instrumentation is provided by @opentelemetry/instrumentation-pg through the auto-instrumentations-node package.

Can I use a different collector protocol than gRPC?

Yes. Set OTEL_EXPORTER_OTLP_ENDPOINT to a URL with the appropriate scheme, or configure the protocol explicitly. Paperclip selects http/protobuf or http/json exporters based on environment detection in instrumentation.ts lines 316–322. The default grpc protocol uses port 4317; HTTP protocols typically use 4318.

How do plugins participate in tracing without depending on the full SDK?

Plugins import a minimal Span interface from packages/plugins/sdk/src/types.ts rather than @opentelemetry/api. The startup timing helper in packages/adapter-utils/src/acpx-engine/startup-timing.ts uses this pattern to create spans that work regardless of whether the OpenTelemetry SDK is present at runtime.

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 →