clickhouse-logs-queries

作者: supabase

Write, review, and migrate Supabase logs queries against the ClickHouse-backed `logs` table (the `logs.all.otel` analytics endpoint). Use this whenever a task…

npx skills add https://github.com/supabase/supabase --skill clickhouse-logs-queries

Querying Supabase logs (ClickHouse)

Supabase logs live in a single ClickHouse logs table, served by the logs.all.otel analytics endpoint. Every log line from every part of the stack is one row in this table, tagged by a source column. This replaces the older BigQuery model, where each service had its own table and fields were reached through cross join unnest(metadata).

Two kinds of work use this skill, and they share the same SQL model:

  1. Writing or reviewing a logs query (in the Logs Explorer or anywhere a raw ClickHouse logs query is needed). Start here in this file.
  2. Wiring a logs query in the Studio codebase (branded analytics SQL, the endpoint picker, the OTEL query builders). Read references/codebase-integration.md.

If you are converting an existing BigQuery logs query, read references/bigquery-migration.md for the full translation table.

The logs table

Each row has a small set of real columns. Everything specific to a service lives in log_attributes.

ColumnTypeNotes
idStringUnique log identifier.
timestampDateTime64 (UTC)When the log was produced. Order/compare it directly.
event_messageStringThe raw log line.
severity_textStringLog level, when the source sets one.
sourceStringThe service the log came from. Always filter on this.
log_attributesMap(String, String)Structured per-source fields, keyed by a dotted path.

timestamp is formatted like 2026-06-22T09:34:06.215000 (ISO 8601, microsecond precision, no trailing Z). In the Logs Explorer the selected time range is applied for you, so you rarely need to write a timestamp filter by hand.

A minimal, well-formed query. Lead with a comment naming the query, filter by source, and always limit:

-- recent edge requests
select timestamp, event_message
from logs
where source = 'edge_logs'
order by timestamp desc
limit 100;

Sources

source selects the service. The common ones:

  • edge_logs — API gateway requests and responses
  • postgres_logs — database statements and errors (also where pg_cron logs live)
  • auth_logs — authentication and authorization activity
  • function_edge_logs — edge function requests and responses
  • function_logsconsole output from inside edge functions
  • storage_logs — object upload and retrieval activity
  • realtime_logs — Realtime client connections
  • postgrest_logs, supavisor_logs, pgbouncer_logs — mostly id, timestamp, event_message

The Logs Explorer Field Reference drawer lists every source and the fields it actually sets. When in doubt about a key, discover it from real data rather than guessing (see below).

Reading fields from log_attributes

log_attributes maps a string key to a string value. Read a field with bracket access. There are no unnesting joins:

select
  log_attributes['request.method'] as method,
  log_attributes['request.path'] as path,
  log_attributes['response.status_code'] as status
from logs
where source = 'edge_logs'

The key keeps the dotted path that BigQuery expressed through nested structs, with the metadata root dropped: BigQuery metadata.request.method becomes log_attributes['request.method']. Keep the full prefix — request.cf.country is log_attributes['request.cf.country'], not log_attributes['cf.country'].

Common keys by source:

  • edge_logs: request.method, request.path, request.search, response.status_code, identifier
  • postgres_logs: parsed.error_severity, parsed.detail, parsed.hint, parsed.query, identifier
  • auth_logs: level, status, path, msg, error
  • function_edge_logs: response.status_code, request.method, request.pathname, function_id, execution_id, execution_time_ms
  • function_logs: event_type, function_id, execution_id, level

Numeric fields are strings

Map values are always strings. To compare or aggregate a numeric field, wrap it in toInt32OrZero, which returns 0 for missing or non-numeric values so it never errors on partial data:

select count() as server_errors
from logs
where source = 'edge_logs'
  and toInt32OrZero(log_attributes['response.status_code']) between 500 and 599

Discover the keys a source sets

Read mapKeys from recent rows rather than guessing key names:

select arrayJoin(mapKeys(log_attributes)) as key, count() as n
from logs
where source = 'postgres_logs'
group by key
order by n desc
limit 100;

arrayJoin(mapKeys(...)) flattens the map keys into one row per key so you can rank them by frequency. (The Studio codebase does exactly this for the Field Reference drawer and to feed real keys to the AI rewrite.)

ClickHouse vs BigQuery functions

These are the substitutions that trip people up most:

