← All posts

Logs, metrics and traces, built in

product observability

Two weeks ago, in the post about our tagline, we listed what was still a proof of concept here, and monitoring came first: “you read your app’s logs with kubectl on its own cluster.” That line is out of date. Every project on Basable now has its logs, metrics and traces collected from the moment it runs, alerts when something goes wrong, and Grafana to look at all of it. There is nothing to install and nothing to configure.

Logs, from the first line

Everything every container writes to stdout and stderr is collected and kept for seven days. That includes containers that have since crashed and restarted, which is usually where the line you need is, and environments that are asleep. Open an environment’s Logs in the editor, narrow them to a time range, a workload or a container, or search for a word. A JSON line with a level field, or a line with a level word such as ERROR, gets its level. A stack trace spread over several lines can be stitched back together with a parser in the pod’s annotations; the help page has the recipe.

Metrics, without code

Every container’s CPU, memory, network and file system, its restarts, why it is waiting (CrashLoopBackOff, ImagePullBackOff) and why it last ended (OOMKilled), every 30 seconds, kept for fifteen days. Every request that reaches your app through the platform’s gateway is counted as well, per route and status class, with the time your app took to answer. The editor’s Metrics panel shows each route’s requests per second, its server errors and its 95th percentile response time, and how full each volume is.

Metrics of your own come in two ways: a container that serves the Prometheus format is scraped once its pod opts the port in, or an OpenTelemetry SDK pushes them. A project keeps up to 10,000 series.

Traces, from the gateway to your database

Every request through the gateway starts a trace, and the gateway hands it to your app in the traceparent header. Every container already has OTEL_EXPORTER_OTLP_ENDPOINT set, so any OpenTelemetry SDK sends its spans without an address to configure, and the trace continues from the edge through your services down to the database query. A log line that carries its trace id gets a Trace button that opens the whole request as a waterfall. Projects on Basable’s Rust framework (basable-app 0.8.0 and later) start the SDK by themselves. Traces are kept for seven days.

Grafana, with your Basable account

All of it is also in Grafana, at grafana.basable.com. You sign in with your Basable account. Each of your organisations is an organisation there, holding its own projects and nobody else’s, and a Basable folder has every project’s overview, logs and traces, with room for dashboards of your own beside them. The Grafana button in the editor opens the project’s overview with the project already chosen.

Grafana holds no key to the stores. Every query it makes goes through Basable, which decides again, query by query, what your account may read. Leave an organisation and its projects disappear from Grafana within a minute.

Alerts

Basable watches every live environment for four things: a container that keeps crashing, one that runs out of memory and is restarted, a route where more than half of the requests end in a server error, and a volume more than 90% full. Mail is off until you turn it on, per environment, and the same block in .basable/config.yaml takes rules of your own, in PromQL, with the threshold in the query:

spec:
  environments:
    live:
      alerts:
        email: {enabled: true}
        rules:
          - name: queue-backlog
            query: jobs_waiting > 1000

A rule the metrics store cannot parse fails the deploy and names the rule, so a typo does not sit there silently. Alerts can also go to your app as a webhook, on the environment’s own address and nowhere else, signed with an Ed25519 key whose public half is published. From there your app forwards them to a chat or a pager.

Your agent reads them too

The assistant in the editor reads an environment’s logs, metrics and traces while it works on the project, so “the deploy is green but the page shows an error” is something it can look into instead of something you paste into the chat. An agent you connect over MCP, such as Claude Code or Cursor, has the same tools: get_logs, query_metrics, list_metrics, search_traces, get_trace and list_alerts. Each answer ends with a link that opens the same query in Grafana, so you can see what your agent saw.

Kept apart, including from us

Each project is a tenant of its own in the log, metric and trace stores. What a container sends is filed under the project and pod that sent it, whatever the data claims about itself, and a query, yours or your agent’s, only ever reads its own project. A request’s trace keeps no visitor IP address and no query string.

Basable’s staff have no standing access to any of it. When we need to look at a project, to answer a support mail for instance, we open a read grant on that one project, with a reason, for one hour. Every grant is listed on the organisation’s Staff access page with its reason, and the project’s page shows a notice while one is open.

What is not there yet

Retention is fixed: seven days of logs and traces, fifteen days of metrics, and no way yet to keep more. There is no error tracker that groups exceptions for you, and no check from outside that your site answers. Alerts go by mail or to your own app; Slack or a pager is reached through that webhook, not directly. And the security audit logs mentioned in the environment post, what the cluster itself saw, are a separate piece of work that is still open.

Sign up with just your email, deploy something, and open its Logs: the first lines will already be there.