import { useFlag, useParams } from 'common' import { BookOpen, Check, ChevronDown, ChevronsUpDown, Copy, ExternalLink } from 'lucide-react' import Link from 'next/link' import { ReactNode, useEffect, useState } from 'react' import { logConstants } from 'shared-data' import { Badge, Button, Card, cn, Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, copyToClipboard, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, Popover, PopoverContent, PopoverTrigger, Sheet, SheetContent, SheetDescription, SheetHeader, SheetSection, SheetTitle, SheetTrigger, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, Tooltip, TooltipContent, TooltipTrigger, } from 'ui' import { ShimmeringLoader } from 'ui-patterns/ShimmeringLoader' import { EXPLORER_DATEPICKER_HELPERS, LOGS_SOURCE_DESCRIPTION, LogsTableName, } from './Logs.constants' import { DatePickerValue, LogsDatePicker } from './Logs.DatePickers' import { otelFieldsFromKeys, toOtelFieldSchemas } from './Logs.fieldReference' import { LogsWarning, LogTemplate } from './Logs.types' import { useOtelLogKeysQuery } from '@/data/logs/otel-log-keys-query' import { useIsFeatureEnabled } from '@/hooks/misc/useIsFeatureEnabled' import { useShowMultigresLogs } from '@/hooks/misc/useShowMultigresLogs' import { DOCS_URL } from '@/lib/constants' interface LogsQueryPanelProps { templates?: LogTemplate[] value: DatePickerValue warnings: LogsWarning[] onSelectTemplate: (template: LogTemplate) => void onSelectSource: (source: string) => void onDateChange: (value: DatePickerValue) => void showRewriteAction?: boolean isRewriting?: boolean onRewrite?: () => void } function DropdownMenuItemContent({ name, desc }: { name: ReactNode; desc?: string }) { return (
{name}
{desc &&
{desc}
}
) } export const LogsQueryPanel = ({ templates = [], value, warnings, onSelectTemplate, onSelectSource, onDateChange, showRewriteAction = false, isRewriting = false, onRewrite, }: LogsQueryPanelProps) => { const [showReference, setShowReference] = useState(false) const { logsTemplates } = useIsFeatureEnabled(['logs:templates']) const { projectAuthAll: authEnabled, projectStorageAll: storageEnabled, projectEdgeFunctionAll: edgeFunctionsEnabled, } = useIsFeatureEnabled(['project_auth:all', 'project_storage:all', 'project_edge_function:all']) const logsTableNames = Object.entries(LogsTableName) .filter(([key]) => { if (key === 'AUTH') return authEnabled if (key === 'STORAGE') return storageEnabled if (key === 'FN_EDGE') return edgeFunctionsEnabled if (key === 'PG_CRON') return false return true }) .map(([, value]) => value) const [selectedDatePickerValue, setSelectedDatePickerValue] = useState(value) useEffect(() => { setSelectedDatePickerValue(value) }, [value.from, value.to, value.text, value.isHelper]) const [open, setOpen] = useState(false) const showMultigresLogs = useShowMultigresLogs() const useOtel = useFlag('otelLegacyLogs') const baseSchemas = logConstants.schemas.filter( (schema) => schema.reference !== 'multigres_logs' || showMultigresLogs ) const schemas = useOtel ? toOtelFieldSchemas(baseSchemas) : baseSchemas const [selectedRef, setSelectedRef] = useState(schemas[0]?.reference) const selectedSchema = schemas.find((s) => s.reference === selectedRef) ?? schemas[0] const { ref: projectRef } = useParams() const { data: discoveredKeys, isPending: isLoadingKeys } = useOtelLogKeysQuery( { projectRef, source: selectedRef }, { enabled: useOtel && showReference } ) return (
{logsTableNames .sort((a, b) => a.localeCompare(b)) .map((source) => ( onSelectSource(source)}> ))} {logsTemplates && ( {templates .sort((a, b) => a.label!.localeCompare(b.label!)) .map((template) => ( onSelectTemplate(template)} >

{template.label}

))}
)} { setSelectedDatePickerValue(value) onDateChange(value) }} helpers={EXPLORER_DATEPICKER_HELPERS} />
0 ? 'opacity-100' : 'invisible h-0 w-0 opacity-0' }`} > {warnings.length} {warnings.length > 1 ? 'warnings' : 'warning'} {warnings.map((warning, index) => (

{warning.text}{' '} {warning.link && ( {warning.linkText || 'View'} )}

))}
{showRewriteAction && onRewrite && ( Logs now run on a ClickHouse-backed engine. Click to rewrite this query with the Assistant. )} Field Reference {useOtel ? ( The following table shows the fields available on each source. Nested fields live in the{' '} log_attributes{' '} map and are read with{' '} log_attributes['key'] {' '} — no unnesting joins needed. ) : ( The following table shows all the available paths that can be queried from each respective source. Do note that to access nested keys, you would need to perform the necessary{' '} unnesting joins )} No source found. {schemas.map((schema) => ( { setSelectedRef(schema.reference) setOpen(false) }} > {schema.name} ))} Path Type {useOtel && isLoadingKeys ? Array.from({ length: 3 }).map((_, i) => ( )) : (useOtel ? otelFieldsFromKeys(discoveredKeys ?? []) : selectedSchema.fields ).map((field) => )}
) } const Field = ({ field, }: { field: { path: string type: string } }) => { const [isCopied, setIsCopied] = useState(false) return ( copyToClipboard(field.path, () => { setIsCopied(true) setTimeout(() => setIsCopied(false), 3000) }) } > {field.path} {isCopied ? ( Copied ) : ( Copy value )} {field.type} ) }