import { useQueryClient } from '@tanstack/react-query' import { useParams } from 'common' import dayjs from 'dayjs' import { RefreshCw } from 'lucide-react' import { useRouter } from 'next/router' import { useCallback, useMemo, useState } from 'react' import { Badge, Button, Tooltip, TooltipContent, TooltipTrigger } from 'ui' import { useUnifiedLogsPreview } from '../App/FeaturePreview/FeaturePreviewContext' import { DatabaseInfrastructureSection } from './DatabaseInfrastructureSection' import { OBSERVABILITY_DOCS_HREFS } from './Observability.constants' import { useObservabilityOverviewData } from './ObservabilityOverview.utils' import { ObservabilityOverviewFooter } from './ObservabilityOverviewFooter' import { ServiceHealthTable } from './ServiceHealthTable' import { useSlowQueriesCount } from './useSlowQueriesCount' import ReportHeader from '@/components/interfaces/Reports/ReportHeader' import { ReportPadding } from '@/components/interfaces/Reports/ReportPadding' import { buildUnifiedLogsUrl, type UnifiedLogType, } from '@/components/interfaces/UnifiedLogs/UnifiedLogs.utils' import { DocsButton } from '@/components/ui/DocsButton' import { ChartIntervalDropdown } from '@/components/ui/Logs/ChartIntervalDropdown' import { CHART_INTERVALS } from '@/components/ui/Logs/logs.utils' import { ShortcutTooltip } from '@/components/ui/ShortcutTooltip' import { useIsDataApiEnabled } from '@/hooks/misc/useIsDataApiEnabled' import { useIsFeatureEnabled } from '@/hooks/misc/useIsFeatureEnabled' import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization' import { SHORTCUT_IDS } from '@/state/shortcuts/registry' import { useShortcut } from '@/state/shortcuts/useShortcut' const REPORT_TITLE = 'Overview' type ChartIntervalKey = '1hr' | '1day' | '7day' export const ObservabilityOverview = () => { const router = useRouter() const { ref: projectRef } = useParams() const { data: organization } = useSelectedOrganizationQuery() const queryClient = useQueryClient() const { isEnabled: isUnifiedLogsEnabled } = useUnifiedLogsPreview() const { projectStorageAll: storageSupported } = useIsFeatureEnabled(['project_storage:all']) const { isEnabled: isDataApiEnabled } = useIsDataApiEnabled({ projectRef }) const DEFAULT_INTERVAL: ChartIntervalKey = '1day' const [interval, setInterval] = useState(DEFAULT_INTERVAL) const [refreshKey, setRefreshKey] = useState(0) const [showIntervalDropdown, setShowIntervalDropdown] = useState(false) const selectedInterval = CHART_INTERVALS.find((i) => i.key === interval) || CHART_INTERVALS[1] const { datetimeFormat } = useMemo(() => { const format = selectedInterval.format || 'MMM D, ha' return { datetimeFormat: format } }, [selectedInterval]) const overviewData = useObservabilityOverviewData(projectRef!, interval, refreshKey) const { slowQueriesCount, isLoading: slowQueriesLoading } = useSlowQueriesCount( projectRef, refreshKey ) const handleRefresh = useCallback(() => { setRefreshKey((prev) => prev + 1) queryClient.invalidateQueries({ queryKey: ['projects', projectRef, 'service-health'] }) queryClient.invalidateQueries({ queryKey: ['project-metrics'] }) queryClient.invalidateQueries({ queryKey: ['infra-monitoring'] }) queryClient.invalidateQueries({ queryKey: ['max-connections'] }) }, [queryClient, projectRef]) useShortcut(SHORTCUT_IDS.OBSERVABILITY_REFRESH, handleRefresh) useShortcut(SHORTCUT_IDS.OBSERVABILITY_TOGGLE_DATE_PICKER, () => { setShowIntervalDropdown((open) => !open) }) const getLogsUrl = useCallback( (logType: UnifiedLogType, legacyLogsUrl: string) => isUnifiedLogsEnabled ? buildUnifiedLogsUrl({ projectRef: projectRef!, logType }) : `/project/${projectRef}${legacyLogsUrl}`, [projectRef, isUnifiedLogsEnabled] ) const serviceBase = useMemo( () => [ { key: 'data_api' as const, name: 'API Gateway', reportUrl: undefined, logType: 'edge' as const, logsUrl: getLogsUrl('edge', '/logs/edge-logs'), enabled: isDataApiEnabled, hasReport: false, }, { key: 'db' as const, name: 'Database', reportUrl: `/project/${projectRef}/observability/database`, logType: 'postgres' as const, logsUrl: getLogsUrl('postgres', '/logs/postgres-logs'), enabled: true, hasReport: true, }, { key: 'postgrest' as const, name: 'PostgREST', reportUrl: `/project/${projectRef}/observability/postgrest`, logType: 'postgrest' as const, logsUrl: getLogsUrl('postgrest', '/logs/postgrest-logs'), enabled: true, hasReport: true, }, { key: 'auth' as const, name: 'Auth', reportUrl: `/project/${projectRef}/observability/auth`, logType: 'auth' as const, logsUrl: getLogsUrl('auth', '/logs/auth-logs'), enabled: true, hasReport: true, }, { key: 'functions' as const, name: 'Edge Functions', reportUrl: `/project/${projectRef}/observability/edge-functions`, logType: 'edge function' as const, logsUrl: getLogsUrl('edge function', '/logs/edge-functions-logs'), enabled: true, hasReport: true, }, { key: 'storage' as const, name: 'Storage', reportUrl: `/project/${projectRef}/observability/storage`, logType: 'storage' as const, logsUrl: getLogsUrl('storage', '/logs/storage-logs'), enabled: storageSupported, hasReport: true, }, { key: 'realtime' as const, name: 'Realtime', reportUrl: `/project/${projectRef}/observability/realtime`, logType: 'realtime' as const, logsUrl: getLogsUrl('realtime', '/logs/realtime-logs'), enabled: true, hasReport: true, }, ], [projectRef, storageSupported, isDataApiEnabled, getLogsUrl] ) const enabledServices = serviceBase.filter((s) => s.enabled) const dbServiceData = overviewData.services.db // Navigate to the log view scoped to the clicked bar's bucket window const handleBarClick = useCallback( (service: { logType: UnifiedLogType; logsUrl: string }) => (datum: any) => { if (!datum?.timestamp) return // datum.timestamp is already the UTC-truncated bucket boundary from timestamp_trunc(), // so use it directly to avoid local-timezone startOf() misalignment (e.g. UTC+5:30). const unit = interval === '1hr' ? 'minute' : 'hour' const start = datum.timestamp const end = dayjs.utc(datum.timestamp).add(1, unit).toISOString() if (isUnifiedLogsEnabled) { router.push( buildUnifiedLogsUrl({ projectRef: projectRef!, logType: service.logType, start, end }) ) } else { const queryParams = new URLSearchParams({ its: start, ite: end }) const separator = service.logsUrl.includes('?') ? '&' : '?' router.push(`${service.logsUrl}${separator}${queryParams.toString()}`) } }, [router, interval, isUnifiedLogsEnabled, projectRef] ) return (
Beta

This page is subject to change

setInterval(interval as ChartIntervalKey)} organizationSlug={organization?.slug} dropdownAlign="end" tooltipSide="left" open={showIntervalDropdown} onOpenChange={setShowIntervalDropdown} />
({ key: service.key, name: service.name, description: '', reportUrl: service.hasReport ? service.reportUrl : undefined, logType: service.logType, logsUrl: service.logsUrl, }))} serviceData={overviewData.services} onBarClick={handleBarClick} datetimeFormat={datetimeFormat} />
) }