Traces, logs, and metrics on OpenTelemetry. Query billions of rows in milliseconds — or let your AI agent do it.
Maple is the observability backend you'd build if you weren't trying to extract every dollar. OpenTelemetry in. ClickHouse-grade query speed out. The source on GitHub the whole time.
Every request ends up as a span tree with attributes you can actually inspect. Hover any row in this trace to see what was on it.
Structured logs streamed straight from OTLP. Severity, service, message, duration. Searchable in seconds, retained for the window you actually care about.
Live request flow across your services. Hot services light up. The cascade you'd otherwise reconstruct from a postmortem is right here in the foreground.
Maple ships with a first-class MCP server. Any compatible agent can list services, search traces, find errors, propose fixes, open PRs. The transcript on the right is real.
Session replay with every click, route, console line, and failed request captured. Replay and your spans share one session id — so you jump from the moment it broke straight to the trace behind it.
Drop a Helm chart on your cluster. Kubelet, host, and kube-state metrics start flowing in. Every span your apps emit arrives carrying pod, node, and namespace, so a slow request lands you on the exact replica that served it.
maple-k8s-infra is a small Helm chart you can read end-to-end. Kubelet stats per pod, host metrics per node, kube-state metrics across the cluster. OTLP receivers on every node for your apps.
Spans arrive carrying k8s.pod.name, k8s.node.name, and k8s.namespace.name. Drill from a slow trace straight to the pod and node that ran it.
Deployments, StatefulSets, DaemonSets, replica counts, and pod phase, all scraped via the standard kube-state-metrics exporter.
Side-by-side, line-item. The questions your finance team will ask, answered before they have to ask.
| Line item | Maple | Datadog | New Relic | Grafana Cloud |
|---|---|---|---|---|
| Per-host fee | None | $15+ / host / mo | Bundled in plan | Bundled in plan |
| Per-seat fee | None | Enterprise tier | $99 / full-user / mo | $8+ / active-user / mo |
| Ingest pricing | $0.30 / GB ingested | $1.27 / M events | $0.30 / GB | $0.50 / M lines |
| Default retention | 30d default · custom | 15d default | 30d default | 14d default |
| OpenTelemetry support | Native | Partial (custom agent) | Yes | Yes |
| License | FSL-1.1 | Proprietary | Proprietary | AGPL components |
| Self-host option | Supported | No | No | OSS components |
| MCP / agent surface | First-class | No | No | No |
Competitor numbers are public list prices summarized for comparison. Maple ingest rate is the published flat per-GB. See the calculator for projected monthly cost on your volume.
Drop a snippet into your app, paste your key, run. Six SDKs covered out of the box; any OTel-compatible runtime works.
npm install @opentelemetry/sdk-node \
@opentelemetry/auto-instrumentations-node \
@opentelemetry/exporter-trace-otlp-http \
@opentelemetry/exporter-logs-otlp-http npm install @vercel/otel @opentelemetry/sdk-logs @opentelemetry/exporter-logs-otlp-http pip install opentelemetry-sdk \
opentelemetry-exporter-otlp-proto-http \
opentelemetry-instrumentation go get go.opentelemetry.io/otel \
go.opentelemetry.io/otel/sdk \
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp npm install @maple-dev/effect-sdk effect # Use your language's OpenTelemetry SDK
# https://opentelemetry.io/docs/languages/ // tracing.ts — run with: node --import ./tracing.ts app.ts
import { NodeSDK } from "@opentelemetry/sdk-node";
import { getNodeAutoInstrumentations } from "@opentelemetry/auto-instrumentations-node";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-http";
import { SimpleLogRecordProcessor } from "@opentelemetry/sdk-logs";
const sdk = new NodeSDK({
traceExporter: new OTLPTraceExporter({
url: "https://ingest.maple.dev/v1/traces",
headers: { Authorization: "Bearer maple_pk_••••••••" },
}),
logRecordProcessors: [
new SimpleLogRecordProcessor(
new OTLPLogExporter({
url: "https://ingest.maple.dev/v1/logs",
headers: { Authorization: "Bearer maple_pk_••••••••" },
})
),
],
instrumentations: [getNodeAutoInstrumentations()],
});
sdk.start(); // tracing.ts — run with: node --import ./tracing.ts app.ts
import { NodeSDK } from "@opentelemetry/sdk-node";
import { getNodeAutoInstrumentations } from "@opentelemetry/auto-instrumentations-node";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-http";
import { SimpleLogRecordProcessor } from "@opentelemetry/sdk-logs";
const sdk = new NodeSDK({
traceExporter: new OTLPTraceExporter({
url: "https://ingest.maple.dev/v1/traces",
headers: { Authorization: "Bearer maple_pk_••••••••" },
}),
logRecordProcessors: [
new SimpleLogRecordProcessor(
new OTLPLogExporter({
url: "https://ingest.maple.dev/v1/logs",
headers: { Authorization: "Bearer maple_pk_••••••••" },
})
),
],
instrumentations: [getNodeAutoInstrumentations()],
});
sdk.start(); // instrumentation.ts (project root)
import { registerOTel } from "@vercel/otel";
import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-http";
import { SimpleLogRecordProcessor } from "@opentelemetry/sdk-logs";
export function register() {
registerOTel({
serviceName: "my-next-app",
attributes: { environment: "production" },
traceExporter: { url: "https://ingest.maple.dev/v1/traces" },
logRecordProcessor: new SimpleLogRecordProcessor(
new OTLPLogExporter({
url: "https://ingest.maple.dev/v1/logs",
headers: { Authorization: "Bearer maple_pk_••••••••" },
})
),
});
} // instrumentation.ts (project root)
import { registerOTel } from "@vercel/otel";
import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-http";
import { SimpleLogRecordProcessor } from "@opentelemetry/sdk-logs";
export function register() {
registerOTel({
serviceName: "my-next-app",
attributes: { environment: "production" },
traceExporter: { url: "https://ingest.maple.dev/v1/traces" },
logRecordProcessor: new SimpleLogRecordProcessor(
new OTLPLogExporter({
url: "https://ingest.maple.dev/v1/logs",
headers: { Authorization: "Bearer maple_pk_••••••••" },
})
),
});
} # tracing.py
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
provider = TracerProvider()
exporter = OTLPSpanExporter(
endpoint="https://ingest.maple.dev/v1/traces",
headers={"Authorization": "Bearer maple_pk_••••••••"},
)
provider.add_span_processor(BatchSpanProcessor(exporter))
trace.set_tracer_provider(provider)
tracer = trace.get_tracer("quickstart")
with tracer.start_as_current_span("hello-maple"):
print("Trace sent!") # tracing.py
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
provider = TracerProvider()
exporter = OTLPSpanExporter(
endpoint="https://ingest.maple.dev/v1/traces",
headers={"Authorization": "Bearer maple_pk_••••••••"},
)
provider.add_span_processor(BatchSpanProcessor(exporter))
trace.set_tracer_provider(provider)
tracer = trace.get_tracer("quickstart")
with tracer.start_as_current_span("hello-maple"):
print("Trace sent!") package main
import (
"context"
"log"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
"go.opentelemetry.io/otel/sdk/trace"
)
func main() {
ctx := context.Background()
exporter, err := otlptracehttp.New(ctx,
otlptracehttp.WithEndpointURL("https://ingest.maple.dev/v1/traces"),
otlptracehttp.WithHeaders(map[string]string{
"Authorization": "Bearer maple_pk_••••••••",
}),
)
if err != nil {
log.Fatal(err)
}
tp := trace.NewTracerProvider(trace.WithBatcher(exporter))
defer tp.Shutdown(ctx)
otel.SetTracerProvider(tp)
tracer := otel.Tracer("quickstart")
_, span := tracer.Start(ctx, "hello-maple")
span.End()
tp.ForceFlush(ctx)
log.Println("Trace sent!")
} package main
import (
"context"
"log"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
"go.opentelemetry.io/otel/sdk/trace"
)
func main() {
ctx := context.Background()
exporter, err := otlptracehttp.New(ctx,
otlptracehttp.WithEndpointURL("https://ingest.maple.dev/v1/traces"),
otlptracehttp.WithHeaders(map[string]string{
"Authorization": "Bearer maple_pk_••••••••",
}),
)
if err != nil {
log.Fatal(err)
}
tp := trace.NewTracerProvider(trace.WithBatcher(exporter))
defer tp.Shutdown(ctx)
otel.SetTracerProvider(tp)
tracer := otel.Tracer("quickstart")
_, span := tracer.Start(ctx, "hello-maple")
span.End()
tp.ForceFlush(ctx)
log.Println("Trace sent!")
} // telemetry.ts
import { Maple } from "@maple-dev/effect-sdk"
import { Effect } from "effect"
// Auto-detects MAPLE_ENDPOINT, MAPLE_INGEST_KEY,
// commit SHA, and deployment environment from env vars
const TracerLive = Maple.layer({
serviceName: "my-effect-app",
})
const program = Effect.gen(function* () {
yield* Effect.log("Hello from Effect!")
}).pipe(Effect.withSpan("hello-maple"))
Effect.runPromise(
program.pipe(Effect.provide(TracerLive))
) // telemetry.ts
import { Maple } from "@maple-dev/effect-sdk"
import { Effect } from "effect"
// Auto-detects MAPLE_ENDPOINT, MAPLE_INGEST_KEY,
// commit SHA, and deployment environment from env vars
const TracerLive = Maple.layer({
serviceName: "my-effect-app",
})
const program = Effect.gen(function* () {
yield* Effect.log("Hello from Effect!")
}).pipe(Effect.withSpan("hello-maple"))
Effect.runPromise(
program.pipe(Effect.provide(TracerLive))
) # Configure via environment variables — works with any OTel SDK
export OTEL_EXPORTER_OTLP_ENDPOINT="https://ingest.maple.dev"
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer maple_pk_••••••••"
export OTEL_SERVICE_NAME="my-service"
# Then run your application with your language's OTel SDK enabled # Configure via environment variables — works with any OTel SDK
export OTEL_EXPORTER_OTLP_ENDPOINT="https://ingest.maple.dev"
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer maple_pk_••••••••"
export OTEL_SERVICE_NAME="my-service"
# Then run your application with your language's OTel SDK enabled Java, Rust, .NET, Ruby — anything that speaks OTLP works. The custom tab shows the env-var-only setup.
14-day trial on Starter. Self-host is supported. The dashboard ships with the same live waterfall you just watched.