Observability
Logs, request records, traces, metrics, errors, monitors and dashboards for every project, collected by the platform with no agent and no SDK.
Runlot runs your worker, owns every binding it calls, and knows which commit each deployment came from. So most of what an observability vendor asks you to install is already collected: each request gets an ID, a record and an outcome, the process's memory is read from its cgroup, and env.db, env.storage and env.ai calls become spans on their own.
Open a project and choose Observability. The sections are:
| Section | What it shows |
|---|---|
| Overview | Requests, errors, status classes and latency p50/p95/p99, with deploy and OOM markers |
| Logs | The live tail and stored history of what the worker printed |
| Invocations | One record per request, cron run, queue batch or email; the Query Builder; saved queries; log patterns |
| Traces (APM) | Per-route requests, errors and latency, the latency breakdown, the service map, deployment comparison, the trace explorer |
| Database | env.db statement fingerprints and database health |
| Infrastructure | Memory against the limit, CPU, pressure stall, I/O, restarts, park and wake |
| Metrics | Custom metrics from env.metrics and metrics derived from logs |
| Errors | Runlot Errors: issues grouped from exceptions, crashes and out-of-memory kills |
| Browser | Browser errors, Web Vitals and session replays (Pro) |
| Dashboards | The built-in dashboards and your own |
| Monitors | Monitors, SLOs, downtimes and synthetic checks |
| Settings | Sampling, caps and cost, pipelines and the scanner, export, the profiler, roles and audit |
The request ID
Every request through Runlot gets a 32-character hex ID, returned to the caller in the Runlot-Request-Id response header and passed to your worker on the request. It is also the trace ID. Paste it anywhere a request is asked for:
runlot logs request 4bf92f3577b34da6a3ce929d0e0e4736When Runlot itself answers instead of your app — the process ran out of memory, crashed, or is starting — the error page and the JSON body carry the same ID. An anonymous visitor sees only a generic app_error code. A member of the project's organization who is signed in sees the cause (exceeded its memory limit, a signal name, the deployment version), and a link to the request at /o/<org>/p/<project>/observability/requests/<id>. On a public app the page offers Owner? Sign in to see details.
What you turn on
Request records, metrics, memory, worker exits and server errors are collected for every project with nothing to configure. Two things cost the worker CPU on every request, so they are off until you turn them on: the console lines and exceptions of each request (logs), and its spans (traces).
{
"observability": {
"logs": { "enabled": true, "head_sampling_rate": 1 },
"traces": { "enabled": true, "head_sampling_rate": 0.1 }
}
}The dashboard's Settings can override both without a deploy. See Settings.
Free and Pro
Every plan sees why its app failed. Plans differ in how much is kept, for how long, and how much analysis runs over it.
| Free | Pro | |
|---|---|---|
| Failure causes: out of memory, crashes, platform errors, request IDs, the signed-in cause view | Yes | Yes |
| Logs and request records | 3 days; 200,000 events a day per project, cap fixed | 7 days; 2,000,000 events a day per project, cap liftable |
| Built-in metrics | 15 days | 15 months |
| Query Builder | Yes; 10 saved queries | Unlimited saved queries |
| Runlot Errors (server) | 5,000 events a month, kept 30 days | 100,000 events a month, kept 90 days |
| APM | The top 5 routes only | Full: breakdown, service map, deployment comparison, env.db query monitoring |
| Long-kept traces (errors, slowest 1 %, your filters) | — | 30 days |
| Custom metric series | 100 a month, fixed | 2,000 a month included, cap liftable |
| Custom dashboards | 3 | Unlimited |
| Monitors | 10 | 500 |
| SLOs | 1 | 50 |
| Synthetic checks | 1, every 15 minutes | 20, every minute |
| Watchdog | Deploy regressions | Deploy regressions, seasonal anomalies, new log patterns |
| Browser errors, RUM, session replay | — | Yes (50,000 RUM sessions and 5,000 replays a month included) |
| Continuous profiler, heap snapshots | — | Yes |
| Export (OTLP, Logpush) and rehydration | — | 5 destinations |
| Tail consumers | 1 | 5 |
Pro usage past an included amount is billed only if you raise or remove that cap, and always inside your organization's spending limit:
| Meter | Price past the included amount |
|---|---|
| Log events | $0.60 per million |
| Spans | $0.60 per million |
| Error events | $0.20 per 1,000 |
| Session replays | $3.00 per 1,000 |
| RUM sessions | $0.50 per 1,000 |
| Custom metric series | $0.05 per series per month |
| Synthetic checks | $1.00 per check per month at a 1-minute interval, pro rata |
| Profiler | $0.03 per profiled hour |
| Long-kept traces | $0.10 per GB-month |
See Caps and cost.
Self-hosted
Self-hosted Runlot stores observability data in ClickHouse, and ClickHouse is optional. Without it you keep request IDs, error pages, the live tail, stored log lines (in PostgreSQL), worker exit records and the memory charts. The Query Builder, request records, traces, APM and export need ClickHouse; the dashboard hides what is unavailable rather than showing empty sections.
Pages in this section
- Logs —
runlot logs, live and stored - Invocations and queries — request records, the Query Builder,
runlot logs queryandrunlot logs request - Traces and APM — routes, the service map, deployment comparison, retention filters
- Metrics — built-in, infrastructure, database,
env.metrics, log-to-metrics - Monitors and SLOs — alerts, downtimes, synthetic checks, monitors as code
- Dashboards — custom dashboards, dashboards as code, sharing
- Settings — sampling, caps, pipelines, the scanner, export, the profiler, roles
- Bug reports —
runlot bug-report