关于 clickhouse-logs-queries
clickhouse-logs-queries 是用于查询和迁移 Supabase 日志的技能,面向使用 Logs Explorer 或 ClickHouse 查询日志的开发者。它说明统一 logs 表、来源筛选、属性映射及 BigQuery 到 ClickHouse 的转换方式。
主要功能
- 01查询统一的 ClickHouse logs 表
- 02按服务来源筛选日志
- 03读取 log_attributes 字段
- 04将 BigQuery 查询转换为 ClickHouse
- 05提供查询示例与实践规范
适用场景
- 01编写或审查 Supabase 日志查询
- 02在日志中筛选特定服务和字段
- 03将旧 BigQuery 查询迁移至 ClickHouse
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ supabase/ supabase --skill clickhouse-logs-queries安装后在 Claude Code 中输入/clickhouse-logs-queries确认已加载。
附带文件references/codebase-integration.mdreferences/bigquery-migration.md
详细介绍
查询 Supabase 日志(ClickHouse)
Supabase 日志都存储在单个 ClickHouse logs 表中,由
logs.all.otel 分析端点提供服务。堆栈各部分的每一行日志在此表中都对应一行,并通过 source
列标记。这取代了旧的 BigQuery 模式:每个服务各有一张表,字段则通过
cross join unnest(metadata) 获取。
有两类工作会用到此技能,它们共用同一套 SQL 模型:
- 编写或审查日志查询(在 Logs Explorer 中,或在其他需要原始 ClickHouse 日志查询的地方)。请从本文档开始。
- 在 Studio 代码库中接入日志查询(品牌分析 SQL、端点选择器、OTEL 查询构建器)。请阅读 references/codebase-integration.md。
如果要转换现有的 BigQuery 日志查询,请阅读 references/bigquery-migration.md,查看完整的转换表。
日志表
每行包含少量真实列。每个服务特有的信息都存储在
log_attributes 中。
| 列 | 类型 | 说明 |
|---|---|---|
id | String | 唯一的日志标识符。 |
timestamp | DateTime64 (UTC) | 日志生成时间。直接对其排序或比较。 |
event_message | String | 原始日志行。 |
severity_text | String | 日志级别(如果来源设置了该值)。 |
source | String | 日志所属的服务。始终按此列筛选。 |
log_attributes | Map(String, String) | 按来源组织的结构化字段,以带点的路径作为键。 |
timestamp 的格式类似 2026-06-22T09:34:06.215000(ISO 8601 格式,精度为微秒,
末尾没有 Z)。在 Logs Explorer 中,系统会自动应用所选时间范围,因此通常无需手动编写
timestamp 筛选条件。
一个格式规范的最简查询。开头添加注释以命名查询,按 source 筛选,并始终设置 limit:
-- recent edge requests
select timestamp, event_message
from logs
where source = 'edge_logs'
order by timestamp desc
limit 100;
来源
source 用于选择服务。常见的来源包括:
edge_logs— API 网关请求和响应postgres_logs— 数据库语句和错误(也包含 pg_cron 日志)auth_logs— 身份验证和授权活动function_edge_logs— Edge Function 请求和响应function_logs— Edge Function 内部的console输出storage_logs— 对象上传和检索活动realtime_logs— Realtime 客户端连接postgrest_logs、supavisor_logs、pgbouncer_logs— 主要包含id、timestamp、event_message
Logs Explorer 中的 字段参考 抽屉会列出每个来源及其实际设置的字段。无法确定某个键时,请从真实数据中查找,而不是猜测(见下文)。
从 log_attributes 读取字段
log_attributes 将字符串键映射到字符串值。使用方括号访问字段。无需使用展开连接:
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'
键保留了 BigQuery 通过嵌套结构表示的点分路径,只是省略了 metadata 根节点:BigQuery 的 metadata.request.method 对应
log_attributes['request.method']。保留完整前缀——request.cf.country 对应 log_attributes['request.cf.country'],而不是 log_attributes['cf.country']。
各来源的常见键:
edge_logs:request.method、request.path、request.search、response.status_code、identifierpostgres_logs:parsed.error_severity、parsed.detail、parsed.hint、parsed.query、identifierauth_logs:level、status、path、msg、errorfunction_edge_logs:response.status_code、request.method、request.pathname、function_id、execution_id、execution_time_msfunction_logs:event_type、function_id、execution_id、level
数值字段是字符串
映射值始终是字符串。要比较或聚合数值字段,请用 toInt32OrZero 包裹它;对于缺失或非数值的值,该函数会返回 0,因此不会因部分数据而报错:
select count() as server_errors
from logs
where source = 'edge_logs'
and toInt32OrZero(log_attributes['response.status_code']) between 500 and 599
查找来源设置的键
从最近的行中读取 mapKeys,不要猜测键名:
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(...)) 会将映射键展开为每个键对应一行,以便按频率排序。(Studio 代码库正是这样实现字段参考抽屉,并为 AI 改写提供真实键名。)
ClickHouse 与 BigQuery 函数对照
以下替换最容易让人踩坑:
| 需求 | BigQuery | ClickHouse |
|---|---|---|
| 统计行数 | count(*) | count() |
| 正则匹配 | regexp_contains(x, 'p') | match(x, 'p') |
| 子串匹配 | x like '%p%' | x ilike '%p%'(不区分大小写)或 like |
| 数值转换 | cast(x as int64) | toInt32OrZero(x) |
| 读取时间戳 | cast(timestamp as datetime) | timestamp(直接使用该列) |
| 映射键 | 不适用(使用 unnest) | mapKeys(log_attributes) |
logs.all.otel 分析端点(以及基于它的 Logs Explorer)会拒绝 count(*) 和 select *——请使用 count(),并列出所需的列。(原始 ClickHouse 支持这两种写法;这是日志查询接口的限制。)
最佳实践
遵循这些做法可以确保查询正确且开销低。日志表数据量很大;无界扫描会读取远超所需的数据。
- 每条查询都以说明性注释开头(例如
-- errors since last deploy)。这会在日志和审查中标记该查询,也便于区分文件中的多条查询。 - 始终包含
LIMIT。 即使在迭代聚合查询时也要包含。 - 始终使用
from logs where source = '...'查询。 不存在按服务划分的表(没有edge_logs、postgres_logs等表)——只有一个logs表,而source用于限定服务范围。必须按source筛选,这不只是优化手段。 - 缩小时间范围。 时间窗口越短,结果返回越快。
- 先筛选实际列(
source、timestamp),再访问log_attributes。 - 按
timestamp desc排序,优先查看最新日志。 - 使用
count(),不要使用count(*)或select *。
示例
按状态码统计请求:
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 错误:
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
搜索原始消息:
select timestamp, event_message
from logs
where source = 'postgres_logs'
and event_message ilike '%deadlock%'
order by timestamp desc
limit 100
按严重级别分组的 Postgres 错误(将 unnest 转换为 map 的规范示例):
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
当用户粘贴 BigQuery 查询时
请转换查询,而不是直接运行。机械转换步骤(将每服务的表替换为 from logs where source = ...,移除所有
cross join unnest(...),将 unnest 别名列改写为 log_attributes['...']
查询,并替换上述函数)已在
references/bigquery-migration.md 中通过完整的转换前后示例说明。Logs
Explorer 还内置了 Rewrite to ClickHouse 操作,可借助 AI 完成转换;对于在仪表板中进行的一次性转换,请引导用户使用此功能。