日志系统
Supabase 平台包含一个日志浏览器(Logs Explorer),可用于日志追踪和调试。日志保留期限取决于您的项目定价计划。
产品日志
Supabase 为每个产品提供了专门的日志界面。您可以使用简单的正则表达式搜索日志事件消息中的关键词和模式。您还可以将匹配查询的日志事件导出并下载为电子表格。
处理API日志
API日志通过Cloudflare边缘服务器运行,并会在metadata.request.cf.*
字段下附加Cloudflare元数据。
允许的请求头
API日志中只允许特定的请求头和响应头。服务器和客户端仍会接收所有请求头和响应头,但不会附加到生成的API日志中。
允许的请求头:
accept
cf-connecting-ip
cf-ipcountry
host
user-agent
x-forwarded-proto
referer
content-length
x-real-ip
x-client-info
x-forwarded-user-agent
range
prefer
允许的响应头:
cf-cache-status
cf-ray
content-location
content-range
content-type
content-length
date
transfer-encoding
x-kong-proxy-latency
x-kong-upstream-latency
sb-gateway-mode
sb-gateway-version
附加请求元数据
建议使用 User-Agent
请求头来附加额外的元数据,例如用于设备或版本识别。
示例:
12node MyApp/1.2.3 (device-id:abc123)Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0 MyApp/1.2.3 (Foo v1.3.2; Bar v2.2.2)
请勿在 User-Agent
请求头中记录个人身份信息(PII),以避免违反数据保护隐私法规。过于细粒度的用户代理信息可能通过PII实现指纹识别和终端用户身份追踪。
记录Postgres查询
要启用其他类别语句的查询日志:
- 启用pgAudit扩展
- 配置
pgaudit.log
参数(见下文)。如需生效可执行快速重启 - 在日志 > Postgres日志中查看查询日志
配置 pgaudit.log
pgaudit.log
下的存储值决定了 pgAudit 扩展 记录哪些类别的语句。完整值列表请参考 pgAudit 的文档说明。
要为单个会话启用函数调用/do块、写入操作和DDL语句的日志记录,请在会话中执行以下命令:
12-- 临时单会话配置更新set pgaudit.log = 'function, write, ddl';
若要_永久_设置日志配置(跨多个会话),请执行以下命令,然后执行快速重启:
12-- 等效的永久配置更新alter role postgres set pgaudit.log to 'function, write, ddl';
为了便于调试,我们建议将日志范围调整为仅相关语句,因为过宽的范围会导致Postgres日志中出现大量噪音。
注意在上例中,角色设置为postgres
。要记录通过PostgREST驱动的HTTP API的用户流量,请为authenticator
设置配置值。
12-- 用于API相关日志alter role authenticator set pgaudit.log to 'write';
默认情况下,日志级别设置为log
。要查看其他级别,请运行:
123-- 调整日志级别alter role postgres set pgaudit.log_level to 'info';alter role postgres set pgaudit.log_level to 'debug5';
请注意,根据pgAudit的log_level文档,不允许使用error
、fatal
和panic
。
要重置系统范围的设置,请执行以下命令,然后执行快速重启:
12-- 重置存储的配置alter role postgres reset pgaudit.log
如果在执行alter role postgres ...
时遇到任何权限错误,可能是因为您的项目尚未接收到最新版本supautils的补丁,该补丁目前正在逐步推出中。
Postgres 中的 RAISE
日志消息
通过 RAISE INFO
、RAISE NOTICE
、RAISE WARNING
和 RAISE LOG
手动记录的日志消息会显示在 Postgres 日志中。请注意,只有达到或超过您设置的日志级别的消息才会显示。消息同步到 Postgres 日志可能需要几分钟时间。
如果您的日志没有显示,可以通过以下命令检查当前日志级别:
1show log_min_messages;
请注意,LOG
级别高于 WARNING
和 ERROR
,因此如果您的级别设置为 LOG
,将不会看到 WARNING
和 ERROR
级别的消息。
记录实时连接日志
默认情况下,Realtime 不会记录新的 WebSocket 连接或频道加入事件。如需启用连接日志记录,需要在实例化 Supabase 客户端时包含 info
级别的 log_level
参数。
12345678910import { } from '@supabase/supabase-js'const = { : { : { : 'info', }, },}const = ('https://xyzcompany.supabase.co', 'public-anon-key', )
日志浏览器
日志浏览器将Supabase堆栈各部分的日志作为独立的表展示,支持使用SQL进行查询和关联。
您可以通过数据源下拉菜单访问以下日志:
auth_logs
: GoTrue服务器日志,包含认证/授权活动edge_logs
: 边缘网络日志,包含从Cloudflare获取的请求和响应元数据function_edge_logs
: 仅边缘函数的网络日志,包含每次执行的网络请求和响应元数据function_logs
: 函数内部日志,包含边缘函数中的console
输出postgres_logs
: Postgres数据库日志,包含连接应用执行的SQL语句realtime_logs
: Realtime服务器日志,包含客户端连接信息storage_logs
: Storage服务器日志,包含对象上传和检索信息
使用日志浏览器查询
日志浏览器基于BigQuery实现,支持所有可用的SQL函数和操作符。
时间戳显示与行为
每条日志条目都存储为TIMESTAMP
数据类型的timestamp
字段。查询时请使用适当的时间戳函数来处理该字段。
原始顶层时间戳值以Unix微秒格式显示。如需转换为人类可读格式,可使用DATETIME()
函数将Unix时间戳转换为ISO-8601格式。
1234567-- 不使用datetime()的时间戳列select timestamp from ....-- 1664270180000-- 使用datetime()的时间戳列select datetime(timestamp) from ....-- 2022-09-27T09:17:10.439Z
数组展开(Unnesting)
每个日志事件都将元数据存储为一个包含多层级对象的数组,在日志浏览器中选择单个日志事件即可查看。要查询数组数据,需对每个数组字段使用unnest()
函数并将其作为连接(join)添加到查询中。这样您就可以通过别名引用嵌套对象并选择其特定字段。
例如,查询边缘日志时不使用任何连接:
1select timestamp, metadata from edge_logs as t;
结果中的metadata
键在日志浏览器中显示为对象数组。在下图中,每个方框代表一个嵌套的对象数组:
使用cross join unnest()
来处理嵌套在metadata
键中的键值。
要查询嵌套值,需为每个数组层级添加连接:
123456select timestamp, request.method, header.cf_ipcountryfrom edge_logs as t cross join unnest(t.metadata) as metadata cross join unnest(metadata.request) as request cross join unnest(request.headers) as header;
这样会显示以下可供选择的列:
通过这种方式,您可以选择method
和cf_ipcountry
列。用JS点表示法表示时,每个选中列的完整路径为:
metadata[].request[].method
metadata[].request[].headers[].cf_ipcountry
LIMIT与结果行数限制
日志浏览器每次查询最多返回1000行结果。使用LIMIT
子句可以优化查询,进一步减少返回的行数。
最佳实践
- 包含对 timestamp 的筛选条件
查询整个日志历史可能看起来很吸引人。但对于拥有大量日志保留期的企业版客户来说,由于需要扫描更大数据集所需的时间增加,可能会导致查询超时。
- 避免选择大型嵌套对象,改为选择单个值
当查询大型对象时,列式存储引擎会选择与每个嵌套键关联的所有列,导致需要选择大量列。这会无意中影响查询速度,并可能导致超时或内存错误,特别是对于日志量大的项目。
应该只选择需要的值:
12345678910111213141516-- ❌ 避免这样做select datetime(timestamp), m as metadata -- <- metadata包含许多嵌套键from edge_logs as t cross join unnest(t.metadata) as m;-- ✅ 应该这样做select datetime(timestamp), r.method -- <- 只选择需要的值from edge_logs as t cross join unnest(t.metadata) as m cross join unnest(m.request) as r;
示例与模板
日志浏览器提供了模板(可在模板标签页或查询标签页的下拉菜单中找到)来帮助您快速开始。
例如,您可以在SQL编辑器中输入以下查询来获取每个用户的IP地址:
1234567select datetime(timestamp), h.x_real_ipfrom edge_logs cross join unnest(metadata) as m cross join unnest(m.request) as r cross join unnest(r.headers) as hwhere h.x_real_ip is not null and r.method = "GET";
日志字段参考
请参考下方各可用源的完整字段参考。请注意,要访问每个嵌套键,您需要执行必要的数组解嵌套连接
路径 | 类型 |
---|---|
id | string |
timestamp | datetime |
event_message | string |
identifier | string |
metadata.load_balancer_redirect_identifier | string |
metadata.request.cf.asOrganization | string |
metadata.request.cf.asn | number |
metadata.request.cf.botManagement.corporateProxy | boolean |
metadata.request.cf.botManagement.detectionIds | number[] |
metadata.request.cf.botManagement.ja3Hash | string |
metadata.request.cf.botManagement.score | number |
metadata.request.cf.botManagement.staticResource | boolean |
metadata.request.cf.botManagement.verifiedBot | boolean |
metadata.request.cf.city | string |
metadata.request.cf.clientTcpRtt | number |
metadata.request.cf.clientTrustScore | number |
metadata.request.cf.colo | string |
metadata.request.cf.continent | string |
metadata.request.cf.country | string |
metadata.request.cf.edgeRequestKeepAliveStatus | number |
metadata.request.cf.httpProtocol | string |
metadata.request.cf.latitude | string |
metadata.request.cf.longitude | string |
metadata.request.cf.metroCode | string |
metadata.request.cf.postalCode | string |
metadata.request.cf.region | string |
metadata.request.cf.timezone | string |
metadata.request.cf.tlsCipher | string |
metadata.request.cf.tlsClientAuth.certPresented | string |
metadata.request.cf.tlsClientAuth.certRevoked | string |
metadata.request.cf.tlsClientAuth.certVerified | string |
metadata.request.cf.tlsExportedAuthenticator.clientFinished | string |
metadata.request.cf.tlsExportedAuthenticator.clientHandshake | string |
metadata.request.cf.tlsExportedAuthenticator.serverFinished | string |
metadata.request.cf.tlsExportedAuthenticator.serverHandshake | string |
metadata.request.cf.tlsVersion | string |
metadata.request.headers.cf_connecting_ip | string |
metadata.request.headers.cf_ipcountry | string |
metadata.request.headers.cf_ray | string |
metadata.request.headers.host | string |
metadata.request.headers.referer | string |
metadata.request.headers.x_client_info | string |
metadata.request.headers.x_forwarded_proto | string |
metadata.request.headers.x_real_ip | string |
metadata.request.host | string |
metadata.request.method | string |
metadata.request.path | string |
metadata.request.protocol | string |
metadata.request.search | string |
metadata.request.url | string |
metadata.response.headers.cf_cache_status | string |
metadata.response.headers.cf_ray | string |
metadata.response.headers.content_location | string |
metadata.response.headers.content_range | string |
metadata.response.headers.content_type | string |
metadata.response.headers.date | string |
metadata.response.headers.sb_gateway_version | string |
metadata.response.headers.transfer_encoding | string |
metadata.response.headers.x_kong_proxy_latency | string |
metadata.response.origin_time | number |
metadata.response.status_code | number |