Agent SkillsMapleTechLabs/maple › maple-effect-style

maple-effect-style

GitHub

Maple Effect SDK 集成指南,提供 OpenTelemetry 追踪、日志与 Span 管理。支持 Node/Bun/Deno Server、Browser 及 Cloudflare Workers 多运行时环境,涵盖 Bootstrap 配置、自动属性填充及优雅关闭机制。

skills/maple-effect-style/SKILL.md MapleTechLabs/maple

Trigger Scenarios

需要为基于 Effect-TS 的应用程序集成分布式追踪和日志记录 询问如何在 Maple 框架中配置 OpenTelemetry 导出器或处理不同运行时的生命周期

Install

npx skills add MapleTechLabs/maple --skill maple-effect-style -g -y
More Options

Use without installing

npx skills use MapleTechLabs/maple@maple-effect-style

指定 Agent (Claude Code)

npx skills add MapleTechLabs/maple --skill maple-effect-style -a claude-code -g -y

安装 repo 全部 skill

npx skills add MapleTechLabs/maple --all -g -y

预览 repo 内 skill

npx skills add MapleTechLabs/maple --list

SKILL.md

Frontmatter
{
    "name": "maple-effect-style",
    "description": "Effect-TS OpenTelemetry style for Maple via @maple-dev\/effect-sdk: Maple.layer() bootstrap, Effect.withSpan \/ Effect.annotateCurrentSpan call sites, Effect.log for trace-correlated logging, server \/ browser \/ Cloudflare entry points."
}

Maple Effect style

For Effect apps, use @maple-dev/effect-sdk — Maple's first-class Effect SDK. It wraps Effect's built-in Otlp.layerJson exporter and handles batching, shutdown, and resource attributes for you.

Install

npm install @maple-dev/effect-sdk effect

For Effect 3, use @maple-dev/effect-sdk@effect-v3 instead. Same API, different peer ranges.

Bootstrap

Pick the entry point per runtime — they have different lifecycle requirements:

  • Server (Node.js, Bun, Deno): background-export fiber, env-var auto-detection, graceful shutdown.
  • Browser: explicit config (no env vars), browser metadata baked in.
  • Cloudflare Workers: manual flush() in ctx.waitUntil, lazy env resolution, in-isolate buffering.

Server

import { Maple } from "@maple-dev/effect-sdk"
import { Effect, Layer } from "effect"

const TracerLive = Maple.layer({
	serviceName: "orders-api",
	endpoint: "https://ingest.maple.dev",
	ingestKey: "MAPLE_TEST", // set by maple-onboard skill on pairing
	attributes: {
		"vcs.repository.url.full": "https://github.com/acme/orders-api",
	},
})

const program = Effect.gen(function* () {
	yield* Effect.log("Order received")
}).pipe(Effect.withSpan("order.submit"))

Effect.runPromise(program.pipe(Effect.provide(TracerLive)))

The default import resolves to the server build under Node.js. Import @maple-dev/effect-sdk/server explicitly when needed.

If endpoint is omitted, the server layer auto-detects it from MAPLE_ENDPOINT (falling back to OTEL_EXPORTER_OTLP_ENDPOINT, then the public Maple ingest). Maple.layer always exports — a missing ingest key does not disable it, which is what keeps keyless local-mode and self-hosted-collector setups working. Inline the key when you want telemetry to flow regardless of env (matches the rest of the maple-onboard inline-key pattern). MapleFlush.make and the Cloudflare make() differ: those no-op without a key.

The server layer also auto-fills vcs.ref.head.revision from COMMIT_SHA / RAILWAY_GIT_COMMIT_SHA / VERCEL_GIT_COMMIT_SHA / CF_PAGES_COMMIT_SHA / RENDER_GIT_COMMIT (first match wins). For vcs.repository.url.full, use the repositoryUrl option or MAPLE_REPOSITORY_URL — don't hand-write the attribute.

Cloudflare Workers

The Cloudflare entry point exports make(), not a Maple namespace. Build the telemetry object once at module scope — it buffers in-isolate and resolves env lazily on the first flush:

import * as MapleCloudflareSDK from "@maple-dev/effect-sdk/cloudflare"
import { Effect } from "effect"

const telemetry = MapleCloudflareSDK.make({
	serviceName: "orders-edge",
	endpoint: "https://ingest.maple.dev",
	ingestKey: "MAPLE_TEST",
})

