mirror of
https://github.com/supabase/supabase.git
synced 2026-09-06 09:59:03 +08:00
## Problem The Multigres log type is available in the legacy logs collections but was missing from the new unified logs, so Multigres logs could not be selected or viewed there. ## Fix Wire the `multigres_logs` source into unified logs the same way the other single-source types (Realtime, Supavisor, PgBouncer) are: a display label, a filter condition, the derived `log_type` expression, a display-casing entry, and a sidebar icon. ## How to test - Open a project with Multigres logs and go to the new unified logs view - Open the Log Type filter and confirm "Multigres" appears as an option - Select "Multigres" and confirm rows from the `multigres_logs` source are returned and labeled "Multigres" with the network icon - Expected result: Multigres logs are filterable and display correctly, matching the legacy logs behavior ## Notes Level/severity uses the shared `severity_text` fallback that all non-HTTP sources rely on. If Multigres rows come back always classified as success, the OTEL pipeline may not populate `severity_text` for this source (legacy logs read the level from a JSON `event_message`), which would need a source-specific level branch. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added support for the **Multigres** log type in Unified Logs (labels, icon, and derived filtering/grouping/counting). * Unified Logs now renders Multigres **event_message** by extracting the `msg` field from valid JSON, with correct capitalization. * Unified Logs row click telemetry now recognizes **Multigres**. * The **Multigres** log type option is hidden when the selected project is not high-availability. * **Tests** * Added/updated unit tests for Multigres event-message parsing and shared event-message display behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
125 lines
4.3 KiB
TypeScript
125 lines
4.3 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
|
|
import {
|
|
buildUnifiedLogsUrl,
|
|
gateMultigresLogType,
|
|
getEventMessageDisplay,
|
|
parseMultigresEventMessage,
|
|
} from './UnifiedLogs.utils'
|
|
|
|
describe('buildUnifiedLogsUrl', () => {
|
|
const parse = (url: string) => {
|
|
const [path, query] = url.split('?')
|
|
return { path, params: new URLSearchParams(query) }
|
|
}
|
|
|
|
it('targets the project logs route with a log_type filter', () => {
|
|
const { path, params } = parse(buildUnifiedLogsUrl({ projectRef: 'abc', logType: 'postgres' }))
|
|
expect(path).toBe('/project/abc/logs')
|
|
expect(params.get('filter')).toBe('log_type:eq:postgres')
|
|
expect(params.has('date')).toBe(false)
|
|
})
|
|
|
|
it('preserves multi-word log types once decoded', () => {
|
|
const { params } = parse(buildUnifiedLogsUrl({ projectRef: 'abc', logType: 'edge function' }))
|
|
expect(params.get('filter')).toBe('log_type:eq:edge function')
|
|
})
|
|
|
|
it('adds the date range as an epoch-ms pair when start and end are provided', () => {
|
|
const start = new Date('2026-05-08T00:00:00.000Z')
|
|
const end = new Date('2026-05-08T01:00:00.000Z')
|
|
const { params } = parse(
|
|
buildUnifiedLogsUrl({ projectRef: 'abc', logType: 'auth', start, end })
|
|
)
|
|
expect(params.get('date')).toBe(`${start.valueOf()}-${end.valueOf()}`)
|
|
})
|
|
|
|
it('accepts ISO strings for the date range', () => {
|
|
const start = '2026-05-08T00:00:00.000Z'
|
|
const end = '2026-05-08T01:00:00.000Z'
|
|
const { params } = parse(
|
|
buildUnifiedLogsUrl({ projectRef: 'abc', logType: 'auth', start, end })
|
|
)
|
|
expect(params.get('date')).toBe(`${new Date(start).valueOf()}-${new Date(end).valueOf()}`)
|
|
})
|
|
|
|
it('omits the date range when only one bound is provided', () => {
|
|
const { params } = parse(
|
|
buildUnifiedLogsUrl({ projectRef: 'abc', logType: 'storage', start: new Date() })
|
|
)
|
|
expect(params.has('date')).toBe(false)
|
|
})
|
|
})
|
|
|
|
describe('parseMultigresEventMessage', () => {
|
|
it('extracts the msg field from a stringified JSON payload', () => {
|
|
const value = JSON.stringify({
|
|
time: '2026-07-03T09:42:12.344925698Z',
|
|
level: 'INFO',
|
|
msg: 'user pool capacity updated',
|
|
user: 'supabase_admin',
|
|
})
|
|
expect(parseMultigresEventMessage(value)).toBe('user pool capacity updated')
|
|
})
|
|
|
|
it('returns the raw string when it is not JSON', () => {
|
|
expect(parseMultigresEventMessage('plain text message')).toBe('plain text message')
|
|
})
|
|
|
|
it('returns the raw string when msg is missing or empty', () => {
|
|
expect(parseMultigresEventMessage(JSON.stringify({ level: 'INFO' }))).toBe('{"level":"INFO"}')
|
|
expect(parseMultigresEventMessage(JSON.stringify({ msg: ' ' }))).toBe('{"msg":" "}')
|
|
})
|
|
|
|
it('passes empty values through unchanged', () => {
|
|
expect(parseMultigresEventMessage(undefined)).toBeUndefined()
|
|
expect(parseMultigresEventMessage('')).toBe('')
|
|
})
|
|
})
|
|
|
|
describe('getEventMessageDisplay', () => {
|
|
it('parses multigres rows into their msg field and capitalizes them', () => {
|
|
const value = JSON.stringify({ level: 'INFO', msg: 'Configuring synchronous replication' })
|
|
expect(getEventMessageDisplay('multigres', value)).toEqual({
|
|
message: 'Configuring synchronous replication',
|
|
capitalize: true,
|
|
})
|
|
})
|
|
|
|
it('leaves non-parsed log types untouched and uncapitalized', () => {
|
|
expect(getEventMessageDisplay('postgres', 'relation does not exist')).toEqual({
|
|
message: 'relation does not exist',
|
|
capitalize: false,
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('gateMultigresLogType', () => {
|
|
const fields = [
|
|
{ value: 'date' },
|
|
{
|
|
value: 'log_type',
|
|
options: [
|
|
{ label: 'Postgres', value: 'postgres' },
|
|
{ label: 'Multigres', value: 'multigres' },
|
|
],
|
|
},
|
|
]
|
|
|
|
it('drops the multigres log_type option when the flag is disabled', () => {
|
|
const gated = gateMultigresLogType(fields, false)
|
|
const logType = gated.find((field) => field.value === 'log_type')
|
|
expect(logType?.options?.map((option) => option.value)).toEqual(['postgres'])
|
|
})
|
|
|
|
it('keeps the multigres option when the flag is enabled', () => {
|
|
const gated = gateMultigresLogType(fields, true)
|
|
expect(gated).toBe(fields)
|
|
})
|
|
|
|
it('leaves non log_type fields untouched', () => {
|
|
const gated = gateMultigresLogType(fields, false)
|
|
expect(gated.find((field) => field.value === 'date')).toEqual({ value: 'date' })
|
|
})
|
|
})
|