遥测

日志系统


Supabase 平台包含一个日志浏览器(Logs Explorer),可用于日志追踪和调试。日志保留期限取决于您的项目定价计划

产品日志

Supabase 为每个产品提供了专门的日志界面。您可以使用简单的正则表达式搜索日志事件消息中的关键词和模式。您还可以将匹配查询的日志事件导出并下载为电子表格。

API 日志显示 REST 和 GraphQL API 的所有网络请求和响应。如果启用了读取副本,日志会自动在不同数据库以及API 负载均衡器端点之间进行筛选。可以通过仪表板右上角的Source按钮切换特定端点的日志。

当查看来自 API 负载均衡器端点的日志时,可以在Redirect Identifier字段下找到最终处理请求的上游数据库。这相当于查询底层日志时的metadata.load_balancer_redirect_identifier

API 日志


处理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 请求头来附加额外的元数据,例如用于设备或版本识别。

示例:

1
2
node 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)

记录Postgres查询

要启用其他类别语句的查询日志:

  1. 启用pgAudit扩展
  2. 配置 pgaudit.log 参数(见下文)。如需生效可执行快速重启
  3. 日志 > Postgres日志中查看查询日志

配置 pgaudit.log

pgaudit.log 下的存储值决定了 pgAudit 扩展 记录哪些类别的语句。完整值列表请参考 pgAudit 的文档说明

要为单个会话启用函数调用/do块、写入操作和DDL语句的日志记录,请在会话中执行以下命令:

1
2
-- 临时单会话配置更新set pgaudit.log = 'function, write, ddl';

若要_永久_设置日志配置(跨多个会话),请执行以下命令,然后执行快速重启:

1
2
-- 等效的永久配置更新alter role postgres set pgaudit.log to 'function, write, ddl';

为了便于调试,我们建议将日志范围调整为仅相关语句,因为过宽的范围会导致Postgres日志中出现大量噪音。

注意在上例中,角色设置为postgres。要记录通过PostgREST驱动的HTTP API的用户流量,请为authenticator设置配置值。

1
2
-- 用于API相关日志alter role authenticator set pgaudit.log to 'write';

默认情况下,日志级别设置为log。要查看其他级别,请运行:

1
2
3
-- 调整日志级别alter role postgres set pgaudit.log_level to 'info';alter role postgres set pgaudit.log_level to 'debug5';

请注意,根据pgAudit的log_level文档,不允许使用errorfatalpanic

要重置系统范围的设置,请执行以下命令,然后执行快速重启:

1
2
-- 重置存储的配置alter role postgres reset pgaudit.log

Postgres 中的 RAISE 日志消息

通过 RAISE INFORAISE NOTICERAISE WARNINGRAISE LOG 手动记录的日志消息会显示在 Postgres 日志中。请注意,只有达到或超过您设置的日志级别的消息才会显示。消息同步到 Postgres 日志可能需要几分钟时间。

如果您的日志没有显示,可以通过以下命令检查当前日志级别:

1
show log_min_messages;

请注意,LOG 级别高于 WARNINGERROR,因此如果您的级别设置为 LOG,将不会看到 WARNINGERROR 级别的消息。

记录实时连接日志

默认情况下,Realtime 不会记录新的 WebSocket 连接或频道加入事件。如需启用连接日志记录,需要在实例化 Supabase 客户端时包含 info 级别的 log_level 参数。

1
2
3
4
5
6
7
8
9
10
import { } 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格式。

1
2
3
4
5
6
7
-- 不使用datetime()的时间戳列select timestamp from ....-- 1664270180000-- 使用datetime()的时间戳列select datetime(timestamp) from ....-- 2022-09-27T09:17:10.439Z

数组展开(Unnesting)

每个日志事件都将元数据存储为一个包含多层级对象的数组,在日志浏览器中选择单个日志事件即可查看。要查询数组数据,需对每个数组字段使用unnest()函数并将其作为连接(join)添加到查询中。这样您就可以通过别名引用嵌套对象并选择其特定字段。

例如,查询边缘日志时不使用任何连接:

1
select timestamp, metadata from edge_logs as t;

结果中的metadata键在日志浏览器中显示为对象数组。在下图中,每个方框代表一个嵌套的对象数组:

