specific query to run SQL against this data when debugging production incidents, investigating regressions, or doing ad-hoc analytics.
Running a query
| Flag | Description |
|---|---|
-e, --environment <name|id> | Target environment by name or ID (defaults to the current one). Run specific status to list environments. |
--db <name> | Run the query against one of the environment’s Postgres databases (read-only) instead of the observability store. The SQL is standard Postgres, and the schema is your application’s tables; see Postgres. |
--format <table|json|jsonl|csv> | Output format. Defaults to table on a terminal, jsonl when piped. |
INSERT, UPDATE, DELETE, and DDL are rejected. Each query is capped at 30 seconds of execution time, returns at most 100,000 rows, and the SQL string is limited to 50,000 characters.
The table format truncates wide values for readability. Agents and scripts should use jsonl, json, or csv for complete, untruncated values. Notices are printed to stderr so stdout remains parseable.
Choosing an environment
specific query runs against exactly one environment. Run specific status to discover every environment and its ID. The output has two sections:
- Environments - long-lived environments such as
productionandstaging. - Preview environments - ephemeral environments created for pull requests (plus any manually created previews), each with its name,
env_...ID, PR number and title, deployed URL, and expiry.
--environment; that’s the only way to reach a preview’s data, since queries are auto-scoped:
Schema
Two views are exposed in theobservability database. Column names follow OpenTelemetry’s ClickHouse exporter conventions and are case-sensitive, so use SeverityText, ServiceName, and ResourceAttributes exactly as shown. If unsure, run DESCRIBE observability.logs.
observability.logs
Unified log stream from services. Retention: up to 30 days (your plan may restrict how far back queries can read; a cutoff prints a note on stderr).| Column | Type | Notes |
|---|---|---|
Timestamp | DateTime64(9) | When the log was emitted; supports time arithmetic (Timestamp > now() - INTERVAL 1 HOUR). |
ServiceName | String | Service that emitted the log. |
SeverityText | String | |
SeverityNumber | UInt8 | |
Body | String | The log message. |
LogAttributes | Map(String, String) | Per-event attributes. |
ResourceAttributes | Map(String, String) | Resource labels (for example, service.name, deployment.environment.name). |
TraceId | String | Distributed-tracing trace ID, if present. |
SpanId | String | Distributed-tracing span ID, if present. |
EnvironmentId, ProjectId (used for auto-scoping), TraceFlags.
observability.metrics
Unified metrics from services. Retention: up to 90 days.| Column | Type | Notes |
|---|---|---|
TimeUnix | DateTime64(9) | Measurement timestamp; supports time arithmetic. |
MetricName | String | For example, container.cpu.time (see below). |
MetricType | String | 'gauge' or 'sum'. |
Value | Float64 | Measured value. |
ServiceName | String | Service that emitted the metric. |
Attributes | Map(String, String) | Metric dimensions. |
ResourceAttributes | Map(String, String) | Resource labels. |
Service metrics
Emitted for every running service container:| Metric | Type | Notes |
|---|---|---|
container.cpu.time | sum | Cumulative CPU seconds; rate it for utilization. |
container.cpu.usage | gauge | Current CPU usage in cores. |
container.cpu.limit_utilization | gauge | Fraction of CPU limit used (0–1). |
container.memory.working_set | gauge | Memory in active use (bytes), the right number for “is this service close to OOM”. |
container.memory.usage | gauge | Total memory usage including page cache (bytes). |
container.memory.available | gauge | Memory available before the container limit (bytes). |
k8s.volume.available | gauge | Free space on attached volumes (bytes). |
k8s.volume.capacity | gauge | Total capacity of attached volumes (bytes). |
Debugging recipes
Recent errors for a service:Performance
Both views are partitioned and ordered to make environment-scoped, time-bounded queries fast. To stay within the 30-second limit:- Always filter by time -
Timestamp >=for logs,TimeUnix >=for metrics. Unbounded scans across 30/90 days will time out. - Add
ServiceNameand/orMetricNameto narrow the partition whenever you can. - Select only the columns you need -
Body,LogAttributes, andResourceAttributesare the heavyweight columns. LIMITexploratory queries while shaping them.
Discovering data
When you don’t know what’s there, ask the database:ClickHouse dialect
The observability store is ClickHouse, sospecific query without --db uses the ClickHouse SQL dialect (unlike --db, which is standard Postgres). Commonly needed functions:
now(),now64()- current timetoStartOfMinute(t),toStartOfFiveMinutes(t),toStartOfHour(t)- bucket timestampspositionCaseInsensitive(haystack, needle)- case-insensitive substring searchJSONExtractString(s, 'field'),JSONExtractInt,JSONExtractFloat- pull fields out of JSON log bodiesLogAttributes['key'],Attributes['key'],ResourceAttributes['key']- read map columns