| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226 |
- import { useQuery } from '@tanstack/react-query'
- import { useMemo } from 'react'
- import type { LogsBarChartDatum } from '../ProjectHome/ProjectUsage.metrics'
- import { LogsTableName } from '../Settings/Logs/Logs.constants'
- import { genChartQuery } from '../Settings/Logs/Logs.utils'
- import {
- calculateAggregatedMetrics,
- calculateDateRange,
- calculateHealthMetrics,
- transformToBarChartData,
- type RawChartData,
- } from './useServiceHealthMetrics.utils'
- import { get } from '@/data/fetchers'
- import { useFillTimeseriesSorted } from '@/hooks/analytics/useFillTimeseriesSorted'
- import useTimeseriesUnixToIso from '@/hooks/analytics/useTimeseriesUnixToIso'
- export type ServiceKey = 'db' | 'functions' | 'auth' | 'storage' | 'realtime' | 'postgrest'
- export type ServiceHealthData = {
- total: number
- errorRate: number
- successRate: number
- errorCount: number
- warningCount: number
- okCount: number
- eventChartData: LogsBarChartDatum[]
- isLoading: boolean
- error: unknown | null
- refresh: () => void
- }
- type ServiceConfig = {
- table: LogsTableName
- enabled: boolean
- }
- const SERVICE_CONFIG: Record<ServiceKey, ServiceConfig> = {
- db: { table: LogsTableName.POSTGRES, enabled: true },
- auth: { table: LogsTableName.AUTH, enabled: true },
- functions: { table: LogsTableName.FN_EDGE, enabled: true },
- storage: { table: LogsTableName.STORAGE, enabled: true },
- realtime: { table: LogsTableName.REALTIME, enabled: true },
- postgrest: { table: LogsTableName.POSTGREST, enabled: true },
- }
- type ChartQueryResult = {
- timestamp: string | number
- ok_count: number
- warning_count: number
- error_count: number
- }
- /**
- * Fetches service health metrics using the same logic as the logs pages
- */
- const fetchServiceHealthMetrics = async (
- projectRef: string,
- table: LogsTableName,
- startDate: string,
- endDate: string,
- signal?: AbortSignal
- ): Promise<ChartQueryResult[]> => {
- const sql = genChartQuery(
- table,
- {
- iso_timestamp_start: startDate,
- iso_timestamp_end: endDate,
- },
- {}
- )
- const { data, error } = await get(`/platform/projects/{ref}/analytics/endpoints/logs.all`, {
- params: {
- path: { ref: projectRef },
- query: {
- sql,
- iso_timestamp_start: startDate,
- iso_timestamp_end: endDate,
- },
- },
- signal,
- })
- if (error ?? data?.error) {
- throw error ?? data?.error
- }
- return (data?.result ?? []) as ChartQueryResult[]
- }
- /**
- * Hook to fetch health metrics for a single service
- */
- const useServiceHealthQuery = ({
- projectRef,
- serviceKey,
- startDate,
- endDate,
- enabled,
- }: {
- projectRef: string
- serviceKey: ServiceKey
- startDate: string
- endDate: string
- enabled: boolean
- }) => {
- const config = SERVICE_CONFIG[serviceKey]
- const table = config.table
- const queryResult = useQuery({
- queryKey: ['service-health-metrics', projectRef, serviceKey, startDate, endDate, table],
- queryFn: ({ signal }) =>
- fetchServiceHealthMetrics(projectRef, table, startDate, endDate, signal),
- enabled: enabled && config.enabled && Boolean(projectRef),
- staleTime: 1000 * 60, // 1 minute
- })
- // Convert unix microseconds to ISO timestamps
- const normalizedData = useTimeseriesUnixToIso(queryResult.data ?? [], 'timestamp')
- // Fill gaps in timeseries
- const { data: filledData } = useFillTimeseriesSorted({
- data: normalizedData,
- timestampKey: 'timestamp',
- valueKey: 'ok_count',
- defaultValue: 0,
- startDate,
- endDate,
- })
- // Transform to LogsBarChartDatum format
- const eventChartData: LogsBarChartDatum[] = useMemo(
- () => transformToBarChartData(filledData as RawChartData[]),
- [filledData]
- )
- // Calculate metrics
- const metrics = useMemo(() => calculateHealthMetrics(eventChartData), [eventChartData])
- return {
- ...metrics,
- eventChartData,
- isLoading: queryResult.isLoading,
- error: queryResult.error,
- refresh: queryResult.refetch,
- }
- }
- /**
- * Hook to fetch observability overview data for all services using logs page queries
- */
- export const useServiceHealthMetrics = (
- projectRef: string,
- interval: '1hr' | '1day' | '7day',
- refreshKey: number
- ) => {
- // Calculate date range based on interval
- // refreshKey is intentionally included to force recalculation when user refreshes
- // eslint-disable-next-line react-hooks/exhaustive-deps
- const { startDate, endDate } = useMemo(() => calculateDateRange(interval), [interval, refreshKey])
- const enabled = Boolean(projectRef)
- // Fetch metrics for each service
- const db = useServiceHealthQuery({ projectRef, serviceKey: 'db', startDate, endDate, enabled })
- const auth = useServiceHealthQuery({
- projectRef,
- serviceKey: 'auth',
- startDate,
- endDate,
- enabled,
- })
- const functions = useServiceHealthQuery({
- projectRef,
- serviceKey: 'functions',
- startDate,
- endDate,
- enabled,
- })
- const storage = useServiceHealthQuery({
- projectRef,
- serviceKey: 'storage',
- startDate,
- endDate,
- enabled,
- })
- const realtime = useServiceHealthQuery({
- projectRef,
- serviceKey: 'realtime',
- startDate,
- endDate,
- enabled,
- })
- const postgrest = useServiceHealthQuery({
- projectRef,
- serviceKey: 'postgrest',
- startDate,
- endDate,
- enabled,
- })
- const services: Record<ServiceKey, ServiceHealthData> = useMemo(
- () => ({
- db,
- auth,
- functions,
- storage,
- realtime,
- postgrest,
- }),
- [db, auth, functions, storage, realtime, postgrest]
- )
- // Calculate aggregated metrics
- const aggregated = useMemo(() => calculateAggregatedMetrics(Object.values(services)), [services])
- const isLoading = Object.values(services).some((s) => s.isLoading)
- return {
- services,
- aggregated,
- isLoading,
- endDate,
- }
- }
|