name: maple-telemetry-conventions
description: Maple's OpenTelemetry conventions — custom span attribute keys (maple.* vendor namespace, query.context, db.query.*, result.*, cache.*, tenant.*), Title Case status codes (Ok/Error/Unset), resource attribute dual-emit (deployment.environment + deployment.environment.name), span kinds, Tinybird MV pre-extracted columns, loop-prevention filters, and sampling. Use whenever writing or reviewing instrumentation code in any language (TypeScript, Rust, Python) in this repo — adding setAttribute/setAttributes/record/#[instrument(fields(...))] calls, setting span status, configuring an OTLP exporter, defining a new resource attribute, or wiring a new query through WarehouseQueryService.sqlQuery().
version: "1.0.0"
Maple Telemetry Conventions
Reference for the language-agnostic OpenTelemetry conventions Maple uses across TypeScript (apps/api, Cloudflare workers in lib/effect-sdk/), Rust (apps/ingest), and future Python services. These conventions are load-bearing — Tinybird materialized views pre-extract certain attribute keys into columns, dashboards filter on Title Case status strings, and sampling-aware throughput math relies on the SampleRate column. Use the exact attribute spellings here in every language.
When to apply
- Adding
setAttribute/Effect.annotateCurrentSpan/Span::current().record(...)/#[instrument(fields(...))]to any code path - Setting span status (Ok / Error / Unset)
- Wiring a new query through
WarehouseQueryService.sqlQuery()(thecontextandprofileoptions become span attributes) - Configuring an OTLP exporter, tracer provider, or resource builder
- Introducing a new pre-extracted MV column or a new vendor attribute under
maple.* - Reviewing a PR that touches
apps/api/src/services/WarehouseQueryService.ts,apps/ingest/src/main.rs,apps/api/src/app.ts,lib/effect-sdk/src/cloudflare/, orpackages/domain/src/tinybird/materializations.ts
Index
rules/span-attributes.md— Master reference of every custom attribute key Maple emits, grouped by namespace, with file:line citations.rules/status-and-kind.md— Title Case status code rule (Ok/Error/Unset) and span kind conventions (Server/Client/Internal).rules/resource-attributes.md—service.*identity,deployment.environment.nameresolution order, the legacydeployment.environmentdual-emit, andmaple_org_id.rules/language-bindings.md— Parallel TypeScript / Rust / Python snippets that emit the same attribute keys.rules/mv-first-class-columns.md— Which span and resource attributes Tinybird MVs pre-extract into columns (and the rule for adding new ones).rules/service-map-attribution.md— Required span and resource attributes for the service map to render edges, runtime icons, and platform badges. Includes the canonicalpeer.serviceregistry.rules/loop-prevention.md— The three guards that prevent Maple's self-traffic from creating a feedback loop: APITracerDisabledWhen, ingest loopback guard, sampling.
Quick reference
| Topic | Rule |
|---|---|
| Status codes | Always Title Case: "Ok", "Error", "Unset". Never OK, ERROR, SUCCESS, FAILED. |
| Vendor namespace | Custom attributes go under maple.*. Sub-namespaces: maple.ingest.*, maple.cloudflare.*. |
| Standard semconv | Use OTel semconv keys verbatim: service.name, http.request.method, db.system.name, error.type. |
| Org identity | orgId (camelCase) in TypeScript spans, maple.org_id (dotted) in Rust spans. Don't unify until MVs migrate. |
| Deployment env | Dual-emit deployment.environment + deployment.environment.name. Keep both until MV coalesce() migration lands. |
| Warehouse SQL spans | Every span from WarehouseQueryService.executeSql carries db.system.name, peer.service, db.query.text, db.query.fingerprint, db.duration_ms, result.rowCount, orgId, query.context, query.profile. Legacy spans (pre 2026-06) use db.statement*/db.system; warehouse readers coalesce both. |
| Service map | Outbound spans need peer.service (HTTP/RPC) or db.system.name (DB) on a Client/Producer span. Resource attrs need process.runtime.name, cloud.platform, maple.sdk.type for runtime icon + platform badge. See rules/service-map-attribution.md. |
| Loop prevention | Never remove HttpMiddleware.TracerDisabledWhen (apps/api/src/app.ts:169-175) or the ingest loopback guard (apps/ingest/src/main.rs:499-514). |
Canonical references (do not modify from this skill)
apps/api/src/services/WarehouseQueryService.ts:441-510—executeSqlspan emission (the canonical example for TS).apps/ingest/src/otel.rs— Resource builder + platform detection + forward-span helper for Rust. The canonical example for Rust resource and outbound-span attribution.apps/ingest/src/main.rs—handle_signalandhandle_cloudflare_logpush(search fortracing::info_span!withotel.kind = "server") — Server-kind span macros for OTLP inbound.apps/api/src/app.ts:169-175—TracerDisabledWhenfilter.lib/effect-sdk/src/cloudflare/index.ts—MapleCloudflareSDKtracer setup.packages/domain/src/tinybird/materializations.ts— MVSELECTlists that pre-extract attribute keys into columns.