level param now being removed correctly.

This commit is contained in:
Jonathan Summers-Muir committed 2025-05-12 17:01:24 +08:00
1 parent 734bc076da
commit 5e38366b37
5 files changed
+136 -57

No files matched your search

@@ -1,5 +1,5 @@
export const ARRAY_DELIMITER = ",";
export const SLIDER_DELIMITER = "-";
export const SPACE_DELIMITER = "_";
export const RANGE_DELIMITER = "-";
export const SORT_DELIMITER = ".";
export const ARRAY_DELIMITER = ','
export const SLIDER_DELIMITER = '-'
export const SPACE_DELIMITER = '_'
export const RANGE_DELIMITER = '-'
export const SORT_DELIMITER = '.'
@@ -53,6 +53,9 @@ import { dataOptions, useChartData } from './query-options-new'
import { filterFields as defaultFilterFields, sheetFields } from './constants'
import { useParams } from 'common'
// Debug mode flag - set to true to enable detailed logs
const DEBUG_FILTER_PROCESSING = false
function TooltipLabel({ level }: { level: keyof Omit<TimelineChartSchema, 'timestamp'> }) {
return (
<div className="mr-2 flex w-20 items-center justify-between gap-2 font-mono">
@@ -226,26 +229,53 @@ export function Client() {
}, [rowSelection, table, isLoading, isFetching, flatData])
React.useEffect(() => {
if (DEBUG_FILTER_PROCESSING) console.log('========== FILTER CHANGE DETECTED ==========')
if (DEBUG_FILTER_PROCESSING) console.log('Raw columnFilters:', JSON.stringify(columnFilters))
// Check for level filters specifically
const levelColumnFilter = columnFilters.find((filter) => filter.id === 'level')
if (DEBUG_FILTER_PROCESSING) console.log('Level column filter:', levelColumnFilter)
const columnFiltersWithNullable = filterFields.map((field) => {
const filterValue = columnFilters.find((filter) => filter.id === field.value)
if (DEBUG_FILTER_PROCESSING) console.log(`Processing field ${field.value}:`, filterValue)
if (!filterValue) return { id: field.value, value: null }
return { id: field.value, value: filterValue.value }
})
// Debug level filter specifically
const levelFilter = columnFiltersWithNullable.find((f) => f.id === 'level')
if (DEBUG_FILTER_PROCESSING) console.log('Level filter after mapping:', levelFilter)
if (DEBUG_FILTER_PROCESSING)
console.log('All column filters after mapping:', columnFiltersWithNullable)
const search = columnFiltersWithNullable.reduce(
(prev, curr) => {
if (DEBUG_FILTER_PROCESSING)
console.log(`Processing filter for URL: ${curr.id}`, {
value: curr.value,
type: Array.isArray(curr.value) ? 'array' : typeof curr.value,
isEmpty: Array.isArray(curr.value) && curr.value.length === 0,
isNull: curr.value === null,
})
// Add to search parameters
prev[curr.id as string] = curr.value
return prev
},
{} as Record<string, unknown>
)
// Debug column filters
if (columnFilters.length > 0) {
console.log('Setting search from column filters:', columnFilters, '->', search)
}
if (DEBUG_FILTER_PROCESSING) console.log('Final search object to be set in URL:', search)
if (DEBUG_FILTER_PROCESSING) console.log('Level value in final search:', search.level)
if (DEBUG_FILTER_PROCESSING) console.log('Is level in search object:', 'level' in search)
// Set the search state without any console logs
if (DEBUG_FILTER_PROCESSING) console.log('CALLING setSearch with:', JSON.stringify(search))
setSearch(search)
if (DEBUG_FILTER_PROCESSING) console.log('========== END FILTER PROCESSING ==========')
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [columnFilters])
@@ -0,0 +1,35 @@
/**
* Parameters that should be excluded from standard SQL query condition building
*/
// Pagination and control parameters
export const PAGINATION_PARAMS = [
'sort',
'start',
'size',
'uuid',
'cursor',
'direction',
'live',
] as const
export type PaginationParamType = (typeof PAGINATION_PARAMS)[number]
// Special filter parameters that need custom handling
export const SPECIAL_FILTER_PARAMS = ['date'] as const
export type SpecialFilterParamType = (typeof SPECIAL_FILTER_PARAMS)[number]
// Combined list of all parameters to exclude from standard filtering
export const EXCLUDED_QUERY_PARAMS = [...PAGINATION_PARAMS, ...SPECIAL_FILTER_PARAMS] as const
export type ExcludedQueryParamType = (typeof EXCLUDED_QUERY_PARAMS)[number]
/**
* Parameters that need special handling in buildBaseConditions and raw SQL queries
* This is different from the EXCLUDED_QUERY_PARAMS used in getUnifiedLogsQuery.
*
* In raw SQL queries against log type tables, 'level' needs to be processed differently
* because it maps to different columns/conditions depending on the log type.
*/
export const BASE_CONDITIONS_EXCLUDED_PARAMS = [...PAGINATION_PARAMS, 'date', 'level'] as const
@@ -3,6 +3,40 @@
*/
import { SearchParamsType } from './search-params'
import { BASE_CONDITIONS_EXCLUDED_PARAMS, EXCLUDED_QUERY_PARAMS } from './constants/query-params'
/**
* Builds query conditions from search parameters and returns WHERE clause
* @param search SearchParamsType object containing query parameters
* @returns Object with whereConditions array and formatted WHERE clause
*/
export const buildQueryConditions = (search: SearchParamsType) => {
const whereConditions: string[] = []
// Process all search parameters for filtering
Object.entries(search).forEach(([key, value]) => {
// Skip pagination/control parameters
if (EXCLUDED_QUERY_PARAMS.includes(key as any)) {
return
}
// Handle array filters (IN clause)
if (Array.isArray(value) && value.length > 0) {
whereConditions.push(`${key} IN (${value.map((v) => `'${v}'`).join(',')})`)
return
}
// Handle scalar values
if (value !== null && value !== undefined) {
whereConditions.push(`${key} = '${value}'`)
}
})
// Create final WHERE clause
const finalWhere = whereConditions.length > 0 ? `WHERE ${whereConditions.join(' AND ')}` : ''
return { whereConditions, finalWhere }
}
/**
* Builds level-specific condition for different log types
@@ -99,22 +133,16 @@ export const buildBaseConditions = (search: SearchParamsType): string[] => {
Object.entries(search).forEach(([key, value]) => {
// Skip pagination/control parameters, date and level (handled separately)
if (
['sort', 'start', 'size', 'uuid', 'cursor', 'direction', 'live', 'date', 'level'].includes(
key
)
) {
if (BASE_CONDITIONS_EXCLUDED_PARAMS.includes(key as any)) {
return
}
// Handle array filters (IN clause)
if (Array.isArray(value) && value.length > 0) {
baseConditions.push(`${key} IN (${value.map((v) => `'${v}'`).join(',')})`)
return
baseConditions.push(`${key} IN (${value.map((v) => `'${v}'`).join(', ')})`)
}
// Handle scalar values
if (value !== null && value !== undefined) {
else if (value !== null && value !== undefined) {
baseConditions.push(`${key} = '${value}'`)
}
})
@@ -126,15 +154,8 @@ export const buildBaseConditions = (search: SearchParamsType): string[] => {
* Unified logs SQL query
*/
export const getUnifiedLogsQuery = (search: SearchParamsType): string => {
const baseConditions = buildBaseConditions(search)
const levelFilter = search.level || []
// Create source-specific where clauses with level filters
const edgeWhere = createFilterWhereClause('edge', levelFilter, baseConditions)
const postgresWhere = createFilterWhereClause('postgres', levelFilter, baseConditions)
const functionWhere = createFilterWhereClause('edge function', levelFilter, baseConditions)
const authWhere = createFilterWhereClause('auth', levelFilter, baseConditions)
const supavisorWhere = createFilterWhereClause('supavisor', levelFilter, baseConditions)
// Use the buildQueryConditions helper
const { finalWhere } = buildQueryConditions(search)
// The unified SQL query with UNION ALL statements
const sql = `
@@ -166,7 +187,6 @@ WITH unified_logs AS (
left join unnest(sb.jwt) as jwt
left join unnest(jwt.authorization) as auth
left join unnest(auth.payload) as authorization_payload
${edgeWhere}
union all
@@ -191,7 +211,6 @@ WITH unified_logs AS (
from postgres_logs as pgl
cross join unnest(pgl.metadata) as pgl_metadata
cross join unnest(pgl_metadata.parsed) as pgl_parsed
${postgresWhere}
union all
@@ -231,7 +250,6 @@ WITH unified_logs AS (
WHERE fl_metadata.execution_id IS NOT NULL
GROUP BY fl_metadata.execution_id
) as function_logs_agg on fel_metadata.execution_id = function_logs_agg.execution_id
${functionWhere}
union all
@@ -267,7 +285,7 @@ WITH unified_logs AS (
left join unnest(auth.payload) as authorization_payload
)
on al_metadata.request_id = el_in_al_response_headers.cf_ray
${authWhere}
WHERE al_metadata.request_id is not null
union all
@@ -290,7 +308,6 @@ WITH unified_logs AS (
null as log_count
from supavisor_logs as svl
cross join unnest(metadata) as svl_metadata
${supavisorWhere}
)
SELECT
@@ -307,24 +324,15 @@ SELECT
auth_user,
log_count
FROM unified_logs
WHERE
level = "error"
${finalWhere}
`
return sql
}
export const getLogsChartQuery2 = (search: SearchParamsType): string => {
const baseConditions = buildBaseConditions(search)
const levelFilter = search.level || []
// Create source-specific where clauses with level filters
const edgeWhere = createFilterWhereClause('edge', levelFilter, baseConditions)
const postgresWhere = createFilterWhereClause('postgres', levelFilter, baseConditions)
const functionWhere = createFilterWhereClause('edge function', levelFilter, baseConditions)
const authWhere = createFilterWhereClause('auth', levelFilter, baseConditions)
const supavisorWhere = createFilterWhereClause('supavisor', levelFilter, baseConditions)
// Use the buildQueryConditions helper
const { finalWhere } = buildQueryConditions(search)
return `
WITH unified_logs AS (
@@ -346,7 +354,7 @@ WITH unified_logs AS (
edge_logs_request.method as method,
authorization_payload.role as api_role,
COALESCE(sb.auth_user, null) as auth_user,
null as log_count,
null as log_count
from edge_logs as el
cross join unnest(metadata) as edge_logs_metadata
cross join unnest(edge_logs_metadata.request) as edge_logs_request
@@ -355,7 +363,6 @@ WITH unified_logs AS (
left join unnest(sb.jwt) as jwt
left join unnest(jwt.authorization) as auth
left join unnest(auth.payload) as authorization_payload
${edgeWhere}
union all
@@ -376,11 +383,10 @@ WITH unified_logs AS (
null as method,
'api_role' as api_role,
null as auth_user,
null as log_count,
null as log_count
from postgres_logs as pgl
cross join unnest(pgl.metadata) as pgl_metadata
cross join unnest(pgl_metadata.parsed) as pgl_parsed
${postgresWhere}
union all
@@ -420,7 +426,6 @@ WITH unified_logs AS (
WHERE fl_metadata.execution_id IS NOT NULL
GROUP BY fl_metadata.execution_id
) as function_logs_agg on fel_metadata.execution_id = function_logs_agg.execution_id
${functionWhere}
union all
@@ -441,7 +446,7 @@ WITH unified_logs AS (
el_in_al_request.method as method,
authorization_payload.role as api_role,
COALESCE(sb.auth_user, null) as auth_user,
null as log_count,
null as log_count
from auth_logs as al
cross join unnest(metadata) as al_metadata
left join (
@@ -456,7 +461,7 @@ WITH unified_logs AS (
left join unnest(auth.payload) as authorization_payload
)
on al_metadata.request_id = el_in_al_response_headers.cf_ray
${authWhere}
WHERE al_metadata.request_id is not null
union all
@@ -476,10 +481,9 @@ WITH unified_logs AS (
null as method,
'api_role' as api_role,
null as auth_user,
null as log_count,
null as log_count
from supavisor_logs as svl
cross join unnest(metadata) as svl_metadata
${supavisorWhere}
)
SELECT
@@ -488,10 +492,12 @@ SELECT
COUNTIF(level = 'warning') as warning,
COUNTIF(level = 'error') as error
FROM unified_logs
${finalWhere}
GROUP BY minute
ORDER BY minute ASC
`
}
/**
* Chart data query for logs time series
*/
@@ -235,10 +235,18 @@ export const useChartData = (search: SearchParamsType, projectRef: string) => {
}
// Only include the levels that were requested in the filter (or all if no filter)
const levelFilter = search.level || ['success', 'warning', 'error']
if (levelFilter.includes('success')) dataPoint.success = minuteData.success
if (levelFilter.includes('warning')) dataPoint.warning = minuteData.warning
if (levelFilter.includes('error')) dataPoint.error = minuteData.error
const levelFilter = search.level
// If no level filter is specified or it's empty, include all levels
if (!levelFilter || levelFilter.length === 0) {
dataPoint.success = minuteData.success
dataPoint.warning = minuteData.warning
dataPoint.error = minuteData.error
} else {
// Otherwise only include the selected levels
if (levelFilter.includes('success')) dataPoint.success = minuteData.success
if (levelFilter.includes('warning')) dataPoint.warning = minuteData.warning
if (levelFilter.includes('error')) dataPoint.error = minuteData.error
}
// Always push a data point for this minute
chartData.push(dataPoint)