Skip to content

Commit

Permalink
Add support for logs filtering under postgres logs (supabase#23394)
Browse files Browse the repository at this point in the history
* Add support for logs filtering under postgres logs

* Update edge logs and postgres logs searching for replica

* Update button CTA
  • Loading branch information
joshenlim committed Apr 29, 2024
1 parent 3a11e23 commit 681a837
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -277,12 +277,14 @@ const _SQL_FILTER_COMMON = {
export const SQL_FILTER_TEMPLATES: any = {
postgres_logs: {
..._SQL_FILTER_COMMON,
database: (value: string) => `identifier = '${value}'`,
'severity.error': `parsed.error_severity in ('ERROR', 'FATAL', 'PANIC')`,
'severity.noError': `parsed.error_severity not in ('ERROR', 'FATAL', 'PANIC')`,
'severity.log': `parsed.error_severity = 'LOG'`,
},
edge_logs: {
..._SQL_FILTER_COMMON,
database: (value: string) => `identifier = '${value}'`,
'status_code.error': `response.status_code between 500 and 599`,
'status_code.success': `response.status_code between 200 and 299`,
'status_code.warning': `response.status_code between 400 and 499`,
Expand Down Expand Up @@ -344,6 +346,7 @@ export const SQL_FILTER_TEMPLATES: any = {
},
supavisor_logs: {
..._SQL_FILTER_COMMON,
database: (value: string) => `m.project like '${value}%'`,
},
}

Expand Down Expand Up @@ -712,3 +715,9 @@ export const TIER_QUERY_LIMITS: {
TEAM: { text: '28 days', value: 28, unit: 'day', promptUpgrade: true },
ENTERPRISE: { text: '90 days', value: 90, unit: 'day', promptUpgrade: false },
}

export const LOG_ROUTES_WITH_REPLICA_SUPPORT = [
'/project/[ref]/logs/edge-logs',
'/project/[ref]/logs/pooler-logs',
'/project/[ref]/logs/postgres-logs',
]
12 changes: 3 additions & 9 deletions apps/studio/components/interfaces/Settings/Logs/Logs.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ const genWhereStatement = (table: LogsTableName, filters: Filters) => {
if (value !== undefined && typeof template === 'function') {
return template(value)
} else if (template === undefined) {
// resolve unknwon filters (possibly from filter overrides)
// resolve unknown filters (possibly from filter overrides)
// no template, set a default
if (typeof value === 'string') {
return `${dotKey} = '${value}'`
Expand All @@ -105,12 +105,6 @@ const genWhereStatement = (table: LogsTableName, filters: Filters) => {
(typeof filters[rootKey] === 'string' && (filters[rootKey] as string).length === 0)
) {
return null
} else if (rootKey === 'database') {
return table === 'edge_logs'
? `(request.host like '${filters[rootKey]}%')`
: table === 'supavisor_logs'
? `(m.project like '${filters[rootKey]}%')`
: null
} else if (typeof filters[rootKey] === 'object') {
// join all statements with an OR
const nestedStatements = getDotKeys(filters[rootKey] as Filters, rootKey)
Expand Down Expand Up @@ -145,7 +139,7 @@ export const genDefaultQuery = (table: LogsTableName, filters: Filters) => {
const orderBy = 'order by timestamp desc'
switch (table) {
case 'edge_logs':
return `select id, timestamp, event_message, request.method, request.path, response.status_code
return `select id, identifier, timestamp, event_message, request.method, request.path, response.status_code
from ${table}
${joins}
${where}
Expand All @@ -154,7 +148,7 @@ export const genDefaultQuery = (table: LogsTableName, filters: Filters) => {
`

case 'postgres_logs':
return `select postgres_logs.timestamp, id, event_message, parsed.error_severity from ${table}
return `select identifier, postgres_logs.timestamp, id, event_message, parsed.error_severity from ${table}
${joins}
${where}
${orderBy}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
import { Fragment, useState } from 'react'

import { Button, Checkbox, Form, Popover } from 'ui'
import React, { useState } from 'react'
import { Filters, FilterSet } from '.'
import { FilterSet, Filters } from '.'

interface Props {
interface LogsFilterPopoverProps {
options: FilterSet
filters: Filters
onFiltersChange: (filters: Filters) => void
buttonClassName: string
}

const LogsFilterPopover: React.FC<Props> = ({
const LogsFilterPopover = ({
options,
filters,
onFiltersChange,
buttonClassName,
}) => {
}: LogsFilterPopoverProps) => {
const [open, setOpen] = useState(false)

const handleReset = () => {
Expand Down Expand Up @@ -51,7 +52,7 @@ const LogsFilterPopover: React.FC<Props> = ({
<>
<div className="space-y-4 py-6">
{options.options.map((x, i: number) => (
<React.Fragment key={x.key}>
<Fragment key={x.key}>
<Checkbox
value="true"
id={`${options.key}.${x.key}`}
Expand All @@ -62,15 +63,15 @@ const LogsFilterPopover: React.FC<Props> = ({
defaultChecked={(filters?.[options.key] as Filters)?.[x.key] as boolean}
/>
{i !== options.options.length - 1 && <Popover.Separator />}
</React.Fragment>
</Fragment>
))}
</div>
<div className="flex items-center justify-end gap-2 border-t border-default bg-studio py-2 px-3">
<Button size="tiny" type="default" onClick={handleReset} htmlType="button">
Clear
</Button>
<Button loading={isSubmitting} type="primary" htmlType="submit">
Save
Apply
</Button>
</div>
</>
Expand Down
22 changes: 18 additions & 4 deletions apps/studio/components/interfaces/Settings/Logs/LogsPreviewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import { useSelectedOrganization } from 'hooks'
import useLogsPreview from 'hooks/analytics/useLogsPreview'
import { useUpgradePrompt } from 'hooks/misc/useUpgradePrompt'
import { useDatabaseSelectorStateSnapshot } from 'state/database-selector'
import { LOGS_TABLES } from './Logs.constants'
import { LOGS_TABLES, LOG_ROUTES_WITH_REPLICA_SUPPORT } from './Logs.constants'
import UpgradePrompt from './UpgradePrompt'

/**
Expand Down Expand Up @@ -110,9 +110,23 @@ export const LogsPreviewer = ({
}, [its, subscription])

useEffect(() => {
if (readReplicasEnabled && db !== undefined) {
const database = databases?.find((d) => d.identifier === db)
if (database !== undefined) state.setSelectedDatabaseId(db)
if (readReplicasEnabled) {
if (db !== undefined) {
const database = databases?.find((d) => d.identifier === db)
if (database !== undefined) state.setSelectedDatabaseId(db)
} else if (
state.selectedDatabaseId !== undefined &&
state.selectedDatabaseId !== projectRef
) {
if (LOG_ROUTES_WITH_REPLICA_SUPPORT.includes(router.pathname)) {
router.push({
pathname: router.pathname,
query: { ...router.query, db: state.selectedDatabaseId },
})
} else {
state.setSelectedDatabaseId(projectRef)
}
}
}
}, [readReplicasEnabled, db, isSuccess])

Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,15 @@
import { Eye, EyeOff, RefreshCw, Search, Terminal } from 'lucide-react'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { useEffect, useState } from 'react'
import {
Button,
IconEye,
IconEyeOff,
IconRefreshCw,
IconSearch,
IconTerminal,
Input,
TooltipContent_Shadcn_,
TooltipTrigger_Shadcn_,
Tooltip_Shadcn_,
} from 'ui'
import { Button, Input, TooltipContent_Shadcn_, TooltipTrigger_Shadcn_, Tooltip_Shadcn_ } from 'ui'

import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import CSVButton from 'components/ui/CSVButton'
import DatabaseSelector from 'components/ui/DatabaseSelector'
import { Filters, LogSearchCallback, LogTemplate, PREVIEWER_DATEPICKER_HELPERS } from '.'
import DatePickers from './Logs.DatePickers'
import { FILTER_OPTIONS, LogsTableName } from './Logs.constants'
import { FILTER_OPTIONS, LOG_ROUTES_WITH_REPLICA_SUPPORT, LogsTableName } from './Logs.constants'
import LogsFilterPopover from './LogsFilterPopover'

interface PreviewFilterPanelProps {
Expand Down Expand Up @@ -72,8 +62,7 @@ const PreviewFilterPanel = ({

// [Joshen] These are the routes tested that can show replica logs
const showDatabaseSelector =
project?.is_read_replicas_enabled &&
['/project/[ref]/logs/edge-logs', '/project/[ref]/logs/pooler-logs'].includes(router.pathname)
project?.is_read_replicas_enabled && LOG_ROUTES_WITH_REPLICA_SUPPORT.includes(router.pathname)

const hasEdits = search !== defaultSearchValue

Expand All @@ -99,11 +88,11 @@ const PreviewFilterPanel = ({
{newCount > 1000 ? `${Math.floor(newCount / 100) / 10}K` : newCount}
</p>
</div>
<div className="h-full w-full animate-ping rounded-full bg-green-800 opacity-60"></div>
<div className="h-4 w-4 animate-ping rounded-full bg-green-800 opacity-60"></div>
<div className="z-60 absolute top-0 right-0 h-full w-full rounded-full bg-green-900 opacity-80"></div>
</div>
)}
<IconRefreshCw />
<RefreshCw />
</div>
}
loading={isLoading}
Expand Down Expand Up @@ -147,7 +136,7 @@ const PreviewFilterPanel = ({
}}
icon={
<div className="text-foreground-lighter">
<IconSearch size={14} />
<Search size={14} />
</div>
}
value={search}
Expand Down Expand Up @@ -205,7 +194,7 @@ const PreviewFilterPanel = ({
<Button
type="default"
onClick={() => onToggleEventChart()}
icon={isShowingEventChart ? <IconEye /> : <IconEyeOff />}
icon={isShowingEventChart ? <Eye /> : <EyeOff />}
>
Chart
</Button>
Expand All @@ -217,7 +206,7 @@ const PreviewFilterPanel = ({
<div className="flex items-center justify-center gap-x-2">
<Tooltip_Shadcn_ delayDuration={100}>
<TooltipTrigger_Shadcn_ asChild>
<Button asChild className="px-1" type="default" icon={<IconTerminal />}>
<Button asChild className="px-1" type="default" icon={<Terminal />}>
<Link href={queryUrl} />
</Button>
</TooltipTrigger_Shadcn_>
Expand Down

0 comments on commit 681a837

Please sign in to comment.