mirror of
https://github.com/supabase/supabase.git
synced 2026-09-06 18:11:51 +08:00
## Context Couple of changes to the Unified Logs logic, mainly to align unified logs filters with legacy logs behaviour ## Changes involved - Postgrest + Storage logs will no longer overlap with edge logs source - They will specifically just pull logs from their own sources only - This will match legacy logs behaviour + also the observability overview behaviour as well - Re-introduce "API Gateway" as a log type (was there in the old UI) - Added service filters for convenience <img width="271" height="233" alt="image" src="https://github.com/user-attachments/assets/6264b7c5-e3e8-4db8-a378-4d8c46af3d62" /> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added **API Gateway** (“Edge”) logs to Unified Logs, including new sub-filters for auth, storage, and postgrest activity. * Updated the default log selection to include API Gateway logs. * **Bug Fixes** * Improved how log types are bucketed and filtered, ensuring edge, postgrest, and storage sources display under the correct views and toggles. * Refined “connection logs” filtering so results and counts remain consistent with the selected options. * **Style** * Refined the Unified Logs filter checkbox layout and nested expand/collapse controls. * **Tests** * Updated and expanded query tests to cover the new edge filter behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
147 lines
4.5 KiB
TypeScript
147 lines
4.5 KiB
TypeScript
import { cn } from 'ui'
|
|
|
|
import { LOG_TYPES_LABELS, METHODS, STATUS_CODE_LABELS } from './UnifiedLogs.constants'
|
|
import { ColumnSchema } from './UnifiedLogs.schema'
|
|
import { getLevelLabel } from './UnifiedLogs.utils'
|
|
import { LEVELS } from '@/components/ui/DataTable/DataTable.constants'
|
|
import { DataTableFilterField, Option } from '@/components/ui/DataTable/DataTable.types'
|
|
import { getLevelColor } from '@/components/ui/DataTable/DataTable.utils'
|
|
|
|
// instead of filterFields, maybe just 'fields' with a filterDisabled prop?
|
|
// that way, we could have 'message' or 'headers' field with label and value as well as type!
|
|
export const filterFields = [
|
|
{
|
|
label: 'Time Range',
|
|
value: 'date',
|
|
type: 'timerange',
|
|
defaultOpen: true,
|
|
commandDisabled: true,
|
|
},
|
|
{
|
|
label: 'Log Type',
|
|
value: 'log_type',
|
|
type: 'checkbox',
|
|
defaultOpen: true,
|
|
options: Object.entries(LOG_TYPES_LABELS).map(([value, label]) => ({
|
|
label,
|
|
value,
|
|
options:
|
|
// [Joshen] Nested options are treated as just boolean toggles atm for simplicity
|
|
// Refer to DataTableFilterCheckbox for their logic
|
|
value === 'edge'
|
|
? [
|
|
{ label: 'Auth', value: 'edge_auth' },
|
|
{ label: 'Storage', value: 'edge_storage' },
|
|
{ label: 'Postgrest', value: 'edge_postgrest' },
|
|
]
|
|
: value === 'postgres'
|
|
? [{ label: 'Connection logs', value: 'show_connection_logs' }]
|
|
: [],
|
|
})),
|
|
component: (props: Option) => {
|
|
return (
|
|
<div className="flex items-center w-full justify-between gap-2">
|
|
<span className="text-foreground/70 group-hover:text-accent-foreground text-xs">
|
|
{props.label}
|
|
</span>
|
|
</div>
|
|
)
|
|
},
|
|
},
|
|
{
|
|
label: 'Level',
|
|
value: 'level',
|
|
type: 'checkbox',
|
|
defaultOpen: true,
|
|
options: LEVELS.map((level) => ({ label: level, value: level })),
|
|
component: (props: Option) => {
|
|
// TODO: type `Option` with `options` values via Generics
|
|
const value = props.value as (typeof LEVELS)[number]
|
|
return (
|
|
<div className="flex w-full max-w-28 items-center justify-between gap-2">
|
|
<span className="capitalize text-foreground/70 group-hover:text-accent-foreground text-xs">
|
|
{props.label}
|
|
</span>
|
|
<div className="flex items-center gap-2">
|
|
<div className={cn('h-2.5 w-2.5 rounded-[2px]', getLevelColor(value).bg)} />
|
|
<span className="text-xs text-muted-foreground/70">{getLevelLabel(value)}</span>
|
|
</div>
|
|
</div>
|
|
)
|
|
},
|
|
},
|
|
{
|
|
label: 'Status',
|
|
value: 'status',
|
|
type: 'checkbox',
|
|
defaultOpen: false,
|
|
options: [],
|
|
hasDynamicOptions: true,
|
|
component: (props: Option) => {
|
|
if (typeof props.value === 'boolean') return null
|
|
if (typeof props.value === 'undefined') return null
|
|
|
|
const statusValue = String(props.value)
|
|
const statusLabel = STATUS_CODE_LABELS[statusValue as keyof typeof STATUS_CODE_LABELS]
|
|
|
|
return (
|
|
<div className="flex items-center gap-2 w-full min-w-0">
|
|
<span className="shrink-0 text-foreground">{statusValue}</span>
|
|
{statusLabel && (
|
|
<span className="text-[0.7rem] text-foreground-lighter truncate" title={statusLabel}>
|
|
{statusLabel}
|
|
</span>
|
|
)}
|
|
</div>
|
|
)
|
|
},
|
|
},
|
|
{
|
|
label: 'Method',
|
|
value: 'method',
|
|
type: 'checkbox',
|
|
defaultOpen: false,
|
|
options: METHODS.map((method) => ({ label: method, value: method })),
|
|
component: (props: Option) => {
|
|
return (
|
|
<span className="truncate block text-[0.75rem]" title={props.value as string}>
|
|
{props.value}
|
|
</span>
|
|
)
|
|
},
|
|
},
|
|
{
|
|
label: 'Pathname',
|
|
value: 'pathname',
|
|
type: 'checkbox',
|
|
defaultOpen: false,
|
|
options: [],
|
|
hasDynamicOptions: true,
|
|
hasAsyncSearch: false,
|
|
component: (props: Option) => {
|
|
return (
|
|
<span className="truncate block w-full text-[0.75rem]" title={props.value as string}>
|
|
{props.value}
|
|
</span>
|
|
)
|
|
},
|
|
},
|
|
{
|
|
label: 'Event message',
|
|
value: 'event_message',
|
|
type: 'checkbox',
|
|
defaultOpen: false,
|
|
options: [],
|
|
hasDynamicOptions: false,
|
|
hasAsyncSearch: false,
|
|
hidden: true,
|
|
component: (props: Option) => {
|
|
return (
|
|
<span className="truncate block w-full text-[0.75rem]" title={props.value as string}>
|
|
{props.value}
|
|
</span>
|
|
)
|
|
},
|
|
},
|
|
] satisfies DataTableFilterField<ColumnSchema>[]
|