自托管分析
Supabase Analytics 服务器是 Logflare 的一个可自托管实例,用于管理摄取和查询管道,以搜索和聚合结构化分析事件。
自托管 Analytics 服务器时,Studio 实例中提供了与 Supabase 平台相同的完整日志记录体验,从而实现了集成和增强的开发体验。但是,需要注意的是,由于平台的基础设施,可能会出现某些差异。
Logflare 技术文档
所有 Logflare 技术文档都可以在 https://docs.logflare.app 找到。
支持的后端#
Analytics 服务器支持 Postgres 或 BigQuery 作为后端。supabase-cli 体验默认使用 Postgres 后端。然而,Supabase 平台使用 BigQuery 后端存储所有平台日志。
使用 BigQuery 后端时,会在提供的 Google Cloud 项目中创建一个 BigQuery 数据集,并为每个源创建表。日志事件会流式传输到每个表中,并且 Studio 或日志浏览器生成的所有查询都会针对 BigQuery API 执行。此后端需要互联网访问才能工作,并且无法完全在本地运行。
使用 Postgres 后端时,会在提供的模式中为每个源创建表(对于 supabase-cli,这将是 _analytics)。Logflare 接收到的日志事件会直接插入到相应的表中。Studio 中所有 BigQuery 方言的 SQL 查询都将由 Analytics 服务器中的翻译层处理。此翻译层将查询翻译为 PostgreSQL 方言,然后针对 Postgres 数据库执行。
Postgres 后端尚未针对高容量插入或繁重查询使用进行优化。目前,翻译层仅处理 BigQuery 方言的有限子集。因此,当使用 Postgres 后端时,日志浏览器可能会对更高级的查询产生错误。
入门#
在熟悉和尝试自托管 Supabase 时,建议使用 Postgres 后端。对于生产环境,我们建议使用 BigQuery 后端。有关更多信息,请参阅生产建议。
要设置自托管 Supabase 中的日志记录,请参阅 docker-compose 示例。需要两个 compose 服务:Logflare 和 Vector。Logflare 是 HTTP 分析服务器,而 Vector 是日志记录管道,用于将所有 compose 服务的 syslog 路由到 Logflare 服务器。
无论选择哪个后端,都必须为 supabase/logflare docker 镜像设置以下环境变量
LOGFLARE_SINGLE_TENANT=true:用于启用 Logflare 单租户模式的功能标志。必须设置为trueLOGFLARE_SUPABASE_MODE=true:用于初始化 Supabase 相关数据的功能标志。必须设置为true
有关所有其他配置环境变量,请参阅 Logflare 自托管文档。
Postgres 后端设置#
示例 docker-compose 默认使用 Postgres 后端。
1# clone the supabase/supabase repo, and run the following2cd docker3docker compose -f docker-compose.yml up配置和要求#
supabase/logflare:1.4.0或更高版本- 相关环境变量
POSTGRES_BACKEND_URL:必需。Postgres 数据库的连接字符串。POSTGRES_BACKEND_SCHEMA:可选。允许自定义用于在数据库中限定所有后端操作的模式。
BigQuery 后端设置#
BigQuery 后端是一个更健壮、可扩展的后端选项,经过实战检验,已可用于生产环境。如果您打算进行大量日志记录使用并需要高级查询功能(例如日志浏览器),请使用此后端。
配置和要求#
创建项目后的要求如下
- 已启用账单的 Google Cloud 项目
- 项目 ID
- 项目编号
- 服务帐号密钥。
您必须在 Google Cloud 项目中启用账单功能,因为流式插入需要有效的账单帐号。
设置 BigQuery 服务帐号#
所使用的服务帐号必须具有足够的权限才能插入您的 Google Cloud BigQuery。确保服务帐号具有以下任一权限:
- BigQuery Admin 角色;或
- 以下权限
bigquery.datasets.createbigquery.datasets.getbigquery.datasets.getIamPolicybigquery.datasets.updatebigquery.jobs.createbigquery.routines.createbigquery.routines.updatebigquery.tables.createbigquery.tables.deletebigquery.tables.getbigquery.tables.getDatabigquery.tables.updatebigquery.tables.updateData
您可以根据 Google Cloud 文档,通过网络控制台或 gcloud CLI 创建服务帐号。在网络控制台中,您可以通过导航到 IAM > 服务帐号 > 操作(下拉菜单)> 管理密钥来创建密钥
我们建议设置 BigQuery Admin 角色,因为它简化了权限设置。
下载服务帐号密钥#
创建服务帐号后,您需要为该服务帐号创建一个密钥。此密钥将签署 Analytics 服务器向 BigQuery 发出的 API 请求的 JWT。这可以通过 Google Cloud 控制台中的 IAM 部分完成。
Docker 镜像配置#
使用基于 docker-compose 的示例自托管堆栈,您可以使用以下命令包含日志记录相关服务
- 使用必要的环境变量更新
.env.example文件。
GOOGLE_PROJECT_IDGOOGLE_PROJECT_NUMBER
- 将您的服务帐号密钥放置在当前工作目录中,文件名为
gcloud.json。 - 在
docker-compose.yml中,取消注释注释# Uncomment to use Big Query backend for analytics下方的块部分 - 在
docker-compose.yml中,注释掉注释# Comment variables to use Big Query backend for analytics下方的块部分
此后,您可以使用以下命令启动示例堆栈
1# assuming you clone the supabase/supabase repo.2cd docker3docker compose -f docker-compose.ymlBigQuery 数据集存储位置#
目前,由 Analytics 存储和管理的所有 BigQuery 数据集,无论是通过 CLI 还是自托管,都将默认使用美国区域。
Vector 使用#
在 Docker Compose 示例中,Vector 用于日志记录管道,其中日志事件转发到 Analytics API 进行摄取。
定制自己的设置时,请参阅 Vector 配置文件。
您必须确保有效载荷与预期的事件模式结构匹配。如果没有正确的结构,将导致 Studio Logs UI 功能中断。
与平台的差异#
API 日志依赖于 Kong 而非 Supabase Cloud API Gateway。来自 Kong 的日志未包含平台专属数据。
在自托管设置中,所有日志都通过 Vector 路由到 Logflare。由于 Kong 将 API 请求路由到 PostgREST,因此自托管或本地部署将导致 Kong 请求日志。这将导致自托管 API 请求和 Supabase 平台请求之间的日志事件元数据存在差异。
生产建议#
为了在生产环境中自托管,我们建议执行以下操作以获得更好的体验。
确保 Logflare 位于防火墙后方,并限制除安全请求之外的所有网络访问。#
自托管的 Logflare 已禁用 UI 身份验证,并旨在向互联网开放。我们建议限制对仪表板的访问,该仪表板可通过 /dashboard 路径访问。如果需要仪表板访问来管理源,我们建议使用身份验证层,例如 VPN。
使用不同的 Postgres 数据库来存储 Logflare 数据。#
Logflare 需要 Postgres 数据库才能运行。但是,如果您的自托管 Postgres 服务出现问题,您将无法调试它,因为它也会同时使 Logflare 崩溃。
自托管示例仅用作运行整个堆栈的最小示例,但不建议将相同的数据库服务器用于生产和可观测性。
使用 BigQuery 作为 Logflare 后端#
当前的 Postgres 摄取后端尚未针对生产使用进行优化。对于更繁重的使用场景,我们建议使用 BigQuery。
我们建议在生产环境中使用 BigQuery 后端,因为它提供了更好的扩展性和查询/调试体验。
定期轮换加密密钥#
Logflare 服务器使用在 LOGFLARE_DB_ENCRYPTION_KEY 环境变量上设置的 Base64 加密密钥,对敏感数据库列执行静态加密。
要执行加密密钥轮换,请将已弃用的密钥移动到 LOGFLARE_DB_ENCRYPTION_KEY_RETIRED 环境变量中,并将 LOGFLARE_DB_ENCRYPTION_KEY 环境变量替换为新密钥。执行服务器重启并检查 info 日志以检测和执行迁移。
迁移完成后,您可以安全地删除已弃用的密钥。
列出端点
/api/endpoints响应代码
- 200
响应 (200)
1[2 {3 "cache_duration_seconds": 42,4 "enable_auth": true,5 "inserted_at": "2021-12-31T23:34:00Z",6 "max_limit": 42,7 "name": "lorem",8 "proactive_requerying_seconds": 42,9 "query": "lorem",10 "sandboxable": true,11 "source_mapping": {},12 "token": "lorem",13 "updated_at": "2021-12-31T23:34:00Z"14 }15]创建端点
/api/endpoints正文
- cache_duration_seconds可选integer
- enable_auth可选boolean
- inserted_at可选字符串
- max_limit可选integer
- name必需字符串
- proactive_requerying_seconds可选integer
- query必需字符串
- sandboxable可选boolean
- source_mapping可选object
- token可选字符串
- updated_at可选字符串
响应代码
- 201
- 404
响应 (201)
1{2 "cache_duration_seconds": 42,3 "enable_auth": true,4 "inserted_at": "2021-12-31T23:34:00Z",5 "max_limit": 42,6 "name": "lorem",7 "proactive_requerying_seconds": 42,8 "query": "lorem",9 "sandboxable": true,10 "source_mapping": {},11 "token": "lorem",12 "updated_at": "2021-12-31T23:34:00Z"13}删除端点
/api/endpoints/{token}路径参数
- token必需字符串
端点令牌
响应代码
- 204
- 404
响应 (204)
1{}获取端点
/api/endpoints/{token}路径参数
- token必需字符串
端点令牌
响应代码
- 200
- 404
响应 (200)
1{2 "cache_duration_seconds": 42,3 "enable_auth": true,4 "inserted_at": "2021-12-31T23:34:00Z",5 "max_limit": 42,6 "name": "lorem",7 "proactive_requerying_seconds": 42,8 "query": "lorem",9 "sandboxable": true,10 "source_mapping": {},11 "token": "lorem",12 "updated_at": "2021-12-31T23:34:00Z"13}更新端点
/api/endpoints/{token}路径参数
- token必需字符串
端点令牌
正文
- cache_duration_seconds可选integer
- enable_auth可选boolean
- inserted_at可选字符串
- max_limit可选integer
- name必需字符串
- proactive_requerying_seconds可选integer
- query必需字符串
- sandboxable可选boolean
- source_mapping可选object
- token可选字符串
- updated_at可选字符串
响应代码
- 201
- 404
响应 (201)
1{2 "cache_duration_seconds": 42,3 "enable_auth": true,4 "inserted_at": "2021-12-31T23:34:00Z",5 "max_limit": 42,6 "name": "lorem",7 "proactive_requerying_seconds": 42,8 "query": "lorem",9 "sandboxable": true,10 "source_mapping": {},11 "token": "lorem",12 "updated_at": "2021-12-31T23:34:00Z"13}列出源
/api/sources响应代码
- 200
响应 (200)
1{2 "items": {3 "properties": {4 "api_quota": {5 "type": "integer"6 },7 "bigquery_table_ttl": {8 "type": "integer"9 },10 "bq_table_id": {11 "type": "string"12 },13 "bq_table_schema": {14 "type": "object"15 },16 "custom_event_message_keys": {17 "type": "string"18 },19 "favorite": {20 "type": "boolean"21 },22 "has_rejected_events": {23 "type": "boolean"24 },25 "id": {},26 "inserted_at": {27 "format": "date-time",28 "type": "string"29 },30 "metrics": {31 "type": "object"32 },33 "name": {34 "type": "string"35 },36 "notifications": {37 "items": {38 "properties": {39 "other_email_notifications": {40 "type": "string"41 },42 "team_user_ids_for_email": {43 "allOf": {44 "type": "string"45 },46 "type": "array"47 },48 "team_user_ids_for_schema_updates": {49 "allOf": {50 "type": "string"51 },52 "type": "array"53 },54 "team_user_ids_for_sms": {55 "allOf": {56 "type": "string"57 },58 "type": "array"59 },60 "user_email_notifications": {61 "type": "boolean"62 },63 "user_schema_update_notifications": {64 "type": "boolean"65 },66 "user_text_notifications": {67 "type": "boolean"68 }69 },70 "title": "Notification",71 "type": "object"72 },73 "type": "array"74 },75 "public_token": {76 "type": "string"77 },78 "slack_hook_url": {79 "type": "string"80 },81 "token": {82 "type": "string"83 },84 "updated_at": {85 "format": "date-time",86 "type": "string"87 },88 "webhook_notification_url": {89 "type": "string"90 }91 },92 "required": [93 "name"94 ],95 "title": "Source",96 "type": "object"97 },98 "type": "array"99}创建源
/api/sources正文
- api_quota可选integer
- bigquery_table_ttl可选integer
- bq_table_id可选字符串
- bq_table_schema可选object
- custom_event_message_keys可选字符串
- favorite可选boolean
- has_rejected_events可选boolean
- id可选未知
- inserted_at可选字符串
- metrics可选object
- name必需字符串
- notifications可选Array<object>
- public_token可选字符串
- slack_hook_url可选字符串
- token可选字符串
- updated_at可选字符串
- webhook_notification_url可选字符串
响应代码
- 201
- 404
响应 (201)
1{2 "properties": {3 "api_quota": {4 "type": "integer"5 },6 "bigquery_table_ttl": {7 "type": "integer"8 },9 "bq_table_id": {10 "type": "string"11 },12 "bq_table_schema": {13 "type": "object"14 },15 "custom_event_message_keys": {16 "type": "string"17 },18 "favorite": {19 "type": "boolean"20 },21 "has_rejected_events": {22 "type": "boolean"23 },24 "id": {},25 "inserted_at": {26 "format": "date-time",27 "type": "string"28 },29 "metrics": {30 "type": "object"31 },32 "name": {33 "type": "string"34 },35 "notifications": {36 "items": {37 "properties": {38 "other_email_notifications": {39 "type": "string"40 },41 "team_user_ids_for_email": {42 "allOf": {43 "type": "string"44 },45 "type": "array"46 },47 "team_user_ids_for_schema_updates": {48 "allOf": {49 "type": "string"50 },51 "type": "array"52 },53 "team_user_ids_for_sms": {54 "allOf": {55 "type": "string"56 },57 "type": "array"58 },59 "user_email_notifications": {60 "type": "boolean"61 },62 "user_schema_update_notifications": {63 "type": "boolean"64 },65 "user_text_notifications": {66 "type": "boolean"67 }68 },69 "title": "Notification",70 "type": "object"71 },72 "type": "array"73 },74 "public_token": {75 "type": "string"76 },77 "slack_hook_url": {78 "type": "string"79 },80 "token": {81 "type": "string"82 },83 "updated_at": {84 "format": "date-time",85 "type": "string"86 },87 "webhook_notification_url": {88 "type": "string"89 }90 },91 "required": [92 "name"93 ],94 "title": "Source",95 "type": "object"96}删除源
/api/sources/{token}路径参数
- token必需字符串
源令牌
响应代码
- 204
- 404
响应 (204)
1{}获取源
/api/sources/{token}路径参数
- token必需字符串
源令牌
响应代码
- 200
- 404
响应 (200)
1{2 "properties": {3 "api_quota": {4 "type": "integer"5 },6 "bigquery_table_ttl": {7 "type": "integer"8 },9 "bq_table_id": {10 "type": "string"11 },12 "bq_table_schema": {13 "type": "object"14 },15 "custom_event_message_keys": {16 "type": "string"17 },18 "favorite": {19 "type": "boolean"20 },21 "has_rejected_events": {22 "type": "boolean"23 },24 "id": {},25 "inserted_at": {26 "format": "date-time",27 "type": "string"28 },29 "metrics": {30 "type": "object"31 },32 "name": {33 "type": "string"34 },35 "notifications": {36 "items": {37 "properties": {38 "other_email_notifications": {39 "type": "string"40 },41 "team_user_ids_for_email": {42 "allOf": {43 "type": "string"44 },45 "type": "array"46 },47 "team_user_ids_for_schema_updates": {48 "allOf": {49 "type": "string"50 },51 "type": "array"52 },53 "team_user_ids_for_sms": {54 "allOf": {55 "type": "string"56 },57 "type": "array"58 },59 "user_email_notifications": {60 "type": "boolean"61 },62 "user_schema_update_notifications": {63 "type": "boolean"64 },65 "user_text_notifications": {66 "type": "boolean"67 }68 },69 "title": "Notification",70 "type": "object"71 },72 "type": "array"73 },74 "public_token": {75 "type": "string"76 },77 "slack_hook_url": {78 "type": "string"79 },80 "token": {81 "type": "string"82 },83 "updated_at": {84 "format": "date-time",85 "type": "string"86 },87 "webhook_notification_url": {88 "type": "string"89 }90 },91 "required": [92 "name"93 ],94 "title": "Source",95 "type": "object"96}更新源
/api/sources/{token}路径参数
- token必需字符串
源令牌
正文
- api_quota可选integer
- bigquery_table_ttl可选integer
- bq_table_id可选字符串
- bq_table_schema可选object
- custom_event_message_keys可选字符串
- favorite可选boolean
- has_rejected_events可选boolean
- id可选未知
- inserted_at可选字符串
- metrics可选object
- name必需字符串
- notifications可选Array<object>
- public_token可选字符串
- slack_hook_url可选字符串
- token可选字符串
- updated_at可选字符串
- webhook_notification_url可选字符串
响应代码
- 201
- 404
响应 (201)
1{2 "properties": {3 "api_quota": {4 "type": "integer"5 },6 "bigquery_table_ttl": {7 "type": "integer"8 },9 "bq_table_id": {10 "type": "string"11 },12 "bq_table_schema": {13 "type": "object"14 },15 "custom_event_message_keys": {16 "type": "string"17 },18 "favorite": {19 "type": "boolean"20 },21 "has_rejected_events": {22 "type": "boolean"23 },24 "id": {},25 "inserted_at": {26 "format": "date-time",27 "type": "string"28 },29 "metrics": {30 "type": "object"31 },32 "name": {33 "type": "string"34 },35 "notifications": {36 "items": {37 "properties": {38 "other_email_notifications": {39 "type": "string"40 },41 "team_user_ids_for_email": {42 "allOf": {43 "type": "string"44 },45 "type": "array"46 },47 "team_user_ids_for_schema_updates": {48 "allOf": {49 "type": "string"50 },51 "type": "array"52 },53 "team_user_ids_for_sms": {54 "allOf": {55 "type": "string"56 },57 "type": "array"58 },59 "user_email_notifications": {60 "type": "boolean"61 },62 "user_schema_update_notifications": {63 "type": "boolean"64 },65 "user_text_notifications": {66 "type": "boolean"67 }68 },69 "title": "Notification",70 "type": "object"71 },72 "type": "array"73 },74 "public_token": {75 "type": "string"76 },77 "slack_hook_url": {78 "type": "string"79 },80 "token": {81 "type": "string"82 },83 "updated_at": {84 "format": "date-time",85 "type": "string"86 },87 "webhook_notification_url": {88 "type": "string"89 }90 },91 "required": [92 "name"93 ],94 "title": "Source",95 "type": "object"96}列出团队
/api/teams响应代码
- 200
响应 (200)
1[2 {3 "name": "lorem",4 "team_users": [5 {6 "email": "lorem",7 "name": "lorem"8 }9 ],10 "token": "lorem",11 "user": {12 "api_key": "lorem",13 "api_quota": 42,14 "bigquery_dataset_id": "lorem",15 "bigquery_dataset_location": "lorem",16 "bigquery_project_id": "lorem",17 "company": "lorem",18 "email": "lorem",19 "email_me_product": true,20 "email_preferred": "lorem",21 "image": "lorem",22 "name": "lorem",23 "phone": "lorem",24 "provider": "lorem",25 "token": "lorem"26 }27 }28]创建团队
/api/teams正文
- name必需字符串
- team_users可选Array<object>
- token可选字符串
- user可选object
响应代码
- 201
- 404
响应 (201)
1{2 "name": "lorem",3 "team_users": [4 {5 "email": "lorem",6 "name": "lorem"7 }8 ],9 "token": "lorem",10 "user": {11 "api_key": "lorem",12 "api_quota": 42,13 "bigquery_dataset_id": "lorem",14 "bigquery_dataset_location": "lorem",15 "bigquery_project_id": "lorem",16 "company": "lorem",17 "email": "lorem",18 "email_me_product": true,19 "email_preferred": "lorem",20 "image": "lorem",21 "name": "lorem",22 "phone": "lorem",23 "provider": "lorem",24 "token": "lorem"25 }26}删除团队
/api/teams/{token}路径参数
- token必需字符串
团队令牌
响应代码
- 204
- 404
响应 (204)
1{}获取团队
/api/teams/{token}路径参数
- token必需字符串
团队令牌
响应代码
- 200
- 404
响应 (200)
1{2 "name": "lorem",3 "team_users": [4 {5 "email": "lorem",6 "name": "lorem"7 }8 ],9 "token": "lorem",10 "user": {11 "api_key": "lorem",12 "api_quota": 42,13 "bigquery_dataset_id": "lorem",14 "bigquery_dataset_location": "lorem",15 "bigquery_project_id": "lorem",16 "company": "lorem",17 "email": "lorem",18 "email_me_product": true,19 "email_preferred": "lorem",20 "image": "lorem",21 "name": "lorem",22 "phone": "lorem",23 "provider": "lorem",24 "token": "lorem"25 }26}更新团队
/api/teams/{token}路径参数
- token必需字符串
团队令牌
正文
- name必需字符串
- team_users可选Array<object>
- token可选字符串
- user可选object
响应代码
- 201
- 404
响应 (201)
1{2 "name": "lorem",3 "team_users": [4 {5 "email": "lorem",6 "name": "lorem"7 }8 ],9 "token": "lorem",10 "user": {11 "api_key": "lorem",12 "api_quota": 42,13 "bigquery_dataset_id": "lorem",14 "bigquery_dataset_location": "lorem",15 "bigquery_project_id": "lorem",16 "company": "lorem",17 "email": "lorem",18 "email_me_product": true,19 "email_preferred": "lorem",20 "image": "lorem",21 "name": "lorem",22 "phone": "lorem",23 "provider": "lorem",24 "token": "lorem"25 }26}