Supabase 日志查询实战:ClickHouse logs 表、log_attributes 映射与 BigQuery 迁移

发布时间:2026/9/5 23:19:51

Supabase 日志查询实战:ClickHouse logs 表、log_attributes 映射与 BigQuery 迁移
Supabase 日志查询实战ClickHouse logs 表、log_attributes 映射与 BigQuery 迁移【免费下载链接】supabaseThe Postgres development platform. Supabase gives you a dedicated Postgres database to build your web, mobile, and AI applications.项目地址: https://gitcode.com/GitHub_Trending/supa/supabaseSupabase 的日志体系已经从一个基于 BigQuery 的每服务一张表模型演进为统一的 ClickHouselogs表由logs.all.otel分析端点对外提供服务。本指南基于仓库中的技能文档 .claude/skills/clickhouse-logs-queries/SKILL.md 及其配套参考资料展开覆盖三个核心能力在 Logs Explorer 或代码中编写规范的 ClickHouse 日志查询、正确读取log_attributes结构化字段、以及把旧版 BigQuerycross join unnest(metadata)日志查询机械地翻译为 ClickHouse 写法。读完本文你将能独立完成日志 SQL 的编写、评审并在 Studio 代码库中以安全的方式集成新的日志查询。整体模型一张 logs 表取代全部服务表旧模型中每个服务Postgres、Auth、Edge 等各有一张表结构化字段藏在嵌套的metadata列里读取时必须反复cross join unnest(...)。ClickHouse 模型将其折叠为单一事实表栈内每一行日志都是logs表中的一行用source列标记来源服务每个服务特有的结构化字段全部平铺进log_attributes这个Map(String, String)列以点分路径为键查询入口是logs.all.otel分析端点Logs Explorer 即构建在其上。logs表的真实列很少完整结构如下引自 SKILL.md列名类型说明idString唯一日志标识符timestampDateTime64UTC日志产生时间可直接排序/比较event_messageString原始日志行severity_textString日志级别若来源服务设置了sourceString日志来源服务必须作为过滤条件log_attributesMap(String, String)按来源服务的结构化字段键为点分路径timestamp的格式形如2026-06-22T09:34:06.215000ISO 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 取值日志来源清单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 的Field Reference抽屉为准。当不确定某个键是否存在时不要凭猜测从真实数据中探测见下文键发现一节。各来源常见的log_attributes键source常用键edge_logsrequest.method、request.path、request.search、response.status_code、identifierpostgres_logsparsed.error_severity、parsed.detail、parsed.hint、parsed.query、identifierauth_logslevel、status、path、msg、errorfunction_edge_logsresponse.status_code、request.method、request.pathname、function_id、execution_id、execution_time_msfunction_logsevent_type、function_id、execution_id、level读取 log_attributes点分键名与类型规则用方括号取值键名保留完整前缀log_attributes是字符串到字符串的映射读取字段用方括号访问不再需要任何 unnest 连接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 里通过嵌套 struct 表达的路径metadata.request.method在 ClickHouse 中变成log_attributes[request.method]——丢弃metadata根但保留完整点分前缀。也就是说request.cf.country对应log_attributes[request.cf.country]而不是log_attributes[cf.country]。数值字段一律是字符串映射值永远是字符串。要比较或聚合数值字段用toInt32OrZero包裹——它对缺失或非数值的值返回0不会在部分数据上抛错select count() as server_errors from logs where source edge_logs and toInt32OrZero(log_attributes[response.status_code]) between 500 and 599从真实数据中发现键名与其猜测键名不如读取最近行的mapKeysselect 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 代码库正是用这一招实现 Field Reference 抽屉并把这些真实键喂给 AI 改写功能的——对应实现在 apps/studio/data/logs/otel-log-keys-query.tsconst sql safeSqlSELECT arrayJoin(mapKeys(log_attributes)) AS key, count() AS n FROM logs WHERE source ${analyticsLiteral(source)} GROUP BY key ORDER BY n DESC LIMIT 500该模块以 7 天回看窗口查询LOOKBACK_HOURS 24 * 7并带 5 分钟staleTime的 React Query 缓存供订阅组件和提交时的queryClient.fetchQuery调用共享。这是小的、正确加品牌的 OTEL 查询的典范实现。ClickHouse 与 BigQuery 函数对照表最容易踩坑的函数替换如下需求BigQueryClickHouse计数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列映射键无用 unnestmapKeys(log_attributes)还有一个硬性约束logs.all.otel端点以及其上的 Logs Explorer拒绝count(*)和select *必须用count()并显式列出所需列。这是日志查询入口的限制原生 ClickHouse 两者都支持。最佳实践清单日志表很大无界扫描会读走远超需要的数据。保持查询正确且便宜的做法以标识性注释开头如-- errors since last deploy在日志与评审中标记查询意图方便区分同文件多条查询永远带LIMIT迭代阶段的聚合查询也不例外永远from logs where source ...——不存在每服务独立的表source过滤是正确性要求不只是优化时间窗口尽量收紧小窗口返回更快优先用真实列source、timestamp过滤再深入到log_attributesorder by 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 50Auth 错误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 转映射写法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 查询迁移到 ClickHouse参考资料 .claude/skills/clickhouse-logs-queries/references/bigquery-migration.md 给出了一套机械化的五步法用户贴来旧 BigQuery 查询时应当转换而不是直接执行用logs表加source过滤替换原表。旧表名就是source值from postgres_logs as t变成from logs where source postgres_logs。绝不要从每服务表名查询。例外pg_cron 日志归在source postgres_logs下。删除全部 unnest 连接——cross join unnest(metadata) as m、cross join unnest(m.parsed) as p、left join unnest(...) on true等一律删除它们在扁平映射里没有对应物。把每个 unnest 别名列改写成映射取值。来自unnest(metadata)的字段变成log_attributes[field]来自嵌套 struct如unnest(m.parsed)的字段变成log_attributes[parsed.field]——保留 struct 名作为点分前缀丢弃metadata根和所有别名。数值字段包一层toInt32OrZero(...)再比较或聚合因为映射值是字符串。按对照表替换函数。全程保留原始查询的 select 列表、过滤、group by、order by 与 limit 意图。完整迁移前后对照BigQuery 原文select count(t.timestamp) as count, p.error_severity from postgres_logs as t cross join unnest(metadata) as m cross join unnest(m.parsed) as p where p.error_severity in (ERROR, FATAL, PANIC) group by p.error_severity order by count desc limit 100;ClickHouse 结果select count() as count, log_attributes[parsed.error_severity] as error_severity from logs where source postgres_logs and log_attributes[parsed.error_severity] in (ERROR, FATAL, PANIC) group by log_attributes[parsed.error_severity] order by count desc limit 100变化点count(t.timestamp)变count()两条 unnest 连接消失p.error_severity变log_attributes[parsed.error_severity]from/where指向单表。最常见的转换错误是丢掉点分前缀——真实键是log_attributes[request.headers.x_real_ip]时写成了log_attributes[x_real_ip]或把request.cf.country写成cf.country。不确定时就用前文的arrayJoin(mapKeys(...))查询从数据中发现真实键再把旧嵌套路径逐一对应上去。Logs Explorer 也内置了Rewrite to ClickHouse动作用 AI 完成一次性转换适合在仪表盘里临时使用。在 Studio 代码库中集成日志查询当需要修改构建或执行日志查询的 TypeScript而不仅是写 UI 查询时按 references/codebase-integration.md 的约定执行。安全 SQLSafeLogSqlFragment 品牌类型所有分析日志 SQL 必须是SafeLogSqlFragment由 apps/studio/data/logs/safe-analytics-sql.ts 中的助手构建并受 eslint 强制约束。从源码结构看这套设计与 pg-meta 的SafeSqlFragmentPostgres 专用刻意品牌隔离Postgres 安全的转义E…字符串、::jsonb转换、双引号标识符对 BigQuery/ClickHouse 不安全反之亦然两个品牌不可互相提升防止跨引擎误拼接出危险 SQL。核心导出safeSql— 标签模板只接受SafeLogSqlFragment插值普通字符串和 Postgres 品牌类型在编译期被拒绝analyticsLiteral(value)— 把 string/number/boolean 变成安全转义的 литерал 片段单引号与反斜杠按 ClickHouse/BigQuery 共同约定转义与\\所有动态值尤其是source都必须走它joinSqlFragments(fragments, separator)— 以固定的结构分隔符 and 、, 等拼接已安全的片段keyword(value, allowed)— 按编译期允许清单解析值如AND/OR运算符只返回清单内片段绝不返回原始输入quotedIdent(value)— 逐段校验[A-Za-z_][A-Za-z0-9_]*后对点分标识符路径加反引号例如request.method变为request.method。该文件刻意不导出任何 raw 逃生口。典型用法import { analyticsLiteral, safeSql } from data/logs/safe-analytics-sql const source edge_logs const sql safeSql select timestamp, event_message from logs where source ${analyticsLiteral(source)} order by timestamp desc limit 100 同一文件还定义了用户输入的信任边界编辑器里的用户 SQL 以untrustedLogSql()标记为UntrustedLogSqlFragment只允许展示与保存只有acceptUntrustedLogsSql()安全边界能把它提升为可执行的SafeLogSqlFragment而源码注释明确要求只能在用户明确触发运行的事件处理器中调用绝不能在 render 或 useEffect 中调用。按特性开关选择端点与构建器ClickHouse 路径由 PostHog 特性开关otelLegacyLogs门控useFlag(otelLegacyLogs)开关关闭时必须保持 BigQuery 路径可用。apps/studio/data/logs/logs-endpoint.ts 中的两个助手表达了这个分叉export const logsAllEndpointUrl (useOtel: boolean) useOtel ? (/platform/projects/{ref}/analytics/endpoints/logs.all.otel as const) : (/platform/projects/{ref}/analytics/endpoints/logs.all as const) export const pickLogsQueryBuilder T(useOtel: boolean, otel: T, bq: T): T useOtel ? otel : bq使用方式const useOtel useFlag(otelLegacyLogs) const builder pickLogsQueryBuilder(useOtel, genDefaultQueryOtel, genDefaultQuery) const endpoint logsAllEndpointUrl(useOtel) // React Query key 中包含 { otel: useOtel }让两条路径各自缓存随后把片段交给 apps/studio/data/logs/execute-analytics-sql.ts 的executeAnalyticsSql执行。该函数是分析路径的线上边界只接受SafeLogSqlFragment普通字符串编译期被拒请求体携带{ sql, iso_timestamp_start, iso_timestamp_end }默认 POST兼容迁移期遗留的 GET 调用方。端点联合类型AnalyticsSqlEndpoint目前只包含logs.all与logs.all.otel两个成员新增端点需在此扩展。沿用既有 OTEL 构建器新增查询形态时应镜像 apps/studio/components/interfaces/Settings/Logs/Logs.utils.otel.ts 中的生成器而非自创风格。它们已经编码了全部约定genDefaultQueryOtel、genCountQueryOtel、genChartQueryOtel、genSingleLogQueryOtel— 行/计数/图表/单条日志构建器选取真实列加按来源的log_attributes[...]取值并别名为渲染层期望的叶子名mapOtelPreviewRow、mapOtelSingleLogToLegacy、otelTimestampToMicros— JS 归一化层。由于分页游标与渲染器要求timestamp是微秒数字应复用otelTimestampToMicros而不是自己解析 ISO 字符串。集成检查清单每个动态值都经过analyticsLiteral或其他净化助手绝不字符串拼接查询按source过滤且包含LIMIT数值型log_attributes值包了toInt32OrZero端点与构建器基于useFlag(otelLegacyLogs)经logsAllEndpointUrl/pickLogsQueryBuilder选择React Query key 区分 OTEL 与 BigQuery 两条路径表格/游标消费的行timestamp已归一化为微秒存在断言生成 SQL 字符串的单测可参考Logs.utils.otel.test.ts与 apps/studio/data/logs/safe-analytics-sql.test.ts 的模式。小结ClickHouse 日志模型的核心可以浓缩为三句话一张logs表、source列分服务、log_attributes扁平映射承载一切结构化字段。掌握方括号取值 完整点分前缀 toInt32OrZero数值转换三个要点后BigQuery 的 unnest 查询转换就是机械劳动而在 Studio 代码中集成查询时SafeLogSqlFragment品牌类型、otelLegacyLogs开关与既有 OTEL 构建器则保证了安全性、双引擎兼容与风格一致。【免费下载链接】supabaseThe Postgres development platform. Supabase gives you a dedicated Postgres database to build your web, mobile, and AI applications.项目地址: https://gitcode.com/GitHub_Trending/supa/supabase创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

