mirror of
https://github.com/supabase/supabase.git
synced 2026-09-10 20:10:31 +08:00
## Problem The legacy per-service log pages (postgres, auth, api, edge functions, storage, realtime, cron, etc.) and the single-log detail panel query the BigQuery-backed `logs.all` analytics endpoint. We are moving these reads onto the OTEL ClickHouse endpoint (`logs.all.otel`). ## Fix - Add `Logs.utils.otel.ts`: ClickHouse query builders (rows/count/chart/single) + row mappers that target the single `logs` table keyed by `source`, reading fields from the `log_attributes` map and aliasing columns to the leaf names the renderers expect. - Parameterize `buildWhereClauses` / `genWhereStatement` in `Logs.utils.ts` so the OTEL builders reuse the shared nested AND/OR filter grouping. Defaults keep the BigQuery behavior unchanged. - Gate `useLogsPreview` (rows, count, chart) and `useSingleLog` (detail) on the new `otelLegacyLogs` flag. BigQuery stays the default when the flag is off. - Extract the OTEL timestamp parser into `parseOtelTimestamp` (`otel-inspection.utils.ts`) and reuse it in `unified-logs-infinite-query.ts` (replaces an inline copy of the same logic; no behavior change). ## Dependencies None. Standalone, safe to merge on its own. Behind `otelLegacyLogs` (off by default), so no user-facing change. Part of DEBUG-145 (split from #47087). ## How to test - In staging, go to Legacy Logs. - All logs pages should work the same as before. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added OTEL-backed logs support for preview, count, chart, and single-log details when enabled. * **Bug Fixes** * Improved timestamp parsing/normalization for OTEL data to ensure correct display and pagination. * Enhanced filtering behavior, including safer handling of unknown filter keys and invalid values across OTEL queries. * Improved single-log result shaping to preserve expected API/database metadata in OTEL mode. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
116 lines
3.2 KiB
TypeScript
116 lines
3.2 KiB
TypeScript
import { useQuery } from '@tanstack/react-query'
|
|
import { useFlag } from 'common'
|
|
import { useMemo } from 'react'
|
|
|
|
import { LOGS_TABLES } from '@/components/interfaces/Settings/Logs/Logs.constants'
|
|
import type {
|
|
LogData,
|
|
Logs,
|
|
LogsEndpointParams,
|
|
QueryType,
|
|
} from '@/components/interfaces/Settings/Logs/Logs.types'
|
|
import { genSingleLogQuery } from '@/components/interfaces/Settings/Logs/Logs.utils'
|
|
import {
|
|
genSingleLogQueryOtel,
|
|
mapOtelSingleLogToLegacy,
|
|
} from '@/components/interfaces/Settings/Logs/Logs.utils.otel'
|
|
import { executeAnalyticsSql } from '@/data/logs/execute-analytics-sql'
|
|
import { logsAllEndpointUrl } from '@/data/logs/logs-endpoint'
|
|
import { safeSql } from '@/data/logs/safe-analytics-sql'
|
|
import { useIsFeatureEnabled } from '@/hooks/misc/useIsFeatureEnabled'
|
|
|
|
interface SingleLogHook {
|
|
data: LogData | undefined
|
|
error: string | Object | null
|
|
isLoading: boolean
|
|
refresh: () => void
|
|
}
|
|
|
|
type SingleLogParams = {
|
|
id?: string
|
|
projectRef: string
|
|
queryType?: QueryType
|
|
paramsToMerge?: Partial<LogsEndpointParams>
|
|
}
|
|
function useSingleLog({
|
|
projectRef,
|
|
id,
|
|
queryType,
|
|
paramsToMerge,
|
|
}: SingleLogParams): SingleLogHook {
|
|
const table = queryType ? LOGS_TABLES[queryType] : undefined
|
|
|
|
// When on, fetch the log from the OTEL endpoint instead of BigQuery.
|
|
const useOtel = useFlag('otelLegacyLogs')
|
|
const endpoint = logsAllEndpointUrl(useOtel)
|
|
|
|
const sql = useMemo(() => {
|
|
if (!id || !table) return safeSql``
|
|
if (useOtel) {
|
|
try {
|
|
return genSingleLogQueryOtel(id)
|
|
} catch {
|
|
// Malformed (non-uuid) id — emit nothing rather than throwing in render.
|
|
return safeSql``
|
|
}
|
|
}
|
|
return genSingleLogQuery(table, id)
|
|
}, [id, table, useOtel])
|
|
|
|
const enabled = Boolean(id && table)
|
|
|
|
const { logsMetadata } = useIsFeatureEnabled(['logs:metadata'])
|
|
|
|
const {
|
|
data,
|
|
error: rcError,
|
|
isPending,
|
|
isRefetching,
|
|
refetch,
|
|
} = useQuery({
|
|
// id and queryType uniquely identify sql without having to stick the
|
|
// entire sql in the query key.
|
|
// eslint-disable-next-line @tanstack/query/exhaustive-deps
|
|
queryKey: [
|
|
'projects',
|
|
projectRef,
|
|
'single-log',
|
|
id,
|
|
queryType,
|
|
paramsToMerge?.iso_timestamp_start,
|
|
paramsToMerge?.iso_timestamp_end,
|
|
{ otel: useOtel },
|
|
],
|
|
queryFn: async ({ signal }) => {
|
|
const data = await executeAnalyticsSql({
|
|
projectRef,
|
|
endpoint,
|
|
sql,
|
|
iso_timestamp_start: paramsToMerge?.iso_timestamp_start ?? '',
|
|
iso_timestamp_end: paramsToMerge?.iso_timestamp_end ?? '',
|
|
method: 'get',
|
|
signal,
|
|
})
|
|
return data as unknown as Logs
|
|
},
|
|
enabled,
|
|
refetchOnWindowFocus: false,
|
|
refetchOnMount: false,
|
|
refetchOnReconnect: false,
|
|
})
|
|
|
|
let error: null | string | object = rcError ? (rcError as any).message : null
|
|
const rawResult = data?.result ? data.result[0] : undefined
|
|
const result = rawResult && useOtel ? mapOtelSingleLogToLegacy(rawResult, queryType) : rawResult
|
|
|
|
return {
|
|
data: !!result
|
|
? { ...result, metadata: logsMetadata ? result?.metadata : undefined }
|
|
: undefined,
|
|
isLoading: (enabled && isPending) || isRefetching,
|
|
error,
|
|
refresh: () => refetch(),
|
|
}
|
|
}
|
|
export default useSingleLog
|