Quick start
OpenTelemetry traces, logs, and metrics for Effect applications across Node.js, Bun, Deno, browsers, and Cloudflare Workers.
@maple-dev/effect-sdk provides a pre-configured Effect Layer that sets up OpenTelemetry traces, logs, and metrics for Maple. It wraps Effect’s built-in Otlp.layerJson exporter, fills in resource attributes from the runtime, and defaults the endpoint to the public Maple ingest — so in most cases an ingest key is the only configuration you need. Point MAPLE_ENDPOINT at a local maple start sink or your own collector to run the same code against a different destination.
Install
Effect 4+
npm install @maple-dev/effect-sdk effect
Effect 3
npm install @maple-dev/effect-sdk@effect-v3 effect @effect/platform @effect/opentelemetry
The API and import paths are identical between versions. The only differences are the install command and that duration config types use
Duration.DurationInputinstead ofDuration.Inputin Effect 3.
Pick your platform
The SDK ships three platform-specific entry points. Each one has its own setup story:
- Server — Node.js, Bun, Deno. Background-export fiber, env-var auto-detection, graceful shutdown.
- Browser — single-page apps. Explicit config (no env vars), browser metadata baked into resource attributes.
- Cloudflare Workers — short-lived isolates. Manual
flush()inctx.waitUntil, lazy env resolution, in-isolate buffering.
Custom Spans
Use Effect.withSpan to trace operations. Add attributes with Effect.annotateCurrentSpan:
import { Effect } from "effect"
const processOrder = (orderId: string) =>
Effect.gen(function* () {
yield* Effect.annotateCurrentSpan("order.id", orderId)
yield* Effect.annotateCurrentSpan("peer.service", "payment-api")
const result = yield* chargePayment(orderId)
return result
}).pipe(Effect.withSpan("process-order"))
Setting peer.service on outgoing calls makes them visible on Maple’s service map.
Log Correlation
Effect.log automatically includes trace context when called inside a span — no additional setup needed:
const program = Effect.gen(function* () {
yield* Effect.log("Processing started")
yield* doWork()
yield* Effect.log("Processing complete")
}).pipe(Effect.withSpan("process"))
Logs emitted inside spans are correlated with the active trace in the Maple dashboard.
Configuration Reference
All options for Maple.layer() (server and browser entry points). The Cloudflare entry point accepts a slightly different shape — see the Cloudflare page for its config table.
| Option | Type | Required | Description |
|---|---|---|---|
serviceName | string | Yes | Service name reported in traces, logs, and metrics |
endpoint | string | No (server) / Yes (client) | Maple ingest endpoint URL. Server auto-detects from MAPLE_ENDPOINT |
ingestKey | string | Required by the public ingest | Maple ingest key. Server auto-detects from MAPLE_INGEST_KEY. The flushable and Cloudflare presets no-op without one |
serviceVersion | string | No | Override auto-detected commit SHA |
serviceNamespace | string | No | Logical group, emitted as the service.namespace resource attribute |
repositoryUrl | string | No (server / Cloudflare) | Repository URL, emitted as vcs.repository.url.full |
environment | string | No | Override auto-detected deployment environment |
attributes | Record<string, unknown> | No | Additional resource attributes merged into telemetry |
maxBatchSize | number | No | Max telemetry items per export batch |
loggerExportInterval | Duration.Input | No | Export interval for logs |
metricsExportInterval | Duration.Input | No | Export interval for metrics |
tracerExportInterval | Duration.Input | No | Export interval for traces |
shutdownTimeout | Duration.Input | No | Graceful shutdown timeout |
In Effect 3, duration fields use the
Duration.DurationInputtype instead ofDuration.Input.
Browser-only options — replay (built-in rrweb session replay, on by default) and emitSessionMeta — are documented in Session Replay & Sessions on the Browser page.