侧吸油烟机不是风扇:从负压捕集到公共烟道静压匹配,看懂“超大吸力”的工程真相

侧吸油烟机不是风扇:从负压捕集到公共烟道静压匹配,看懂“超大吸力”的工程真相

2026/9/5 23:09:51

厨房电器里,油烟机是最容易被低估的一件。很多人觉得它就是“一个功率大一点的风扇”:灶台上面有烟,开起来呼呼吹走就行。于是当方太 R1S 这类侧吸油烟机进入候选清单时,家庭群里最常出现的疑问往往不是“它侧吸和顶吸差多少”&am…

Spring Boot教育数据风控系统:学情预警闭环实战

Spring Boot教育数据风控系统:学情预警闭环实战

2026/9/5 23:09:51

简介:本资源是一套面向高校教育信息化开发者的Spring Boot后端系统源码,聚焦学生学业状态动态监测与风险预警场景,适用于Java全栈初学者进阶实践及教学管理系统二次开发。压缩包共381个文件,含79个核心Java业务类(涵盖…

二叉树-堆1

二叉树-堆1

2026/9/5 23:09:51

完美二叉树若像下图这样写当child为堆顶时,计算parent为0(不会是-0.5,向上取整为0),while判断parent为0符合条件进入循环,此时if(a[child]a[parent]),跳出循环。这只是程序能巧合运行…

立创转AD库工具升级版--获取元件库与封装库

立创转AD库工具升级版--获取元件库与封装库

2026/9/6 2:40:00

这里Gitee地址: https://gitee.com/connor_c/jlc2-ad_-auto-lib.git 主要功能是搜索立创商城上面的封装,自动转换成元件库、封装库,然后手动复制到AD自己的库里去; 操作方法: 下载后打开脚本; …

两天让零基础学员写出 10 篇真实文章:一套 IP 启蒙课完整教学流程(含定位三问与故事骨架)

两天让零基础学员写出 10 篇真实文章:一套 IP 启蒙课完整教学流程(含定位三问与故事骨架)

2026/9/6 2:40:00

目录 一、课程背景与结果二、核心认知:AI 只放大,不创造三、第一天:定位三问与开号立门面四、第二天:往故事骨架里填真话五、两个必踩的坑与避坑方法六、关于发布:改的不是文章,是勇气七、关于 IP&#xf…

IAR发布原生Linux跨平台IDE,嵌入式开发告别环境割裂

IAR发布原生Linux跨平台IDE,嵌入式开发告别环境割裂

2026/9/6 2:40:00

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

Virtuoso Layout XL Auto Label功能详解:从原理到实战应用

Virtuoso Layout XL Auto Label功能详解:从原理到实战应用

2026/9/6 2:40:00

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

多行业多梯调度场景解析,不同客户落地之后获得哪些实际价值

多行业多梯调度场景解析,不同客户落地之后获得哪些实际价值

2026/9/6 2:40:00

摘要:很多拥有多台货梯的园区、市场、医院,普遍面临多梯协同难、沟通混乱、人力成本高、安全管控难、运维压力大的痛点。讯唐慧梯电梯可视化智慧调度系统,不止应用于冷链冷库,还可以适配大型农产品水产市场、医院后勤、普通多层物…

小白信创:OpenEuler24.03+JeecgBoot 3.8.3 部署全流程(通关版)

小白信创:OpenEuler24.03+JeecgBoot 3.8.3 部署全流程(通关版)

2026/9/6 2:29:59

小白信创:OpenEuler24.03JeecgBoot 3.8.3 部署全流程(通关版) 最近工作场所施工,拖了好久才又继续折腾部署,这次通关了,后续会在此基础上,架构我的资产管理系统,这篇基本就能解决从零…

中国人民大学杨琳团队《Nature Communications》 | 全球潮汐湿地土壤有机碳时空格局与环境驱动:一项2009-2020年的全球评估

中国人民大学杨琳团队《Nature Communications》 | 全球潮汐湿地土壤有机碳时空格局与环境驱动:一项2009-2020年的全球评估

2026/9/6 1:19:56

本文首发于“生态学者”!从“湿地面积”到“土壤碳密度”:为什么需要重新认识潮汐湿地蓝碳变化?潮汐湿地位于陆地与海洋的交汇地带,包括红树林、盐沼和潮滩,是全球重要的蓝碳生态系统。其土壤能够长期储存大量有机碳&a…

adb抓包

adb抓包

2026/9/6 1:19:56

前言 本文介绍如何通过 tcpdump 在 Android 手机上抓取网络数据包,并在电脑端使用 Wireshark 进行分析。适用于需要排查 App 网络请求、分析接口调用或调试网络问题的开发与测试场景。1. 手机要有 root 权限2. 下载 tcpdump3. adb push C:\Users\zhangkuixun\Downlo…

大模型推理镜像极简瘦身:从 25GB 巨无霸到 3GB 精简镜像实战

大模型推理镜像极简瘦身:从 25GB 巨无霸到 3GB 精简镜像实战

2026/9/6 1:19:56

大模型推理镜像极简瘦身:从 25GB 巨无霸到 3GB 精简镜像实战 在云原生基础设施中,容器镜像体积直接决定了服务的部署速度与弹性扩容敏捷度。对于传统的 Go / Java 微服务,镜像体积通常被严格控制在 50MB 到 200MB 以内,拉取镜像只…

中国人民大学杨琳团队《Nature Communications》 | 全球潮汐湿地土壤有机碳时空格局与环境驱动:一项2009-2020年的全球评估

中国人民大学杨琳团队《Nature Communications》 | 全球潮汐湿地土壤有机碳时空格局与环境驱动:一项2009-2020年的全球评估

2026/9/6 1:19:56

本文首发于“生态学者”!从“湿地面积”到“土壤碳密度”:为什么需要重新认识潮汐湿地蓝碳变化?潮汐湿地位于陆地与海洋的交汇地带,包括红树林、盐沼和潮滩,是全球重要的蓝碳生态系统。其土壤能够长期储存大量有机碳&a…

adb抓包

adb抓包

2026/9/6 1:19:56

前言 本文介绍如何通过 tcpdump 在 Android 手机上抓取网络数据包,并在电脑端使用 Wireshark 进行分析。适用于需要排查 App 网络请求、分析接口调用或调试网络问题的开发与测试场景。1. 手机要有 root 权限2. 下载 tcpdump3. adb push C:\Users\zhangkuixun\Downlo…

大模型推理镜像极简瘦身:从 25GB 巨无霸到 3GB 精简镜像实战

大模型推理镜像极简瘦身:从 25GB 巨无霸到 3GB 精简镜像实战

2026/9/6 1:19:56

大模型推理镜像极简瘦身:从 25GB 巨无霸到 3GB 精简镜像实战 在云原生基础设施中,容器镜像体积直接决定了服务的部署速度与弹性扩容敏捷度。对于传统的 Go / Java 微服务,镜像体积通常被严格控制在 50MB 到 200MB 以内,拉取镜像只…

远程协作的工作台整理

远程协作的工作台整理

2026/9/3 6:56:24

远程协作的工作台整理远程协作的核心不是再加一个工具,而是让交接信息足够完整。异步任务要写明目标、输入位置、完成标准和需要决策的人。 工作台的最小配置 将日程、待办、代码和沟通入口收拢到少数固定位置;通知按紧急程度分层。工作台不需要模仿办公…

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

2026/9/4 7:42:10

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

2026/9/5 23:14:13

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…