mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 17:35:10 +03:00
fix(logs): default unified logs to otel
This commit is contained in:
1 parent
3fec6bcc16
commit
8d59cf68ab
8 files changed
+68
-127
No files matched your search
@@ -67,10 +67,7 @@ import { useTableRowSelection } from '@/components/ui/DataTable/useTableRowSelec
|
||||
import { ShortcutTooltip } from '@/components/ui/ShortcutTooltip'
|
||||
import { useUnifiedLogsChartQuery } from '@/data/logs/unified-logs-chart-query'
|
||||
import { useUnifiedLogsCountQuery } from '@/data/logs/unified-logs-count-query'
|
||||
import {
|
||||
useUnifiedLogsBackend,
|
||||
useUnifiedLogsInfiniteQuery,
|
||||
} from '@/data/logs/unified-logs-infinite-query'
|
||||
import { useUnifiedLogsInfiniteQuery } from '@/data/logs/unified-logs-infinite-query'
|
||||
import { useLocalStorageQuery } from '@/hooks/misc/useLocalStorage'
|
||||
import { useShowMultigresLogs } from '@/hooks/misc/useShowMultigresLogs'
|
||||
import { useTrack } from '@/lib/telemetry/track'
|
||||
@@ -96,7 +93,6 @@ export const UnifiedLogs = () => {
|
||||
useResetFocus()
|
||||
|
||||
const { ref: projectRef } = useParams()
|
||||
const { isReady: isLogsBackendReady } = useUnifiedLogsBackend()
|
||||
const track = useTrack()
|
||||
const [search, setSearch] = useQueryStates(SEARCH_PARAMS_PARSER)
|
||||
const showMultigresLogs = useShowMultigresLogs()
|
||||
@@ -213,7 +209,6 @@ export const UnifiedLogs = () => {
|
||||
})
|
||||
|
||||
const refetchAllData = () => {
|
||||
if (!isLogsBackendReady) return
|
||||
refetchLogs()
|
||||
refetchCounts()
|
||||
refetchCharts()
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { useMutation } from '@tanstack/react-query'
|
||||
import { useFlag } from 'common'
|
||||
import { toast } from 'sonner'
|
||||
|
||||
import { logsAllEndpointUrl, pickLogsQueryBuilder } from './logs-endpoint'
|
||||
import { getUnifiedLogsISOStartEnd } from './unified-logs-infinite-query'
|
||||
import { getUnifiedLogsISOStartEnd, useUnifiedLogsBackend } from './unified-logs-infinite-query'
|
||||
import { mapUnifiedLogRow, parseUnifiedLogsQueryRows } from './unified-logs.utils'
|
||||
import { getUnifiedLogsQuery } from '@/components/interfaces/UnifiedLogs/UnifiedLogs.queries'
|
||||
import { getUnifiedLogsQuery as getUnifiedLogsQueryBq } from '@/components/interfaces/UnifiedLogs/UnifiedLogs.queries.bq'
|
||||
@@ -58,7 +57,7 @@ export const useGetUnifiedLogsMutation = ({
|
||||
UseCustomMutationOptions<LogDrainCreateData, ResponseError, getUnifiedLogsVariables>,
|
||||
'mutationFn'
|
||||
> = {}) => {
|
||||
const useOtel = useFlag('otelUnifiedLogs')
|
||||
const useOtel = useUnifiedLogsBackend()
|
||||
return useMutation<LogDrainCreateData, ResponseError, getUnifiedLogsVariables>({
|
||||
mutationFn: (vars) => retrieveUnifiedLogs({ ...vars, useOtel: vars.useOtel ?? useOtel }),
|
||||
async onSuccess(data, variables, context) {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { useFlag } from 'common'
|
||||
|
||||
import { executeAnalyticsSql } from './execute-analytics-sql'
|
||||
import { logsKeys } from './keys'
|
||||
@@ -12,6 +11,7 @@ import { analyticsLiteral as lit, safeSql, type SafeLogSqlFragment } from './saf
|
||||
import {
|
||||
getUnifiedLogsISOStartEnd,
|
||||
UNIFIED_LOGS_QUERY_OPTIONS,
|
||||
useUnifiedLogsBackend,
|
||||
} from './unified-logs-infinite-query'
|
||||
import {
|
||||
getAuthServiceFlowQuery,
|
||||
@@ -301,7 +301,7 @@ export const useUnifiedLogInspectionQuery = <TData = UnifiedLogInspectionData>(
|
||||
...options
|
||||
}: UseCustomQueryOptions<UnifiedLogInspectionData, UnifiedLogInspectionError, TData> = {}
|
||||
) => {
|
||||
const useOtel = !!useFlag('otelUnifiedLogs')
|
||||
const useOtel = useUnifiedLogsBackend()
|
||||
return useQuery<UnifiedLogInspectionData, UnifiedLogInspectionError, TData>({
|
||||
queryKey: [...logsKeys.serviceFlow(projectRef, search, logId), { otel: useOtel }],
|
||||
queryFn: ({ signal }) =>
|
||||
|
||||
@@ -166,14 +166,11 @@ export const useUnifiedLogsChartQuery = <TData = UnifiedLogsChartData>(
|
||||
...options
|
||||
}: UseCustomQueryOptions<UnifiedLogsChartData, UnifiedLogsChartError, TData> = {}
|
||||
) => {
|
||||
const { isReady, useOtel } = useUnifiedLogsBackend()
|
||||
const useOtel = useUnifiedLogsBackend()
|
||||
return useQuery<UnifiedLogsChartData, UnifiedLogsChartError, TData>({
|
||||
queryKey: [...logsKeys.unifiedLogsChart(projectRef, search), { otel: useOtel, ready: isReady }],
|
||||
queryFn: ({ signal }) => {
|
||||
if (!isReady) throw new Error('Unified Logs backend is not ready')
|
||||
return getUnifiedLogsChart({ projectRef, search, useOtel }, signal)
|
||||
},
|
||||
enabled: enabled && isReady && typeof projectRef !== 'undefined',
|
||||
queryKey: [...logsKeys.unifiedLogsChart(projectRef, search), { otel: useOtel }],
|
||||
queryFn: ({ signal }) => getUnifiedLogsChart({ projectRef, search, useOtel }, signal),
|
||||
enabled: enabled && typeof projectRef !== 'undefined',
|
||||
placeholderData: keepPreviousData,
|
||||
...UNIFIED_LOGS_QUERY_OPTIONS,
|
||||
...options,
|
||||
|
||||
@@ -90,14 +90,11 @@ export const useUnifiedLogsCountQuery = <TData = UnifiedLogsCountData>(
|
||||
...options
|
||||
}: UseCustomQueryOptions<UnifiedLogsCountData, UnifiedLogsCountError, TData> = {}
|
||||
) => {
|
||||
const { isReady, useOtel } = useUnifiedLogsBackend()
|
||||
const useOtel = useUnifiedLogsBackend()
|
||||
return useQuery<UnifiedLogsCountData, UnifiedLogsCountError, TData>({
|
||||
queryKey: [...logsKeys.unifiedLogsCount(projectRef, search), { otel: useOtel, ready: isReady }],
|
||||
queryFn: ({ signal }) => {
|
||||
if (!isReady) throw new Error('Unified Logs backend is not ready')
|
||||
return getUnifiedLogsCount({ projectRef, search, useOtel }, signal)
|
||||
},
|
||||
enabled: enabled && isReady && typeof projectRef !== 'undefined',
|
||||
queryKey: [...logsKeys.unifiedLogsCount(projectRef, search), { otel: useOtel }],
|
||||
queryFn: ({ signal }) => getUnifiedLogsCount({ projectRef, search, useOtel }, signal),
|
||||
enabled: enabled && typeof projectRef !== 'undefined',
|
||||
...UNIFIED_LOGS_QUERY_OPTIONS,
|
||||
...options,
|
||||
})
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { useFlag } from 'common'
|
||||
|
||||
import { executeAnalyticsSql } from './execute-analytics-sql'
|
||||
import { logsKeys } from './keys'
|
||||
@@ -9,6 +8,7 @@ import {
|
||||
getUnifiedLogsISOStartEnd,
|
||||
UNIFIED_LOGS_QUERY_OPTIONS,
|
||||
UnifiedLogsVariables,
|
||||
useUnifiedLogsBackend,
|
||||
} from './unified-logs-infinite-query'
|
||||
import { getFacetCountQuery } from '@/components/interfaces/UnifiedLogs/UnifiedLogs.queries'
|
||||
import {
|
||||
@@ -65,7 +65,7 @@ export const useUnifiedLogsFacetCountQuery = <TData = UnifiedLogsFacetCountData>
|
||||
...options
|
||||
}: UseCustomQueryOptions<UnifiedLogsFacetCountData, UnifiedLogsFacetCountError, TData> = {}
|
||||
) => {
|
||||
const useOtel = !!useFlag('otelUnifiedLogs')
|
||||
const useOtel = useUnifiedLogsBackend()
|
||||
return useQuery<UnifiedLogsFacetCountData, UnifiedLogsFacetCountError, TData>({
|
||||
queryKey: [
|
||||
...logsKeys.unifiedLogsFacetCount(projectRef, facet, facetSearch, search),
|
||||
|
||||
@@ -13,7 +13,6 @@ import {
|
||||
QuerySearchParamsType,
|
||||
} from '@/components/interfaces/UnifiedLogs/UnifiedLogs.types'
|
||||
import { handleError } from '@/data/fetchers'
|
||||
import { IS_PLATFORM } from '@/lib/constants'
|
||||
import type { ResponseError, UseCustomInfiniteQueryOptions } from '@/types'
|
||||
|
||||
const LOGS_PAGE_LIMIT = 50
|
||||
@@ -30,14 +29,7 @@ export type UnifiedLogsData = any
|
||||
export type UnifiedLogsError = ResponseError
|
||||
export type UnifiedLogsVariables = { projectRef?: string; search: QuerySearchParamsType }
|
||||
|
||||
export const useUnifiedLogsBackend = () => {
|
||||
const { hasLoaded, configcat } = useFeatureFlags()
|
||||
const useOtel = IS_PLATFORM && configcat.otelUnifiedLogs === true
|
||||
const isReady =
|
||||
!IS_PLATFORM || (hasLoaded === true && typeof configcat.otelUnifiedLogs === 'boolean')
|
||||
|
||||
return { isReady, useOtel }
|
||||
}
|
||||
export const useUnifiedLogsBackend = () => useFeatureFlags().configcat.otelUnifiedLogs !== false
|
||||
|
||||
export const getUnifiedLogsISOStartEnd = (
|
||||
search: QuerySearchParamsType,
|
||||
@@ -162,17 +154,12 @@ export const useUnifiedLogsInfiniteQuery = <TData = UnifiedLogsData>(
|
||||
PageParam | null
|
||||
> = {}
|
||||
) => {
|
||||
const { isReady, useOtel } = useUnifiedLogsBackend()
|
||||
const useOtel = useUnifiedLogsBackend()
|
||||
return useInfiniteQuery({
|
||||
queryKey: [
|
||||
...logsKeys.unifiedLogsInfinite(projectRef, search),
|
||||
{ otel: useOtel, ready: isReady },
|
||||
],
|
||||
queryFn: ({ signal, pageParam }) => {
|
||||
if (!isReady) throw new Error('Unified Logs backend is not ready')
|
||||
return getUnifiedLogs({ projectRef, search, pageParam, useOtel }, signal)
|
||||
},
|
||||
enabled: enabled && isReady && typeof projectRef !== 'undefined',
|
||||
queryKey: [...logsKeys.unifiedLogsInfinite(projectRef, search), { otel: useOtel }],
|
||||
queryFn: ({ signal, pageParam }) =>
|
||||
getUnifiedLogs({ projectRef, search, pageParam, useOtel }, signal),
|
||||
enabled: enabled && typeof projectRef !== 'undefined',
|
||||
placeholderData: keepPreviousData,
|
||||
getPreviousPageParam: (firstPage) => {
|
||||
if (!firstPage.prevCursor) return null
|
||||
|
||||
@@ -9,25 +9,14 @@ import { useUnifiedLogsCountQuery } from './unified-logs-count-query'
|
||||
import { useUnifiedLogsInfiniteQuery } from './unified-logs-infinite-query'
|
||||
import type { QuerySearchParamsType } from '@/components/interfaces/UnifiedLogs/UnifiedLogs.types'
|
||||
|
||||
const { mockExecuteAnalyticsSql, mockIsPlatform } = vi.hoisted(() => ({
|
||||
const { mockExecuteAnalyticsSql } = vi.hoisted(() => ({
|
||||
mockExecuteAnalyticsSql: vi.fn(),
|
||||
mockIsPlatform: { value: true },
|
||||
}))
|
||||
|
||||
vi.mock('./execute-analytics-sql', () => ({
|
||||
executeAnalyticsSql: mockExecuteAnalyticsSql,
|
||||
}))
|
||||
|
||||
vi.mock('@/lib/constants', async () => {
|
||||
const actual = await vi.importActual<Record<string, unknown>>('@/lib/constants')
|
||||
return {
|
||||
...actual,
|
||||
get IS_PLATFORM() {
|
||||
return mockIsPlatform.value
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
type FlagState = {
|
||||
hasLoaded: boolean
|
||||
otelUnifiedLogs?: boolean
|
||||
@@ -60,28 +49,29 @@ const createWrapper = (queryClient: QueryClient) => {
|
||||
}
|
||||
}
|
||||
|
||||
const search: QuerySearchParamsType = {
|
||||
filter: null,
|
||||
latency: null,
|
||||
'timing.dns': null,
|
||||
'timing.connection': null,
|
||||
'timing.tls': null,
|
||||
'timing.ttfb': null,
|
||||
'timing.transfer': null,
|
||||
date: null,
|
||||
sort: null,
|
||||
size: 40,
|
||||
start: 0,
|
||||
direction: 'next',
|
||||
cursor: new Date(),
|
||||
id: null,
|
||||
show_connection_logs: true,
|
||||
edge_auth: true,
|
||||
edge_storage: true,
|
||||
edge_postgrest: true,
|
||||
user: null,
|
||||
}
|
||||
|
||||
const useUnifiedLogsQueries = () => {
|
||||
const search: QuerySearchParamsType = {
|
||||
filter: null,
|
||||
latency: null,
|
||||
'timing.dns': null,
|
||||
'timing.connection': null,
|
||||
'timing.tls': null,
|
||||
'timing.ttfb': null,
|
||||
'timing.transfer': null,
|
||||
date: null,
|
||||
sort: null,
|
||||
size: 40,
|
||||
start: 0,
|
||||
direction: 'next',
|
||||
cursor: new Date(),
|
||||
id: null,
|
||||
show_connection_logs: true,
|
||||
edge_auth: true,
|
||||
edge_storage: true,
|
||||
edge_postgrest: true,
|
||||
user: null,
|
||||
}
|
||||
const variables = { projectRef: 'project-ref', search }
|
||||
return {
|
||||
chart: useUnifiedLogsChartQuery(variables),
|
||||
@@ -96,7 +86,6 @@ describe('unified logs queries', () => {
|
||||
let queryClient: QueryClient
|
||||
|
||||
beforeEach(() => {
|
||||
mockIsPlatform.value = true
|
||||
flagState.hasLoaded = false
|
||||
flagState.otelUnifiedLogs = undefined
|
||||
flagState.unrelatedFlag = undefined
|
||||
@@ -109,12 +98,10 @@ describe('unified logs queries', () => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
test('does not request analytics while flags are pending', async () => {
|
||||
test('uses OTEL for initial and manual requests while flags are pending', async () => {
|
||||
const { result } = renderHook(useUnifiedLogsQueries, { wrapper: createWrapper(queryClient) })
|
||||
|
||||
await Promise.resolve()
|
||||
|
||||
expect(mockExecuteAnalyticsSql).not.toHaveBeenCalled()
|
||||
await waitFor(() => expect(mockExecuteAnalyticsSql).toHaveBeenCalledTimes(3))
|
||||
|
||||
await Promise.all([
|
||||
result.current.chart.refetch(),
|
||||
@@ -122,23 +109,28 @@ describe('unified logs queries', () => {
|
||||
result.current.logs.refetch(),
|
||||
])
|
||||
|
||||
expect(mockExecuteAnalyticsSql).not.toHaveBeenCalled()
|
||||
expect(mockExecuteAnalyticsSql).toHaveBeenCalledTimes(6)
|
||||
expect(endpoints()).toEqual(
|
||||
Array(6).fill('/platform/projects/{ref}/analytics/endpoints/logs.all.otel')
|
||||
)
|
||||
})
|
||||
|
||||
test('starts only ClickHouse requests when the flag resolves enabled', async () => {
|
||||
test('does not start or abort a legacy batch before the flag resolves enabled', async () => {
|
||||
const { rerender } = renderHook(useUnifiedLogsQueries, { wrapper: createWrapper(queryClient) })
|
||||
|
||||
await waitFor(() => expect(mockExecuteAnalyticsSql).toHaveBeenCalledTimes(3))
|
||||
expect(endpoints()).toEqual(
|
||||
Array(3).fill('/platform/projects/{ref}/analytics/endpoints/logs.all.otel')
|
||||
)
|
||||
|
||||
flagState.hasLoaded = true
|
||||
flagState.otelUnifiedLogs = true
|
||||
rerender()
|
||||
|
||||
await waitFor(() => expect(mockExecuteAnalyticsSql).toHaveBeenCalledTimes(3))
|
||||
await Promise.resolve()
|
||||
|
||||
expect(endpoints()).toEqual([
|
||||
'/platform/projects/{ref}/analytics/endpoints/logs.all.otel',
|
||||
'/platform/projects/{ref}/analytics/endpoints/logs.all.otel',
|
||||
'/platform/projects/{ref}/analytics/endpoints/logs.all.otel',
|
||||
])
|
||||
expect(mockExecuteAnalyticsSql).toHaveBeenCalledTimes(3)
|
||||
expect(mockExecuteAnalyticsSql.mock.calls.every(([args]) => !args.signal.aborted)).toBe(true)
|
||||
})
|
||||
|
||||
test('allows BigQuery requests when the flag resolves disabled', async () => {
|
||||
@@ -156,52 +148,26 @@ describe('unified logs queries', () => {
|
||||
])
|
||||
})
|
||||
|
||||
test('does not select BigQuery when ConfigCat fails', async () => {
|
||||
test('defaults to OTEL when ConfigCat fails', async () => {
|
||||
flagState.hasLoaded = true
|
||||
|
||||
renderHook(useUnifiedLogsQueries, { wrapper: createWrapper(queryClient) })
|
||||
|
||||
await Promise.resolve()
|
||||
|
||||
expect(mockExecuteAnalyticsSql).not.toHaveBeenCalled()
|
||||
await waitFor(() => expect(mockExecuteAnalyticsSql).toHaveBeenCalledTimes(3))
|
||||
expect(endpoints()).toEqual(
|
||||
Array(3).fill('/platform/projects/{ref}/analytics/endpoints/logs.all.otel')
|
||||
)
|
||||
})
|
||||
|
||||
test('does not select BigQuery when the loaded flags omit the backend flag', async () => {
|
||||
test('defaults to OTEL when the loaded flags omit the backend flag', async () => {
|
||||
flagState.hasLoaded = true
|
||||
flagState.unrelatedFlag = true
|
||||
|
||||
renderHook(useUnifiedLogsQueries, { wrapper: createWrapper(queryClient) })
|
||||
|
||||
await Promise.resolve()
|
||||
|
||||
expect(mockExecuteAnalyticsSql).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
test('keeps BigQuery available when self-hosted flags are disabled', async () => {
|
||||
mockIsPlatform.value = false
|
||||
|
||||
renderHook(useUnifiedLogsQueries, { wrapper: createWrapper(queryClient) })
|
||||
|
||||
await waitFor(() => expect(mockExecuteAnalyticsSql).toHaveBeenCalledTimes(3))
|
||||
|
||||
expect(endpoints()).toEqual([
|
||||
'/platform/projects/{ref}/analytics/endpoints/logs.all',
|
||||
'/platform/projects/{ref}/analytics/endpoints/logs.all',
|
||||
'/platform/projects/{ref}/analytics/endpoints/logs.all',
|
||||
])
|
||||
})
|
||||
|
||||
test('does not start and abort a legacy batch before enabled flags resolve', async () => {
|
||||
const { rerender } = renderHook(useUnifiedLogsQueries, { wrapper: createWrapper(queryClient) })
|
||||
|
||||
await Promise.resolve()
|
||||
expect(mockExecuteAnalyticsSql).not.toHaveBeenCalled()
|
||||
|
||||
flagState.hasLoaded = true
|
||||
flagState.otelUnifiedLogs = true
|
||||
rerender()
|
||||
|
||||
await waitFor(() => expect(mockExecuteAnalyticsSql).toHaveBeenCalledTimes(3))
|
||||
expect(endpoints()).not.toContain('/platform/projects/{ref}/analytics/endpoints/logs.all')
|
||||
expect(endpoints()).toEqual(
|
||||
Array(3).fill('/platform/projects/{ref}/analytics/endpoints/logs.all.otel')
|
||||
)
|
||||
})
|
||||
})
|
||||
Reference in new issue
Block a user