Skip to content

Commit ec43e11

Browse files
authored
Chore/fix unified logs (supabase#36650)
* Fix z-index position of row * Fix column widths to make it fit a laptop viewport better * Fix auto fetching when scrolling to the bottom * Small style fixes + decouple count loading state from table loading state * Refactor row uuid to just id, add de-duping logic to logs data, standardise unified logs query options * Clean up logs * Misc styling tweaks * Reinstate prev cursor and direction for live mode * Clean * Revert text sizes * Remove now resolved comments
1 parent a403f1c commit ec43e11

20 files changed

+261
-335
lines changed

apps/studio/components/interfaces/UnifiedLogs/UnifiedLogs.constants.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export const CHART_CONFIG = {
3434
} satisfies ChartConfig
3535

3636
export const REGIONS = ['ams', 'fra', 'gru', 'hkg', 'iad', 'syd'] as const
37-
export const METHODS = ['GET', 'POST', 'PUT', 'DELETE'] as const
37+
export const METHODS = ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'] as const
3838
export const LOG_TYPES = [
3939
'auth',
4040
'edge',
@@ -86,5 +86,5 @@ export const SEARCH_PARAMS_PARSER = {
8686
live: parseAsBoolean.withDefault(false),
8787

8888
// REQUIRED FOR SELECTION
89-
uuid: parseAsString,
89+
id: parseAsString,
9090
}

apps/studio/components/interfaces/UnifiedLogs/UnifiedLogs.fields.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export const filterFields = [
3131
const value = props.value as (typeof LEVELS)[number]
3232
return (
3333
<div className="flex w-full max-w-28 items-center justify-between gap-2 font-mono">
34-
<span className="capitalize text-foreground/70 group-hover:text-accent-foreground">
34+
<span className="capitalize text-foreground/70 group-hover:text-accent-foreground text-xs">
3535
{props.label}
3636
</span>
3737
<div className="flex items-center gap-2">
@@ -51,7 +51,7 @@ export const filterFields = [
5151
component: (props: Option) => {
5252
return (
5353
<div className="flex w-full items-center justify-between gap-2 font-mono">
54-
<span className="capitalize text-foreground/70 group-hover:text-accent-foreground">
54+
<span className="capitalize text-foreground/70 group-hover:text-accent-foreground text-xs">
5555
{props.label}
5656
</span>
5757
<span className="text-xs text-muted-foreground/70">{props.value}</span>
@@ -106,7 +106,7 @@ export const filterFields = [
106106

107107
export const sheetFields = [
108108
{
109-
id: 'uuid',
109+
id: 'id',
110110
label: 'Request ID',
111111
type: 'readonly',
112112
skeletonClassName: 'w-64',

apps/studio/components/interfaces/UnifiedLogs/UnifiedLogs.schema.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import { LOG_TYPES, METHODS, REGIONS } from './UnifiedLogs.constants'
1010
export const columnSchema = z.object({
1111
id: z.string(),
1212
log_type: z.enum(LOG_TYPES),
13-
uuid: z.string(),
1413
method: z.enum(METHODS),
1514
host: z.string(),
1615
pathname: z.string(),

apps/studio/components/interfaces/UnifiedLogs/UnifiedLogs.tsx

Lines changed: 42 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ import {
4545
TabsList_Shadcn_ as TabsList,
4646
TabsTrigger_Shadcn_ as TabsTrigger,
4747
} from 'ui'
48-
import { COLUMNS } from './components/Columns'
48+
import { UNIFIED_LOGS_COLUMNS } from './components/Columns'
4949
import { MemoizedDataTableSheetContent } from './components/DataTableSheetContent'
5050
import { FunctionLogsTab } from './components/FunctionLogsTab'
5151
import { CHART_CONFIG, SEARCH_PARAMS_PARSER } from './UnifiedLogs.constants'
@@ -54,19 +54,16 @@ import { useLiveMode, useResetFocus } from './UnifiedLogs.hooks'
5454
import { QuerySearchParamsType } from './UnifiedLogs.types'
5555
import { getFacetedUniqueValues, getLevelRowClassName, logEventBus } from './UnifiedLogs.utils'
5656

57-
// Debug mode flag - set to true to enable detailed logs
58-
const DEBUG_FILTER_PROCESSING = false
59-
6057
export const UnifiedLogs = () => {
6158
useResetFocus()
6259

6360
const { ref: projectRef } = useParams()
6461
const [search, setSearch] = useQueryStates(SEARCH_PARAMS_PARSER)
6562

66-
const { sort, start, size, uuid, cursor, direction, live, ...filter } = search
63+
const { sort, start, size, id, cursor, direction, live, ...filter } = search
6764
const defaultColumnSorting = sort ? [sort] : []
6865
const defaultColumnVisibility = { uuid: false }
69-
const defaultRowSelection = search.uuid ? { [search.uuid]: true } : {}
66+
const defaultRowSelection = search.id ? { [search.id]: true } : {}
7067
const defaultColumnFilters = Object.entries(filter)
7168
.map(([key, value]) => ({ id: key, value }))
7269
.filter(({ value }) => value ?? undefined)
@@ -86,11 +83,11 @@ export const UnifiedLogs = () => {
8683
[]
8784
)
8885

89-
// Create a stable query key object by removing nulls/undefined, uuid, and live
86+
// Create a stable query key object by removing nulls/undefined, id, and live
9087
// Mainly to prevent the react queries from unnecessarily re-fetching
9188
const searchParameters = Object.entries(search).reduce(
9289
(acc, [key, value]) => {
93-
if (!['uuid', 'live'].includes(key) && value !== null && value !== undefined) {
90+
if (!['id', 'live'].includes(key) && value !== null && value !== undefined) {
9491
acc[key] = value
9592
}
9693
return acc
@@ -107,24 +104,28 @@ export const UnifiedLogs = () => {
107104
fetchNextPage,
108105
fetchPreviousPage,
109106
} = useUnifiedLogsInfiniteQuery({ projectRef, search: searchParameters })
110-
const { data: counts } = useUnifiedLogsCountQuery({ projectRef, search: searchParameters })
107+
const { data: counts, isLoading: isLoadingCounts } = useUnifiedLogsCountQuery({
108+
projectRef,
109+
search: searchParameters,
110+
})
111111
const { data: unifiedLogsChart = [] } = useUnifiedLogsChartQuery({
112112
projectRef,
113113
search: searchParameters,
114114
})
115115

116-
const flatData = useMemo(() => {
116+
const rawFlatData = useMemo(() => {
117117
return unifiedLogsData?.pages?.flatMap((page) => page.data ?? []) ?? []
118118
}, [unifiedLogsData?.pages])
119+
// [Joshen] Refer to unified-logs-infinite-query on why the need to deupe
120+
const flatData = useMemo(() => {
121+
return rawFlatData.filter((value, idx) => {
122+
return idx === rawFlatData.findIndex((x) => x.id === value.id)
123+
})
124+
}, [rawFlatData])
119125
const liveMode = useLiveMode(flatData)
120126

121-
// REMINDER: meta data is always the same for all pages as filters do not change(!)
122-
const lastPage = unifiedLogsData?.pages?.[unifiedLogsData?.pages.length - 1]
123-
124-
// Use the totalCount from chartDataResult which gives us the actual count of logs in the time period
125-
// instead of the hardcoded 10000 value
126127
const totalDBRowCount = counts?.totalRowCount
127-
const filterDBRowCount = lastPage?.meta?.filterRowCount
128+
const filterDBRowCount = flatData.length
128129

129130
const facets = counts?.facets
130131
const totalFetched = flatData?.length
@@ -143,12 +144,12 @@ export const UnifiedLogs = () => {
143144
const rowTimestamp = row.original.timestamp
144145
const isPast = rowTimestamp <= (liveMode.timestamp || -1)
145146
const levelClassName = getLevelRowClassName(row.original.level as any)
146-
return cn(levelClassName, isPast ? 'opacity-50' : 'opacity-100')
147+
return cn(levelClassName, isPast ? 'opacity-50' : 'opacity-100', 'h-[30px]')
147148
}
148149

149150
const table = useReactTable({
150151
data: flatData,
151-
columns: COLUMNS,
152+
columns: UNIFIED_LOGS_COLUMNS,
152153
state: {
153154
columnFilters,
154155
sorting,
@@ -160,7 +161,7 @@ export const UnifiedLogs = () => {
160161
columnResizeMode: 'onChange',
161162
filterFns: { inDateRange, arrSome },
162163
meta: { getRowClassName },
163-
getRowId: (row) => row.uuid,
164+
getRowId: (row) => row.id,
164165
onColumnVisibilityChange: setColumnVisibility,
165166
onColumnFiltersChange: setColumnFilters,
166167
onRowSelectionChange: setRowSelection,
@@ -207,52 +208,22 @@ export const UnifiedLogs = () => {
207208
}, [facets])
208209

209210
useEffect(() => {
210-
if (DEBUG_FILTER_PROCESSING) console.log('========== FILTER CHANGE DETECTED ==========')
211-
if (DEBUG_FILTER_PROCESSING) console.log('Raw columnFilters:', JSON.stringify(columnFilters))
212-
213-
// Check for level filters specifically
214-
const levelColumnFilter = columnFilters.find((filter) => filter.id === 'level')
215-
if (DEBUG_FILTER_PROCESSING) console.log('Level column filter:', levelColumnFilter)
216-
217211
const columnFiltersWithNullable = filterFields.map((field) => {
218212
const filterValue = columnFilters.find((filter) => filter.id === field.value)
219-
if (DEBUG_FILTER_PROCESSING) console.log(`Processing field ${field.value}:`, filterValue)
220213
if (!filterValue) return { id: field.value, value: null }
221214
return { id: field.value, value: filterValue.value }
222215
})
223216

224-
// Debug level filter specifically
225-
const levelFilter = columnFiltersWithNullable.find((f) => f.id === 'level')
226-
if (DEBUG_FILTER_PROCESSING) console.log('Level filter after mapping:', levelFilter)
227-
228-
if (DEBUG_FILTER_PROCESSING)
229-
console.log('All column filters after mapping:', columnFiltersWithNullable)
230-
231217
const search = columnFiltersWithNullable.reduce(
232218
(prev, curr) => {
233-
if (DEBUG_FILTER_PROCESSING)
234-
console.log(`Processing filter for URL: ${curr.id}`, {
235-
value: curr.value,
236-
type: Array.isArray(curr.value) ? 'array' : typeof curr.value,
237-
isEmpty: Array.isArray(curr.value) && curr.value.length === 0,
238-
isNull: curr.value === null,
239-
})
240-
241219
// Add to search parameters
242220
prev[curr.id as string] = curr.value
243221
return prev
244222
},
245223
{} as Record<string, unknown>
246224
)
247225

248-
if (DEBUG_FILTER_PROCESSING) console.log('Final search object to be set in URL:', search)
249-
if (DEBUG_FILTER_PROCESSING) console.log('Level value in final search:', search.level)
250-
if (DEBUG_FILTER_PROCESSING) console.log('Is level in search object:', 'level' in search)
251-
252-
// Set the search state without any console logs
253-
if (DEBUG_FILTER_PROCESSING) console.log('CALLING setSearch with:', JSON.stringify(search))
254226
setSearch(search)
255-
if (DEBUG_FILTER_PROCESSING) console.log('========== END FILTER PROCESSING ==========')
256227

257228
// eslint-disable-next-line react-hooks/exhaustive-deps
258229
}, [columnFilters])
@@ -265,10 +236,10 @@ export const UnifiedLogs = () => {
265236
useEffect(() => {
266237
if (isLoading || isFetching) return
267238
if (Object.keys(rowSelection)?.length && !selectedRow) {
268-
setSearch({ uuid: null })
239+
setSearch({ id: null })
269240
setRowSelection({})
270241
} else {
271-
setSearch({ uuid: Object.keys(rowSelection)?.[0] || null })
242+
setSearch({ id: Object.keys(rowSelection)?.[0] || null })
272243
}
273244
// eslint-disable-next-line react-hooks/exhaustive-deps
274245
}, [rowSelection, selectedRow, isLoading, isFetching])
@@ -287,15 +258,17 @@ export const UnifiedLogs = () => {
287258
return (
288259
<DataTableProvider
289260
table={table}
290-
columns={COLUMNS}
261+
columns={UNIFIED_LOGS_COLUMNS}
291262
filterFields={filterFields}
292263
columnFilters={columnFilters}
293264
sorting={sorting}
294265
rowSelection={rowSelection}
295266
columnOrder={columnOrder}
296267
columnVisibility={columnVisibility}
297268
enableColumnOrdering={true}
298-
isLoading={isFetching || isLoading}
269+
isFetching={isFetching}
270+
isLoading={isLoading}
271+
isLoadingCounts={isLoadingCounts}
299272
getFacetedUniqueValues={getFacetedUniqueValues(facets)}
300273
>
301274
<DataTableSideBarLayout topBarHeight={topBarHeight}>
@@ -329,26 +302,22 @@ export const UnifiedLogs = () => {
329302
<ResizablePanel defaultSize={selectedRowKey ? 60 : 100} minSize={30} className="h-full">
330303
<ResizablePanelGroup key="main-logs" direction="vertical" className="h-full">
331304
<ResizablePanel defaultSize={100} minSize={30}>
332-
<div className="h-full overflow-auto">
333-
<DataTableInfinite
334-
columns={COLUMNS}
335-
totalRows={totalDBRowCount}
336-
filterRows={filterDBRowCount}
337-
totalRowsFetched={totalFetched}
338-
isFetching={isFetching}
339-
isLoading={isLoading}
340-
fetchNextPage={fetchNextPage}
341-
hasNextPage={hasNextPage}
342-
renderLiveRow={(props) => {
343-
if (!liveMode.timestamp) return null
344-
if ((props?.row as any).original.uuid !== liveMode?.row?.uuid) return null
345-
return <LiveRow colSpan={COLUMNS.length - 1} />
346-
}}
347-
setColumnOrder={setColumnOrder}
348-
setColumnVisibility={setColumnVisibility}
349-
searchParamsParser={SEARCH_PARAMS_PARSER}
350-
/>
351-
</div>
305+
<DataTableInfinite
306+
columns={UNIFIED_LOGS_COLUMNS}
307+
totalRows={totalDBRowCount}
308+
filterRows={filterDBRowCount}
309+
totalRowsFetched={totalFetched}
310+
fetchNextPage={fetchNextPage}
311+
hasNextPage={hasNextPage}
312+
renderLiveRow={(props) => {
313+
if (!liveMode.timestamp) return null
314+
if (props?.row?.original.id !== liveMode?.row?.id) return null
315+
return <LiveRow colSpan={UNIFIED_LOGS_COLUMNS.length - 1} />
316+
}}
317+
setColumnOrder={setColumnOrder}
318+
setColumnVisibility={setColumnVisibility}
319+
searchParamsParser={SEARCH_PARAMS_PARSER}
320+
/>
352321
</ResizablePanel>
353322
{selectedRow?.original?.logs && selectedRow?.original?.logs?.length > 0 && (
354323
<>

apps/studio/components/interfaces/UnifiedLogs/components/Columns.tsx

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { HoverCardTimestamp } from './HoverCardTimestamp'
1010
import { LogTypeIcon } from './LogTypeIcon'
1111
import { TextWithTooltip } from './TextWithTooltip'
1212

13-
export const COLUMNS: ColumnDef<ColumnSchema>[] = [
13+
export const UNIFIED_LOGS_COLUMNS: ColumnDef<ColumnSchema>[] = [
1414
{
1515
accessorKey: 'level',
1616
header: '',
@@ -39,9 +39,9 @@ export const COLUMNS: ColumnDef<ColumnSchema>[] = [
3939
},
4040
filterFn: (row, columnId, filterValue) => true,
4141
enableResizing: false,
42-
size: 190,
43-
minSize: 190,
44-
maxSize: 190,
42+
size: 130,
43+
minSize: 130,
44+
maxSize: 130,
4545
meta: {
4646
headerClassName:
4747
'w-[--header-date-size] max-w-[--header-date-size] min-w-[--header-date-size]',
@@ -55,16 +55,16 @@ export const COLUMNS: ColumnDef<ColumnSchema>[] = [
5555
cell: ({ row }) => {
5656
const logType = row.getValue<ColumnSchema['log_type']>('log_type')
5757
return (
58-
<div className="absolute right-0 top-1/2 -translate-y-1/2 text-right flex items-center gap-1">
58+
<div className="flex items-center justify-end gap-1">
5959
<LogTypeIcon type={logType} size={16} className="text-foreground/70" />
6060
</div>
6161
)
6262
},
6363
enableHiding: false,
6464
filterFn: (row, columnId, filterValue) => true,
65-
size: 48,
66-
minSize: 48,
67-
maxSize: 48,
65+
size: 40,
66+
minSize: 40,
67+
maxSize: 40,
6868
enableResizing: false,
6969
meta: {
7070
headerClassName:
@@ -83,8 +83,8 @@ export const COLUMNS: ColumnDef<ColumnSchema>[] = [
8383
return <span className="text-foreground-lighter">{value}</span>
8484
},
8585
enableResizing: false,
86-
size: 69,
87-
minSize: 69,
86+
size: 70,
87+
minSize: 70,
8888
meta: {
8989
cellClassName:
9090
'font-mono text-muted-foreground w-[--col-method-size] max-w-[--col-method-size] min-w-[--col-method-size]',
@@ -113,8 +113,8 @@ export const COLUMNS: ColumnDef<ColumnSchema>[] = [
113113
},
114114
filterFn: (row, columnId, filterValue) => true,
115115
enableResizing: false,
116-
size: 60,
117-
minSize: 60,
116+
size: 50,
117+
minSize: 50,
118118
meta: {
119119
headerClassName:
120120
'w-[--header-status-size] max-w-[--header-status-size] min-w-[--header-status-size]',
@@ -181,8 +181,8 @@ export const COLUMNS: ColumnDef<ColumnSchema>[] = [
181181
)
182182
},
183183
enableResizing: true,
184-
size: 400,
185-
minSize: 400,
184+
size: 200,
185+
minSize: 200,
186186
meta: {
187187
headerClassName:
188188
'w-[--header-event_message-size] max-w-[--header-event_message-size] min-w-[--header-event_message-size]',

apps/studio/components/interfaces/UnifiedLogs/components/FunctionLogsTab.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ export const FunctionLogsTab = ({ logs = [] }: FunctionLogsTabProps) => {
2727
<div className="text-sm font-mono">
2828
{logs.map((log) => {
2929
const date = new Date(Number(log.timestamp) / 1000)
30-
// Map the log level to our standard levels
3130

3231
return (
3332
<div key={log.id} className="py-1 border-b border-border last:border-0">
@@ -40,7 +39,6 @@ export const FunctionLogsTab = ({ logs = [] }: FunctionLogsTabProps) => {
4039
level={log.level}
4140
className="min-w-20"
4241
/>
43-
{/* <span className="text-foreground">{log.event_type || 'Log'}</span> */}
4442
</div>
4543
<div className="mt-1 whitespace-pre-wrap break-all pl-2 text-[0.8rem]">
4644
{log.event_message}

apps/studio/components/interfaces/UnifiedLogs/components/LogTypeIcon.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ export const LogTypeIcon = ({
4444
<IconComponent />
4545
</TooltipTrigger>
4646
<TooltipContent side="left">
47-
<div className="text-sm">{type}</div>
47+
<div className="text-xs">{type}</div>
4848
</TooltipContent>
4949
</Tooltip>
5050
)

0 commit comments

Comments
 (0)