mirror of
https://github.com/supabase/supabase.git
synced 2026-06-13 01:39:53 +08:00
## Problem High availability (Multigres) projects don't expose Multigres service logs in the Studio logs UI, so users on HA projects have no entry point to inspect them. ## Fix Add a `Multigres` logs collection, gated behind the `multigresLogs` ConfigCat flag **and** the project's `high_availability` flag (`useShowMultigresLogs`): - New `Multigres` entry in the logs sidebar `Collections`, linking to a new `multigres-logs` page that queries the `multigres_logs` table. - Wire `multigres_logs` through the logs constants, types, table SQL, query type, and service labels. - Row formatting: parse the JSON `event_message` and render `level` through `SeverityFormatter` and `msg` through `TextFormatter`, matching the other service collections (instead of dumping raw JSON). - `WARN` severity is now styled like `WARNING` (amber), since Multigres emits `level: WARN`. - Log detail drawer: parse the JSON `event_message` and spread its keys onto the log so each field (level, msg, query, error, connection_id, etc.) renders as its own collapsible row. - Single-log query omits the `metadata` column for `multigres_logs` (the table has no such column), fixing an `INVALID_ARGUMENT` error when opening the detail drawer. - Event chart: parse the level out of `event_message` via `JSON_VALUE` so error/warning bars are counted (the table has no top-level level column). - Add the `multigres_logs` source schema to the Field Reference drawer, same gating. ## Why a feature flag `high_availability` is an existing product feature that predates Multigres, so existing HA projects could otherwise see a broken collection querying a `multigres_logs` table they don't have. Requiring the `multigresLogs` flag ships the feature dark and decouples rollout from HA status. The flag must be created in ConfigCat before enabling; until then `useFlag` returns false and the feature stays hidden. ## How to test - Enable the `multigresLogs` flag (or override locally) and open a project where `high_availability` is `true`. - Navigate to `Logs`. Confirm a `Multigres` entry appears under `Collections` (after `Replication`). - Open it: the page loads at `/project/<ref>/logs/multigres-logs` and queries `multigres_logs`. - Confirm rows show a colored severity pill (including amber `WARN`) and a readable message rather than raw JSON. - Confirm the chart counts error/warning bars correctly. - Click a row: the detail drawer shows each parsed field as its own row, with no error. - Open the Field Reference drawer and confirm `Multigres` is listed as a source. - With the flag off, or on a non-HA project, confirm the collection and Field Reference source are both hidden. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Multigres added as a dedicated log source with its own Logs page, sidebar entry, and query type. * Log list and preview now parse Multigres payloads to surface timestamp, severity, and formatted message. * Multigres integrated into charting, prompt labels, and field-reference UI (hidden unless enabled). * New hook controls showing Multigres UI only when feature flag + HA project condition are met. * **Bug Fixes** * Severity rendering treats "WARN" the same as "WARNING". * **Tests** * Unit tests added for Multigres parsing and the show-Multigres hook. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
350 lines
16 KiB
TypeScript
350 lines
16 KiB
TypeScript
type LogTable =
|
|
| 'edge_logs'
|
|
| 'postgres_logs'
|
|
| 'function_logs'
|
|
| 'function_edge_logs'
|
|
| 'auth_logs'
|
|
| 'auth_audit_logs'
|
|
| 'realtime_logs'
|
|
| 'storage_logs'
|
|
| 'postgrest_logs'
|
|
| 'supavisor_logs'
|
|
| 'pgbouncer_logs'
|
|
| 'pg_cron_logs'
|
|
| 'pg_upgrade_logs'
|
|
| 'multigres_logs'
|
|
|
|
type LogSchema = {
|
|
name: string
|
|
reference: LogTable
|
|
fields: {
|
|
path: string
|
|
type: string
|
|
}[]
|
|
}
|
|
|
|
const schemas: LogSchema[] = [
|
|
{
|
|
name: 'API Gateway',
|
|
reference: 'edge_logs',
|
|
fields: [
|
|
{ path: 'id', type: 'string' },
|
|
{ path: 'timestamp', type: 'datetime' },
|
|
{ path: 'event_message', type: 'string' },
|
|
{ path: 'identifier', type: 'string' },
|
|
{ path: 'metadata.load_balancer_redirect_identifier', type: 'string' },
|
|
{ path: 'metadata.request.cf.asOrganization', type: 'string' },
|
|
{ path: 'metadata.request.cf.asn', type: 'number' },
|
|
{ path: 'metadata.request.cf.botManagement.corporateProxy', type: 'boolean' },
|
|
{ path: 'metadata.request.cf.botManagement.detectionIds', type: 'number[]' },
|
|
{ path: 'metadata.request.cf.botManagement.ja3Hash', type: 'string' },
|
|
{ path: 'metadata.request.cf.botManagement.score', type: 'number' },
|
|
{ path: 'metadata.request.cf.botManagement.staticResource', type: 'boolean' },
|
|
{ path: 'metadata.request.cf.botManagement.verifiedBot', type: 'boolean' },
|
|
{ path: 'metadata.request.cf.city', type: 'string' },
|
|
{ path: 'metadata.request.cf.clientTcpRtt', type: 'number' },
|
|
{ path: 'metadata.request.cf.clientTrustScore', type: 'number' },
|
|
{ path: 'metadata.request.cf.colo', type: 'string' },
|
|
{ path: 'metadata.request.cf.continent', type: 'string' },
|
|
{ path: 'metadata.request.cf.country', type: 'string' },
|
|
{ path: 'metadata.request.cf.edgeRequestKeepAliveStatus', type: 'number' },
|
|
{ path: 'metadata.request.cf.httpProtocol', type: 'string' },
|
|
{ path: 'metadata.request.cf.latitude', type: 'string' },
|
|
{ path: 'metadata.request.cf.longitude', type: 'string' },
|
|
{ path: 'metadata.request.cf.metroCode', type: 'string' },
|
|
{ path: 'metadata.request.cf.postalCode', type: 'string' },
|
|
{ path: 'metadata.request.cf.region', type: 'string' },
|
|
{ path: 'metadata.request.cf.timezone', type: 'string' },
|
|
{ path: 'metadata.request.cf.tlsCipher', type: 'string' },
|
|
{ path: 'metadata.request.cf.tlsClientAuth.certPresented', type: 'string' },
|
|
{ path: 'metadata.request.cf.tlsClientAuth.certRevoked', type: 'string' },
|
|
{ path: 'metadata.request.cf.tlsClientAuth.certVerified', type: 'string' },
|
|
{ path: 'metadata.request.cf.tlsExportedAuthenticator.clientFinished', type: 'string' },
|
|
{ path: 'metadata.request.cf.tlsExportedAuthenticator.clientHandshake', type: 'string' },
|
|
{ path: 'metadata.request.cf.tlsExportedAuthenticator.serverFinished', type: 'string' },
|
|
{ path: 'metadata.request.cf.tlsExportedAuthenticator.serverHandshake', type: 'string' },
|
|
{ path: 'metadata.request.cf.tlsVersion', type: 'string' },
|
|
{ path: 'metadata.request.headers.cf_connecting_ip', type: 'string' },
|
|
{ path: 'metadata.request.headers.cf_ipcountry', type: 'string' },
|
|
{ path: 'metadata.request.headers.cf_ray', type: 'string' },
|
|
{ path: 'metadata.request.headers.host', type: 'string' },
|
|
{ path: 'metadata.request.headers.referer', type: 'string' },
|
|
{ path: 'metadata.request.headers.x_client_info', type: 'string' },
|
|
{ path: 'metadata.request.headers.x_forwarded_proto', type: 'string' },
|
|
{ path: 'metadata.request.headers.x_real_ip', type: 'string' },
|
|
{ path: 'metadata.request.host', type: 'string' },
|
|
{ path: 'metadata.request.method', type: 'string' },
|
|
{ path: 'metadata.request.path', type: 'string' },
|
|
{ path: 'metadata.request.protocol', type: 'string' },
|
|
{ path: 'metadata.request.search', type: 'string' },
|
|
{ path: 'metadata.request.url', type: 'string' },
|
|
{ path: 'metadata.response.headers.cf_cache_status', type: 'string' },
|
|
{ path: 'metadata.response.headers.cf_ray', type: 'string' },
|
|
{ path: 'metadata.response.headers.content_location', type: 'string' },
|
|
{ path: 'metadata.response.headers.content_range', type: 'string' },
|
|
{ path: 'metadata.response.headers.content_type', type: 'string' },
|
|
{ path: 'metadata.response.headers.date', type: 'string' },
|
|
{ path: 'metadata.response.headers.sb_gateway_version', type: 'string' },
|
|
{ path: 'metadata.response.headers.transfer_encoding', type: 'string' },
|
|
{ path: 'metadata.response.headers.x_kong_proxy_latency', type: 'string' },
|
|
{ path: 'metadata.response.origin_time', type: 'number' },
|
|
{ path: 'metadata.response.status_code', type: 'number' },
|
|
],
|
|
},
|
|
{
|
|
name: 'Auth',
|
|
reference: 'auth_logs',
|
|
fields: [
|
|
{ path: 'event_message', type: 'string' },
|
|
{ path: 'id', type: 'string' },
|
|
{ path: 'timestamp', type: 'datetime' },
|
|
{ path: 'metadata.auth_event.action', type: 'string' },
|
|
{ path: 'metadata.auth_event.actor_id', type: 'string' },
|
|
{ path: 'metadata.auth_event.actor_via_sso', type: 'boolean' },
|
|
{ path: 'metadata.auth_event.actor_username', type: 'string' },
|
|
{ path: 'metadata.auth_event.log_type', type: 'string' },
|
|
{ path: 'metadata.auth_event.traits.provider', type: 'string' },
|
|
{ path: 'metadata.auth_event.traits.user_email', type: 'string' },
|
|
{ path: 'metadata.auth_event.traits.user_id', type: 'string' },
|
|
{ path: 'metadata.auth_event.traits.user_phone', type: 'string' },
|
|
{ path: 'metadata.component', type: 'string' },
|
|
{ path: 'metadata.duration', type: 'number' },
|
|
{ path: 'metadata.host', type: 'string' },
|
|
{ path: 'metadata.level', type: 'string' },
|
|
{ path: 'metadata.method', type: 'string' },
|
|
{ path: 'metadata.msg', type: 'string' },
|
|
{ path: 'metadata.path', type: 'string' },
|
|
{ path: 'metadata.referer', type: 'string' },
|
|
{ path: 'metadata.remote_addr', type: 'string' },
|
|
{ path: 'metadata.status', type: 'number' },
|
|
{ path: 'metadata.timestamp', type: 'string' },
|
|
],
|
|
},
|
|
{
|
|
name: 'Auth Audit Logs',
|
|
reference: 'auth_audit_logs',
|
|
fields: [
|
|
{ path: 'event_message', type: 'string' },
|
|
{ path: 'id', type: 'string' },
|
|
{ path: 'identifier', type: 'string' },
|
|
{ path: 'timestamp', type: 'datetime' },
|
|
{ path: 'metadata.auth_audit_event.action', type: 'string' },
|
|
{ path: 'metadata.auth_audit_event.actor_id', type: 'string' },
|
|
{ path: 'metadata.auth_audit_event.actor_name', type: 'string' },
|
|
{ path: 'metadata.auth_audit_event.actor_username', type: 'string' },
|
|
{ path: 'metadata.auth_audit_event.actor_via_sso', type: 'boolean' },
|
|
{ path: 'metadata.auth_audit_event.audit_log_id', type: 'string' },
|
|
{ path: 'metadata.auth_audit_event.created_at', type: 'string' },
|
|
{ path: 'metadata.auth_audit_event.log_type', type: 'string' },
|
|
{ path: 'metadata.auth_audit_event.request_id', type: 'string' },
|
|
{ path: 'metadata.auth_audit_event.user_agent', type: 'string' },
|
|
{ path: 'metadata.host', type: 'string' },
|
|
{ path: 'metadata.level', type: 'string' },
|
|
{ path: 'metadata.msg', type: 'string' },
|
|
],
|
|
},
|
|
{
|
|
name: 'Storage',
|
|
reference: 'storage_logs',
|
|
fields: [
|
|
{ path: 'event_message', type: 'string' },
|
|
{ path: 'id', type: 'string' },
|
|
{ path: 'timestamp', type: 'datetime' },
|
|
{ path: 'metadata.context.host', type: 'string' },
|
|
{ path: 'metadata.context.pid', type: 'number' },
|
|
{ path: 'metadata.level', type: 'string' },
|
|
{ path: 'metadata.project', type: 'string' },
|
|
{ path: 'metadata.req.headers.accept', type: 'string' },
|
|
{ path: 'metadata.req.headers.cf_connecting_ip', type: 'string' },
|
|
{ path: 'metadata.req.headers.cf_ray', type: 'string' },
|
|
{ path: 'metadata.req.headers.content_length', type: 'string' },
|
|
{ path: 'metadata.req.headers.content_type', type: 'string' },
|
|
{ path: 'metadata.req.headers.host', type: 'string' },
|
|
{ path: 'metadata.req.headers.referer', type: 'string' },
|
|
{ path: 'metadata.req.headers.user_agent', type: 'string' },
|
|
{ path: 'metadata.req.headers.x_client_info', type: 'string' },
|
|
{ path: 'metadata.req.headers.x_forwarded_proto', type: 'string' },
|
|
{ path: 'metadata.req.hostname', type: 'string' },
|
|
{ path: 'metadata.req.method', type: 'string' },
|
|
{ path: 'metadata.req.remoteAddress', type: 'string' },
|
|
{ path: 'metadata.req.remotePort', type: 'number' },
|
|
{ path: 'metadata.req.url', type: 'string' },
|
|
{ path: 'metadata.reqId', type: 'string' },
|
|
{ path: 'metadata.res.statusCode', type: 'number' },
|
|
{ path: 'metadata.res.headers.content_length', type: 'number' },
|
|
{ path: 'metadata.res.headers.content_type', type: 'string' },
|
|
{ path: 'metadata.responseTime', type: 'number' },
|
|
{ path: 'metadata.tenantId', type: 'string' },
|
|
{ path: 'metadata.rawError', type: 'string' },
|
|
],
|
|
},
|
|
{
|
|
name: 'Function Edge',
|
|
reference: 'function_edge_logs',
|
|
fields: [
|
|
{ path: 'event_message', type: 'string' },
|
|
{ path: 'id', type: 'string' },
|
|
{ path: 'timestamp', type: 'datetime' },
|
|
{ path: 'metadata.deployment_id', type: 'string' },
|
|
{ path: 'metadata.execution_time_ms', type: 'number' },
|
|
{ path: 'metadata.function_id', type: 'string' },
|
|
{ path: 'metadata.project_ref', type: 'string' },
|
|
{ path: 'metadata.request.headers.accept', type: 'string' },
|
|
{ path: 'metadata.request.headers.content_length', type: 'string' },
|
|
{ path: 'metadata.request.headers.host', type: 'string' },
|
|
{ path: 'metadata.request.headers.user_agent', type: 'string' },
|
|
{ path: 'metadata.request.host', type: 'string' },
|
|
{ path: 'metadata.request.method', type: 'string' },
|
|
{ path: 'metadata.request.pathname', type: 'string' },
|
|
{ path: 'metadata.request.protocol', type: 'string' },
|
|
{ path: 'metadata.request.url', type: 'string' },
|
|
{ path: 'metadata.response.headers.content_length', type: 'string' },
|
|
{ path: 'metadata.response.headers.content_type', type: 'string' },
|
|
{ path: 'metadata.response.headers.date', type: 'string' },
|
|
{ path: 'metadata.response.headers.server', type: 'string' },
|
|
{ path: 'metadata.response.headers.vary', type: 'string' },
|
|
{ path: 'metadata.response.status_code', type: 'number' },
|
|
{ path: 'metadata.version', type: 'string' },
|
|
],
|
|
},
|
|
{
|
|
name: 'Function Runtime',
|
|
reference: 'function_logs',
|
|
fields: [
|
|
{ path: 'event_message', type: 'string' },
|
|
{ path: 'id', type: 'string' },
|
|
{ path: 'timestamp', type: 'datetime' },
|
|
{ path: 'metadata.deployment_id', type: 'string' },
|
|
{ path: 'metadata.event_type', type: 'string' },
|
|
{ path: 'metadata.execution_id', type: 'string' },
|
|
{ path: 'metadata.function_id', type: 'string' },
|
|
{ path: 'metadata.level', type: 'string' },
|
|
{ path: 'metadata.project_ref', type: 'string' },
|
|
{ path: 'metadata.region', type: 'string' },
|
|
{ path: 'metadata.timestamp', type: 'string' },
|
|
{ path: 'metadata.version', type: 'string' },
|
|
],
|
|
},
|
|
{
|
|
name: 'Postgres',
|
|
reference: 'postgres_logs',
|
|
fields: [
|
|
{ path: 'event_message', type: 'string' },
|
|
{ path: 'id', type: 'string' },
|
|
{ path: 'timestamp', type: 'datetime' },
|
|
{ path: 'identifier', type: 'string' },
|
|
{ path: 'metadata.host', type: 'string' },
|
|
{ path: 'metadata.parsed.backend_type', type: 'string' },
|
|
{ path: 'metadata.parsed.command_tag', type: 'string' },
|
|
{ path: 'metadata.parsed.connection_from', type: 'string' },
|
|
{ path: 'metadata.parsed.database_name', type: 'string' },
|
|
{ path: 'metadata.parsed.error_severity', type: 'string' },
|
|
{ path: 'metadata.parsed.process_id', type: 'number' },
|
|
{ path: 'metadata.parsed.query_id', type: 'number' },
|
|
{ path: 'metadata.parsed.session_id', type: 'string' },
|
|
{ path: 'metadata.parsed.session_line_num', type: 'number' },
|
|
{ path: 'metadata.parsed.session_start_time', type: 'string' },
|
|
{ path: 'metadata.parsed.sql_state_code', type: 'string' },
|
|
{ path: 'metadata.parsed.timestamp', type: 'string' },
|
|
{ path: 'metadata.parsed.transaction_id', type: 'number' },
|
|
{ path: 'metadata.parsed.user_name', type: 'string' },
|
|
{ path: 'metadata.parsed.virtual_transaction_id', type: 'string' },
|
|
],
|
|
},
|
|
{
|
|
name: 'Realtime',
|
|
reference: 'realtime_logs',
|
|
fields: [
|
|
{ path: 'event_message', type: 'string' },
|
|
{ path: 'id', type: 'string' },
|
|
{ path: 'timestamp', type: 'datetime' },
|
|
{ path: 'metadata.level', type: 'string' },
|
|
{ path: 'metadata.measurements.connected', type: 'number' },
|
|
{ path: 'metadata.measurements.connected_cluster', type: 'number' },
|
|
{ path: 'metadata.measurements.limit', type: 'number' },
|
|
{ path: 'metadata.measurements.sum', type: 'number' },
|
|
{ path: 'metadata.external_id', type: 'string' },
|
|
],
|
|
},
|
|
{
|
|
name: 'PostgREST',
|
|
reference: 'postgrest_logs',
|
|
fields: [
|
|
{ path: 'event_message', type: 'string' },
|
|
{ path: 'id', type: 'string' },
|
|
{ path: 'identifier', type: 'string' },
|
|
{ path: 'timestamp', type: 'datetime' },
|
|
{ path: 'metadata.host', type: 'string' },
|
|
],
|
|
},
|
|
{
|
|
name: 'Supavisor (Shared Pooler)',
|
|
reference: 'supavisor_logs',
|
|
fields: [
|
|
{ path: 'event_message', type: 'string' },
|
|
{ path: 'id', type: 'string' },
|
|
{ path: 'timestamp', type: 'datetime' },
|
|
{ path: 'metadata.context.application', type: 'string' },
|
|
{ path: 'metadata.context.domain', type: 'string[]' },
|
|
{ path: 'metadata.context.file', type: 'string' },
|
|
{ path: 'metadata.context.function', type: 'string' },
|
|
{ path: 'metadata.context.gl', type: 'string' },
|
|
{ path: 'metadata.context.line', type: 'number' },
|
|
{ path: 'metadata.context.mfa', type: 'string[]' },
|
|
{ path: 'metadata.context.module', type: 'string' },
|
|
{ path: 'metadata.context.pid', type: 'string' },
|
|
{ path: 'metadata.context.time', type: 'number' },
|
|
{ path: 'metadata.context.vm.node', type: 'string' },
|
|
{ path: 'metadata.db_name', type: 'string' },
|
|
{ path: 'metadata.instance_id', type: 'string' },
|
|
{ path: 'metadata.level', type: 'string' },
|
|
{ path: 'metadata.project', type: 'string' },
|
|
{ path: 'metadata.region', type: 'string' },
|
|
{ path: 'metadata.type', type: 'string' },
|
|
{ path: 'metadata.user', type: 'string' },
|
|
],
|
|
},
|
|
{
|
|
name: 'PgBouncer (Dedicated Pooler)',
|
|
reference: 'pgbouncer_logs',
|
|
fields: [
|
|
{ path: 'id', type: 'string' },
|
|
{ path: 'event_message', type: 'string' },
|
|
{ path: 'file', type: 'string' },
|
|
{ path: 'timestamp', type: 'datetime' },
|
|
{ path: 'metadata.host', type: 'string' },
|
|
{ path: 'project', type: 'string' },
|
|
],
|
|
},
|
|
{
|
|
name: 'Database Version Upgrade',
|
|
reference: 'pg_upgrade_logs',
|
|
fields: [
|
|
{ path: 'event_message', type: 'string' },
|
|
{ path: 'id', type: 'string' },
|
|
{ path: 'timestamp', type: 'datetime' },
|
|
],
|
|
},
|
|
{
|
|
name: 'Multigres',
|
|
reference: 'multigres_logs',
|
|
fields: [
|
|
{ path: 'cluster', type: 'string' },
|
|
{ path: 'component', type: 'string' },
|
|
{ path: 'event_message', type: 'string' },
|
|
{ path: 'id', type: 'string' },
|
|
{ path: 'namespace', type: 'string' },
|
|
{ path: 'node_name', type: 'string' },
|
|
{ path: 'pod_name', type: 'string' },
|
|
{ path: 'project', type: 'string' },
|
|
{ path: 'region', type: 'string' },
|
|
{ path: 'stack', type: 'string' },
|
|
{ path: 'timestamp', type: 'datetime' },
|
|
],
|
|
},
|
|
]
|
|
|
|
export default {
|
|
schemas,
|
|
}
|