diff --git a/apps/studio/components/interfaces/UnifiedLogs/UnifiedLogs.fields.tsx b/apps/studio/components/interfaces/UnifiedLogs/UnifiedLogs.fields.tsx
index 71c79e47759..48cea7695ce 100644
--- a/apps/studio/components/interfaces/UnifiedLogs/UnifiedLogs.fields.tsx
+++ b/apps/studio/components/interfaces/UnifiedLogs/UnifiedLogs.fields.tsx
@@ -52,9 +52,10 @@ export const filterFields = [
return (
- {props.label}
+ {props.label.replace('_', ' ')}
- {props.value}
+ {/* [Joshen] Temporarily hiding, this feels excessive */}
+ {/* {props.value} */}
)
},
diff --git a/apps/studio/components/interfaces/UnifiedLogs/UnifiedLogs.queries.ts b/apps/studio/components/interfaces/UnifiedLogs/UnifiedLogs.queries.ts
index 8156978dfd5..45decf308f8 100644
--- a/apps/studio/components/interfaces/UnifiedLogs/UnifiedLogs.queries.ts
+++ b/apps/studio/components/interfaces/UnifiedLogs/UnifiedLogs.queries.ts
@@ -35,7 +35,11 @@ const buildQueryConditions = (search: QuerySearchParamsType) => {
// Handle scalar values
if (value !== null && value !== undefined) {
- whereConditions.push(`${key} = '${value}'`)
+ if (['host', 'pathname'].includes(key)) {
+ whereConditions.push(`${key} LIKE '%${value}%'`)
+ } else {
+ whereConditions.push(`${key} = '${value}'`)
+ }
}
})
@@ -234,7 +238,7 @@ const getEdgeLogsQuery = () => {
WHEN edge_logs_response.status_code >= 500 THEN 'error'
ELSE 'success'
END as level,
- edge_logs_request.path as path,
+ edge_logs_request.path as pathname,
edge_logs_request.host as host,
null as event_message,
edge_logs_request.method as method,
@@ -273,7 +277,7 @@ const getPostgrestLogsQuery = () => {
WHEN edge_logs_response.status_code >= 500 THEN 'error'
ELSE 'success'
END as level,
- edge_logs_request.path as path,
+ edge_logs_request.path as pathname,
edge_logs_request.host as host,
null as event_message,
edge_logs_request.method as method,
@@ -311,7 +315,7 @@ const getPostgresLogsQuery = () => {
WHEN pgl_parsed.error_severity = 'ERROR' THEN 'error'
ELSE null
END as level,
- null as path,
+ null as pathname,
null as host,
event_message as event_message,
null as method,
@@ -341,7 +345,7 @@ const getEdgeFunctionLogsQuery = () => {
WHEN fel_response.status_code >= 500 THEN 'error'
ELSE 'success'
END as level,
- fel_request.url as path,
+ fel_request.url as pathname,
fel_request.host as host,
COALESCE(function_logs_agg.last_event_message, '') as event_message,
fel_request.method as method,
@@ -387,7 +391,7 @@ const getAuthLogsQuery = () => {
WHEN el_in_al_response.status_code >= 500 THEN 'error'
ELSE 'success'
END as level,
- el_in_al_request.path as path,
+ el_in_al_request.path as pathname,
el_in_al_request.host as host,
null as event_message,
el_in_al_request.method as method,
@@ -428,7 +432,7 @@ const getSupavisorLogsQuery = () => {
WHEN LOWER(svl_metadata.level) = 'warn' OR LOWER(svl_metadata.level) = 'warning' THEN 'warning'
ELSE 'success'
END as level,
- null as path,
+ null as pathname,
null as host,
null as event_message,
null as method,
@@ -478,7 +482,7 @@ SELECT
log_type,
status,
level,
- path,
+ pathname,
host,
event_message,
method,
@@ -498,8 +502,9 @@ ${finalWhere}
* Also returns facets for all filter dimensions
*/
export const getLogsCountQuery = (search: QuerySearchParamsType): string => {
- // Use the buildQueryConditions helper
const { finalWhere } = buildQueryConditions(search)
+ const methodWhere =
+ finalWhere.length > 0 ? `${finalWhere} AND method is NOT NULL` : 'WHERE method IS NOT NULL'
// Create a count query using the same unified logs CTE
const sql = `
@@ -530,8 +535,7 @@ UNION ALL
-- Get counts by method
SELECT 'method' as dimension, method as value, COUNT(*) as count
FROM unified_logs
-${finalWhere}
-WHERE method IS NOT NULL
+${methodWhere}
GROUP BY method
`
diff --git a/apps/studio/components/interfaces/UnifiedLogs/UnifiedLogs.tsx b/apps/studio/components/interfaces/UnifiedLogs/UnifiedLogs.tsx
index 911f62f9cf8..f4bd6be13d1 100644
--- a/apps/studio/components/interfaces/UnifiedLogs/UnifiedLogs.tsx
+++ b/apps/studio/components/interfaces/UnifiedLogs/UnifiedLogs.tsx
@@ -27,7 +27,6 @@ import { FilterSideBar } from 'components/ui/DataTable/FilterSideBar'
import { LiveButton } from 'components/ui/DataTable/LiveButton'
import { LiveRow } from 'components/ui/DataTable/LiveRow'
import { DataTableProvider } from 'components/ui/DataTable/providers/DataTableProvider'
-import { RefreshButton } from 'components/ui/DataTable/RefreshButton'
import { TimelineChart } from 'components/ui/DataTable/TimelineChart'
import { useUnifiedLogsChartQuery } from 'data/logs/unified-logs-chart-query'
import { useUnifiedLogsCountQuery } from 'data/logs/unified-logs-count-query'
@@ -45,6 +44,7 @@ import {
TabsList_Shadcn_ as TabsList,
TabsTrigger_Shadcn_ as TabsTrigger,
} from 'ui'
+import { RefreshButton } from '../../ui/DataTable/RefreshButton'
import { UNIFIED_LOGS_COLUMNS } from './components/Columns'
import { MemoizedDataTableSheetContent } from './components/DataTableSheetContent'
import { FunctionLogsTab } from './components/FunctionLogsTab'
@@ -100,19 +100,36 @@ export const UnifiedLogs = () => {
isLoading,
isFetching,
hasNextPage,
- refetch,
+ refetch: refetchLogs,
fetchNextPage,
fetchPreviousPage,
} = useUnifiedLogsInfiniteQuery({ projectRef, search: searchParameters })
- const { data: counts, isLoading: isLoadingCounts } = useUnifiedLogsCountQuery({
+ const {
+ data: counts,
+ isLoading: isLoadingCounts,
+ isFetching: isFetchingCounts,
+ refetch: refetchCounts,
+ } = useUnifiedLogsCountQuery({
projectRef,
search: searchParameters,
})
- const { data: unifiedLogsChart = [] } = useUnifiedLogsChartQuery({
+ const {
+ data: unifiedLogsChart = [],
+ isFetching: isFetchingCharts,
+ refetch: refetchCharts,
+ } = useUnifiedLogsChartQuery({
projectRef,
search: searchParameters,
})
+ const refetchAllData = () => {
+ refetchLogs()
+ refetchCounts()
+ refetchCharts()
+ }
+
+ const isRefetchingData = isFetching || isFetchingCounts || isFetchingCharts
+
const rawFlatData = useMemo(() => {
return unifiedLogsData?.pages?.flatMap((page) => page.data ?? []) ?? []
}, [unifiedLogsData?.pages])
@@ -275,10 +292,13 @@ export const UnifiedLogs = () => {
-
+
[
- ,
+ ,
fetchPreviousPage ? (
>
+ placeholder?: string
}
-export function DataTableFilterCommand({ searchParamsParser }: DataTableFilterCommandProps) {
+export function DataTableFilterCommand({
+ searchParamsParser,
+ placeholder = 'Search data table...',
+}: DataTableFilterCommandProps) {
const { table, isLoading, filterFields: _filterFields, getFacetedUniqueValues } = useDataTable()
const columnFilters = table.getState().columnFilters
const inputRef = useRef(null)
@@ -59,7 +62,8 @@ export function DataTableFilterCommand({ searchParamsParser }: DataTableFilterCo
const trimmedInputValue = inputValue.trim()
- useHotKey(() => setOpen((open) => !open), 'k')
+ // [Joshen] Temporarily disabling as this conflicts with our current CMD K behaviour
+ // useHotKey(() => setOpen((open) => !open), 'k')
useEffect(() => {
// TODO: we could check for ARRAY_DELIMITER or SLIDER_DELIMITER to auto-set filter when typing
@@ -139,13 +143,15 @@ export function DataTableFilterCommand({ searchParamsParser }: DataTableFilterCo
trimmedInputValue ? 'text-foreground' : 'text-foreground-light'
)}
>
- {trimmedInputValue ? inputValue : 'Search data table...'}
+ {trimmedInputValue ? inputValue : placeholder}
-
+ {/* [Joshen] Temporarily disabling as this conflicts with existing CMD K shortcut */}
+ {/*
⌘
K
-
+ */}
+
div]:border-none bg',
@@ -185,7 +191,7 @@ export function DataTableFilterCommand({ searchParamsParser }: DataTableFilterCo
const word = getWordByCaretPosition({ value, caretPosition })
setCurrentWord(word)
}}
- placeholder="Search data table..."
+ placeholder={placeholder}
className="text-foreground"
/>
diff --git a/apps/studio/components/ui/DataTable/DataTableFilters/DataTableFilterResetButton.tsx b/apps/studio/components/ui/DataTable/DataTableFilters/DataTableFilterResetButton.tsx
index d0665532653..2279eb48b17 100644
--- a/apps/studio/components/ui/DataTable/DataTableFilters/DataTableFilterResetButton.tsx
+++ b/apps/studio/components/ui/DataTable/DataTableFilters/DataTableFilterResetButton.tsx
@@ -9,8 +9,6 @@ export function DataTableFilterResetButton
({ value: _value }: DataTableFi
const value = _value as string
const column = table.getColumn(value)
const filterValue = columnFilters.find((f) => f.id === value)?.value
-
- // TODO: check if we could useMemo
const filters = filterValue ? (Array.isArray(filterValue) ? filterValue : [filterValue]) : []
if (filters.length === 0) return null
@@ -18,7 +16,8 @@ export function DataTableFilterResetButton({ value: _value }: DataTableFi
return (
}
+ className="h-5 rounded-full px-1.5 py-1 font-mono text-[10px] [&>span]:-translate-y-[0.6px] space-x-1"
onClick={(e) => {
e.stopPropagation()
column?.setFilterValue(undefined)
@@ -29,13 +28,8 @@ export function DataTableFilterResetButton({ value: _value }: DataTableFi
column?.setFilterValue(undefined)
}
}}
- icon={}
- asChild
>
- {/* REMINDER: `AccordionTrigger` is also a button(!) and we get Hydration error when rendering button within button */}
-
- {filters.length}
-
+ {filters.length}
)
}
diff --git a/apps/studio/components/ui/DataTable/RefreshButton.tsx b/apps/studio/components/ui/DataTable/RefreshButton.tsx
index 6aaf245d3aa..2adee376572 100644
--- a/apps/studio/components/ui/DataTable/RefreshButton.tsx
+++ b/apps/studio/components/ui/DataTable/RefreshButton.tsx
@@ -1,21 +1,18 @@
import { LoaderCircle, RefreshCcw } from 'lucide-react'
import { Button } from 'ui'
-import { useDataTable } from './providers/DataTableProvider'
-
interface RefreshButtonProps {
- onClick: () => void
+ isLoading: boolean
+ onRefresh: () => void
}
-export function RefreshButton({ onClick }: RefreshButtonProps) {
- const { isLoading } = useDataTable()
-
+export const RefreshButton = ({ isLoading, onRefresh }: RefreshButtonProps) => {
return (