Setup OTel
Use this skill to bootstrap OpenTelemetry tracing for an Effect repository from scratch.
This skill owns installation and baseline runtime wiring.
Instrumentation strategy belongs in observability-tracing-otel.
When To Use
- •The user asks to enable tracing or telemetry.
- •No OTEL dependencies are present.
- •No exporter endpoint or collector configuration exists.
- •Existing traces are missing, incomplete, or uncorrelated with logs.
Only run this setup when the user requests telemetry installation or repair.
Setup Goals
- •Emit spans from Effect programs.
- •Export spans to a backend through OTLP.
- •Keep telemetry provisioning at one application boundary.
- •Enable a reproducible local validation workflow.
Prerequisites
- •Confirm runtime target (
Node,Bun, worker, browser). - •Confirm export destination (local collector, vendor backend, OTLP endpoint).
- •Confirm service identity naming (
service.name, environment, version). - •Confirm the user approved dependency and environment changes.
Step 1: Install Dependencies
Start with the same package set used in Effect tracing examples.
Recommended baseline (adjust to runtime requirements):
bun add @effect/opentelemetry @opentelemetry/api @opentelemetry/sdk-trace-base @opentelemetry/sdk-trace-node @opentelemetry/exporter-trace-otlp-http
Optional for early smoke tests:
- •
@opentelemetry/sdk-trace-webfor browser targets - •
@opentelemetry/sdk-metricswhen metrics export is also requested
Step 2: Add Telemetry Environment Fields
Follow repository rules: do not read process.env directly in new code.
- •Add fields to
src/env.ts(AppEnv). - •Add corresponding entries to
.env.example. - •Use
AppEnvvalues when building telemetry configuration.
Suggested fields:
- •
otelServiceName - •
otelExporterOtlpEndpoint - •
otelTracesSampler - •
otelTracesSamplerArg
Step 3: Create A Telemetry Layer
Create a dedicated module such as src/telemetry.ts and keep setup at the boundary.
Console exporter variant (smoke-test friendly):
import { NodeSdk } from "@effect/opentelemetry";
import { BatchSpanProcessor } from "@opentelemetry/sdk-trace-base";
import { ConsoleSpanExporter } from "@opentelemetry/sdk-trace-base";
export const NodeSdkLive = NodeSdk.layer(() => ({
resource: {
serviceName: "example-service",
},
spanProcessor: new BatchSpanProcessor(new ConsoleSpanExporter()),
}));
OTLP exporter variant (backend integration):
import { NodeSdk } from "@effect/opentelemetry";
import { BatchSpanProcessor } from "@opentelemetry/sdk-trace-base";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
export const NodeSdkLive = NodeSdk.layer(() => ({
resource: {
serviceName: "example-service",
},
spanProcessor: new BatchSpanProcessor(
new OTLPTraceExporter({
url: "http://localhost:4318/v1/traces",
}),
),
}));
Step 4: Provide Telemetry At One Boundary
Merge/provide telemetry once at bootstrap.
Do not scatter Effect.provide(...) calls throughout domain modules.
import { Effect, Layer } from "effect";
const RuntimeLayer = Layer.mergeAll(AppEnv.Default, NodeSdkLive, AppLayer);
const runnable = program.pipe(
Effect.provide(RuntimeLayer),
);
Step 5: Validate End-To-End
- •Add one small span in application code with
Effect.withSpan(...). - •Trigger one request/job path.
- •Confirm spans arrive in the configured exporter/backend.
- •Confirm parent-child hierarchy and timing are plausible.
Local smoke check using console exporter:
- •Run app and inspect emitted spans in terminal output.
- •Verify span names and attributes match operation names.
Local visualization check using OTLP backend:
- •Start a local OTLP-compatible stack (e.g. Grafana Tempo + Grafana).
- •Send traces to OTLP endpoint.
- •Query and inspect trace waterfall, durations, and nesting.
Step 6: Set Initial Sampling Policy
Recommended default:
- •Development: near 100% sampling for debugging.
- •Production: lower ratio with parent-based sampler.
- •Incident windows: temporarily increase sampling for affected paths.
Handoff
- •After setup is complete, use
observability-tracing-otelfor span architecture and instrumentation. - •Use
observability-wide-eventsfor wide structured event contracts.
Troubleshooting
- •No spans exported:
- •Confirm exporter URL, protocol, and network reachability.
- •Confirm telemetry layer is provided at runtime bootstrap.
- •Confirm service executes code paths wrapped by spans.
- •Spans exist but no hierarchy:
- •Check context propagation across async and concurrency boundaries.
- •Ensure nested effects are wrapped with child spans intentionally.
- •High telemetry cost/noise:
- •Reduce span volume and tune sampling rate.
- •Sensitive data leaks:
- •Remove raw payloads and secrets from attributes/log fields.
- •Missing trace/log correlation:
- •Align span attributes and log keys (
operation, IDs, status). - •Ensure logging includes trace context where supported.
- •Align span attributes and log keys (
Verification Checklist
- •Dependencies installed and locked.
- •Telemetry env fields routed through
AppEnv. - •Telemetry layer exists and is provided once at app boundary.
- •At least one trace visible in console or backend.
- •Trace hierarchy and durations look correct.
- •Sensitive fields are not exported.
References
- •https://effect.website/docs/observability/tracing/
- •https://effect-ts.github.io/effect/docs/opentelemetry
- •https://effect-ts.github.io/effect/effect/Tracer.ts.html
- •https://opentelemetry.io/docs/specs/otel/trace/api/
- •https://opentelemetry.io/docs/specs/semconv/
- •https://opentelemetry.io/docs/languages/js/getting-started/nodejs/
- •https://www.npmjs.com/package/@effect/opentelemetry