App
Effect SDK / Quick start

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 returns a no-op layer when no endpoint is configured — so the same code runs locally without exporting telemetry.

Node.js Bun Deno Browsers Cloudflare Workers

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.DurationInput instead of Duration.Input in 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() in ctx.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.

OptionTypeRequiredDescription
serviceNamestringYesService name reported in traces, logs, and metrics
endpointstringNo (server) / Yes (client)Maple ingest endpoint URL. Server auto-detects from MAPLE_ENDPOINT
ingestKeystringNoMaple ingest key. Server auto-detects from MAPLE_INGEST_KEY
serviceVersionstringNoOverride auto-detected commit SHA
environmentstringNoOverride auto-detected deployment environment
attributesRecord<string, unknown>NoAdditional resource attributes merged into telemetry
maxBatchSizenumberNoMax telemetry items per export batch
loggerExportIntervalDuration.InputNoExport interval for logs
metricsExportIntervalDuration.InputNoExport interval for metrics
tracerExportIntervalDuration.InputNoExport interval for traces
shutdownTimeoutDuration.InputNoGraceful shutdown timeout

In Effect 3, duration fields use the Duration.DurationInput type instead of Duration.Input.