export default {
	async fetch(req: Request, env: Env, ctx: ExecutionContext) {
		const program = Effect.gen(function* () {
			yield* Effect.log("edge request")
			return new Response("ok")
		}).pipe(Effect.withSpan("edge.handle"))

		const response = await Effect.runPromise(program.pipe(Effect.provide(telemetry.layer)))
		ctx.waitUntil(telemetry.flush(env))
		return response
	},
}

The Cloudflare entry point requires ctx.waitUntil(telemetry.flush(env)) so telemetry survives the isolate exit — note flush takes env. Forgetting the waitUntil is the most common reason traces don't show up from Workers.

Browser

import { Maple } from "@maple-dev/effect-sdk/client"

const TracerLive = Maple.layer({
	serviceName: "web-client",
	endpoint: "https://ingest.maple.dev",
	ingestKey: "MAPLE_TEST",
})

No env-var fallback in the browser entry point — config is always explicit.

Custom spans

Use Effect.withSpan to trace operations and Effect.annotateCurrentSpan for attributes — don't reach for the raw @opentelemetry/api tracer when an Effect-native primitive is available.

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("order.process"))

Setting peer.service on outgoing calls makes them visible on Maple's service map.

Effect.fail and uncaught defects are recorded as exceptions and set the span status to ERROR automatically — you don't need to wrap with try / catch / finally.

@maple/otel-helpers withSpan is for non-Effect TypeScript code; in Effect code prefer the Effect-native span primitives.

Logs

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.

Coexistence

If the project already uses @effect/opentelemetry or Otlp.layerJson with a custom exporter (e.g. for Honeycomb, Datadog), keep it. Maple.layer() can compose alongside via Layer.merge:

const TracerLive = Layer.merge(
	Maple.layer({ serviceName: "api", endpoint: "https://ingest.maple.dev", ingestKey: "MAPLE_TEST" }),
	HoneycombLayer,
)

Both vendors receive the same spans.

Version History

  • b9cc9f8 Current 2026-08-19 16:20

    修复了 ingest key 缺失时错误发送请求的问题,解决了 flush() 拒绝被缓存导致的未处理拒绝异常,更新了用户代理版本显示,并修正了文档中的错误示例和环境变量说明。

  • 01a5dc6 2026-07-05 18:16

Same Skill Collection

.agents/skills/clickhouse-architecture-advisor/SKILL.md
.agents/skills/clickhouse-best-practices/SKILL.md
.agents/skills/clickhousectl-cloud-deploy/SKILL.md
.agents/skills/clickhousectl-local-dev/SKILL.md
.agents/skills/coss-particles/SKILL.md
.agents/skills/coss/SKILL.md
.agents/skills/react-doctor/SKILL.md
.agents/skills/tinybird-cli-guidelines/SKILL.md
.agents/skills/tinybird-python-sdk-guidelines/SKILL.md
.agents/skills/tinybird-typescript-sdk-guidelines/SKILL.md
.agents/skills/tinybird/SKILL.md
.context/effect/.agents/skills/grill-me/SKILL.md
.context/effect/.agents/skills/jsdocs/SKILL.md
.context/effect/.agents/skills/scratchpad/SKILL.md
.factory/skills/react-doctor/SKILL.md
apps/slack-agent/agent/skills/dashboard-builder/SKILL.md
apps/slack-agent/agent/skills/incident-investigation/SKILL.md
skills/maple-audit/SKILL.md
skills/maple-csharp-style/SKILL.md
skills/maple-go-style/SKILL.md
skills/maple-java-style/SKILL.md
skills/maple-kotlin-style/SKILL.md
skills/maple-nextjs-style/SKILL.md
skills/maple-nodejs-style/SKILL.md
skills/maple-onboard/SKILL.md
skills/maple-onboarding-style/SKILL.md
skills/maple-python-style/SKILL.md
skills/maple-rust-style/SKILL.md
.agents/skills/chdb-datastore/SKILL.md
.agents/skills/chdb-sql/SKILL.md
.agents/skills/maple-telemetry-conventions/SKILL.md
.agents/skills/onboarding-cro/SKILL.md
skills/maple-dashboard-widgets/SKILL.md
skills/maple-otel-spec-review/SKILL.md

Metadata

Files
0
Version
b9cc9f8
Hash
d2686b70
Indexed
2026-07-05 18:16

Accueil - Wiki
Copyright © 2011-2026 iteam. Current version is 2.155.2. UTC+08:00, 2026-08-26 18:46
浙ICP备14020137号-1 $Carte des visiteurs$