NeedBigQueryClickHouse
Count rowscount(*)count()
Regex matchregexp_contains(x, 'p')match(x, 'p')
Substring matchx like '%p%'x ilike '%p%' (case-insensitive) or like
Numeric coercioncast(x as int64)toInt32OrZero(x)
Read the timestampcast(timestamp as datetime)timestamp (use the column directly)
Map keysn/a (used unnest)mapKeys(log_attributes)

The logs.all.otel analytics endpoint (and the Logs Explorer on top of it) rejects count(*) and select * — use count() and list the columns you need. (Raw ClickHouse supports both; this is a constraint of the logs query surface.)

Best practices

These keep queries correct and cheap. Log tables are large; an unbounded scan reads far more data than you need.

  • Start every query with an identifying comment (e.g. -- errors since last deploy). It labels the query in logs and review, and makes each of several queries in a file easy to tell apart.
  • Always include a LIMIT. Even for aggregates while you iterate.
  • Always query from logs where source = '...'. There is no per-service table (no edge_logs, postgres_logs, etc. table) — there is one logs table, and source scopes it to a service. Filtering by source is required, not just an optimization.
  • Keep the time range tight. A smaller window returns results faster.
  • Filter on the real columns (source, timestamp) before reaching into log_attributes.
  • Order by timestamp desc to see the most recent logs first.
  • Use count(), not count(*) or select *.

Worked examples

Requests by status code:

select
  toInt32OrZero(log_attributes['response.status_code']) as status,
  count() as count
from logs
where source = 'edge_logs'
group by status
order by count desc
limit 50

Auth errors:

select timestamp, event_message, log_attributes['msg'] as message
from logs
where source = 'auth_logs'
  and log_attributes['level'] in ('error', 'fatal')
order by timestamp desc
limit 100

Search the raw message:

select timestamp, event_message
from logs
where source = 'postgres_logs'
  and event_message ilike '%deadlock%'
order by timestamp desc
limit 100

Postgres errors grouped by severity (the canonical unnest-to-map conversion):

select log_attributes['parsed.error_severity'] as severity, count() as count
from logs
where source = 'postgres_logs'
  and log_attributes['parsed.error_severity'] in ('ERROR', 'FATAL', 'PANIC')
group by severity
order by count desc
limit 100

When the user pastes a BigQuery query

Convert it rather than running it as-is. The mechanical steps (drop the per-service table for from logs where source = ..., remove every cross join unnest(...), rewrite unnest-alias columns as log_attributes['...'] lookups, swap the functions above) are spelled out with a full before/after in references/bigquery-migration.md. The Logs Explorer also has a built-in Rewrite to ClickHouse action that does this with AI; point users to it for one-off conversions in the dashboard.

來自 supabase 的更多技能

studio-e2e-tests
supabase
撰寫並執行 Supabase Studio 的 Playwright E2E 測試。在收到要求時使用。
official
vitest
supabase
由 Vite 驅動、相容 Jest API 的 Vitest 快速單元測試框架。適用於撰寫測試、模擬、配置覆蓋率或處理測試相關工作時使用。
official
skill-creator
supabase
建立模組化技能的全面指南,可擴展 Claude 的功能,包含專業知識與工作流程。技能由必要的 SKILL.md 檔案(含 YAML 前置資料與 Markdown 指令)以及可選的捆綁資源(腳本、參考資料、素材)組成,這些資源按用途組織並逐步載入以節省上下文空間。圍繞具體使用範例設計技能;識別用於確定性任務的可重複使用腳本、用於領域知識的參考檔案,以及用於...的素材。
official
supabase
supabase
在執行任何涉及 Supabase 的任務時使用。觸發條件:Supabase 產品(資料庫、驗證、邊緣函數、即時功能、儲存、向量、排程、佇列);客戶端…
official
supabase-server
supabase
在撰寫 Supabase 伺服端程式碼時使用 — 包括 Edge Functions、Hono 應用程式、Webhook 處理器,或任何需要建立 Supabase 驗證與客戶端的後端環境。…
official
dev-toolbar-review
supabase
在審查涉及 packages/dev-tools/、packages/common/posthog-client.ts 的 PR 時使用
official
e2e-studio-tests
supabase
在 Studio 應用程式中執行端對端測試。當被要求執行端對端測試、執行 Studio 測試、Playwright 測試或測試功能時使用。
official
safe-sql-execution
supabase
安全地對使用者資料庫執行SQL查詢,避免SQL隱碼攻擊或其他安全漏洞的風險。
official