未展开数组

使用cross join unnest()来处理嵌套在metadata键中的键值。

要查询嵌套值,需为每个数组层级添加连接:

1
2
3
4
5
6
select 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;

这样会显示以下可供选择的列: 两级数组展开

通过这种方式,您可以选择methodcf_ipcountry列。用JS点表示法表示时,每个选中列的完整路径为:

  • metadata[].request[].method
  • metadata[].request[].headers[].cf_ipcountry

LIMIT与结果行数限制

日志浏览器每次查询最多返回1000行结果。使用LIMIT子句可以优化查询,进一步减少返回的行数。

最佳实践

  1. 包含对 timestamp 的筛选条件

查询整个日志历史可能看起来很吸引人。但对于拥有大量日志保留期的企业版客户来说,由于需要扫描更大数据集所需的时间增加,可能会导致查询超时。

  1. 避免选择大型嵌套对象,改为选择单个值

当查询大型对象时,列式存储引擎会选择与每个嵌套键关联的所有列,导致需要选择大量列。这会无意中影响查询速度,并可能导致超时或内存错误,特别是对于日志量大的项目。

应该只选择需要的值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
-- ❌ 避免这样做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地址:

1
2
3
4
5
6
7
select 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";

日志字段参考

请参考下方各可用源的完整字段参考。请注意,要访问每个嵌套键,您需要执行必要的数组解嵌套连接

路径类型
idstring
timestampdatetime
event_messagestring
identifierstring
metadata.load_balancer_redirect_identifierstring
metadata.request.cf.asOrganizationstring
metadata.request.cf.asnnumber
metadata.request.cf.botManagement.corporateProxyboolean
metadata.request.cf.botManagement.detectionIdsnumber[]
metadata.request.cf.botManagement.ja3Hashstring
metadata.request.cf.botManagement.scorenumber
metadata.request.cf.botManagement.staticResourceboolean
metadata.request.cf.botManagement.verifiedBotboolean
metadata.request.cf.citystring
metadata.request.cf.clientTcpRttnumber
metadata.request.cf.clientTrustScorenumber
metadata.request.cf.colostring
metadata.request.cf.continentstring
metadata.request.cf.countrystring
metadata.request.cf.edgeRequestKeepAliveStatusnumber
metadata.request.cf.httpProtocolstring
metadata.request.cf.latitudestring
metadata.request.cf.longitudestring
metadata.request.cf.metroCodestring
metadata.request.cf.postalCodestring
metadata.request.cf.regionstring
metadata.request.cf.timezonestring
metadata.request.cf.tlsCipherstring
metadata.request.cf.tlsClientAuth.certPresentedstring
metadata.request.cf.tlsClientAuth.certRevokedstring
metadata.request.cf.tlsClientAuth.certVerifiedstring
metadata.request.cf.tlsExportedAuthenticator.clientFinishedstring
metadata.request.cf.tlsExportedAuthenticator.clientHandshakestring
metadata.request.cf.tlsExportedAuthenticator.serverFinishedstring
metadata.request.cf.tlsExportedAuthenticator.serverHandshakestring
metadata.request.cf.tlsVersionstring
metadata.request.headers.cf_connecting_ipstring
metadata.request.headers.cf_ipcountrystring
metadata.request.headers.cf_raystring
metadata.request.headers.hoststring
metadata.request.headers.refererstring
metadata.request.headers.x_client_infostring
metadata.request.headers.x_forwarded_protostring
metadata.request.headers.x_real_ipstring
metadata.request.hoststring
metadata.request.methodstring
metadata.request.pathstring
metadata.request.protocolstring
metadata.request.searchstring
metadata.request.urlstring
metadata.response.headers.cf_cache_statusstring
metadata.response.headers.cf_raystring
metadata.response.headers.content_locationstring
metadata.response.headers.content_rangestring
metadata.response.headers.content_typestring
metadata.response.headers.datestring
metadata.response.headers.sb_gateway_versionstring
metadata.response.headers.transfer_encodingstring
metadata.response.headers.x_kong_proxy_latencystring
metadata.response.origin_timenumber
metadata.response.status_codenumber