Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Table } from '@tanstack/react-table'
import { Cable, Clock, Database } from 'lucide-react'
import { Cable, ChevronDown, Clock, Database } from 'lucide-react'
import { memo } from 'react'
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from 'ui'

import { ColumnSchema } from '../../../UnifiedLogs.schema'
import { getRowTimestampMs } from '../../../UnifiedLogs.utils'
Expand Down Expand Up @@ -70,40 +71,52 @@ export const PostgresFlowDetail = memo(function PostgresFlowDetail({
((data as Record<string, unknown>)?.error_severity as string | undefined)

return (
<div className="[&>*:nth-child(even)]:bg-surface-100/50">
<div>
<DetailSectionHeader
title="Request started"
icon={Clock}
summary={formattedTime ?? undefined}
/>

<DetailSectionHeader title="Postgres" icon={Database} />
<Collapsible defaultOpen>
<CollapsibleTrigger className="w-full flex items-center justify-between pr-4 [&[data-state=open]>svg]:-rotate-180!">
<DetailSectionHeader title="Postgres" icon={Database} />
<ChevronDown className="transition-transform duration-200" strokeWidth={1.5} size={14} />
</CollapsibleTrigger>
<CollapsibleContent className="[&>*:nth-child(odd)]:bg-surface-100/50">
{postgresPrimaryFields.map((field) => (
<FieldDetailRow
key={field.id}
config={field}
data={data}
enrichedData={enrichedData}
isLoading={isLoading}
filterFields={filterFields}
table={table}
/>
))}
</CollapsibleContent>
</Collapsible>

{postgresPrimaryFields.map((field) => (
<FieldDetailRow
key={field.id}
config={field}
data={data}
enrichedData={enrichedData}
isLoading={isLoading}
filterFields={filterFields}
table={table}
/>
))}

<DetailSectionHeader title="Connection & Session Details" icon={Cable} />

{postgresDetailsFields.map((field) => (
<FieldDetailRow
key={field.id}
config={field}
data={data}
enrichedData={enrichedData}
isLoading={isLoading}
filterFields={filterFields}
table={table}
/>
))}
<Collapsible defaultOpen>
<CollapsibleTrigger className="w-full flex items-center justify-between pr-4 [&[data-state=open]>svg]:-rotate-180!">
<DetailSectionHeader title="Connection & Session Details" icon={Cable} />
<ChevronDown className="transition-transform duration-200" strokeWidth={1.5} size={14} />
</CollapsibleTrigger>
<CollapsibleContent className="[&>*:nth-child(odd)]:bg-surface-100/50">
{postgresDetailsFields.map((field) => (
<FieldDetailRow
key={field.id}
config={field}
data={data}
enrichedData={enrichedData}
isLoading={isLoading}
filterFields={filterFields}
table={table}
/>
))}
</CollapsibleContent>
</Collapsible>

<DetailSectionHeader
title="Operation result"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { LucideIcon } from 'lucide-react'
import { partition } from 'lodash'
import { ChevronDown, LucideIcon } from 'lucide-react'
import { memo } from 'react'
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from 'ui'

import { BlockFieldConfig, BlockFieldProps, ServiceFlowBlockProps } from '../../types'
import { DetailRow } from './DetailRow'
Expand Down Expand Up @@ -59,25 +61,30 @@ export function createBlock(config: BlockConfig) {
filterFields,
table,
}: ServiceFlowBlockProps) {
return (
<>
<DetailSectionHeader title={config.title} icon={config.icon} />
const [seeMoreFieldsSections, otherSections] = partition(
config.sections,
(x) => 'type' in x && x.type === 'fieldWithSeeMore'
) as [FieldWithSeeMoreSection[], BlockSection[]]

{config.primaryFields?.map((field) => (
<FieldRow
key={field.id}
config={field}
data={data}
enrichedData={enrichedData}
isLoading={isLoading}
filterFields={filterFields}
table={table}
/>
))}
/**
* [Joshen] AFAICT, a lot of the fields do not apply for auth logs as the data is not present
* Am opting to hide all the additional fields only for auth logs, but we can present them if
* we do eventually have the data to show
*/

{config.sections?.map((section) => {
if ('type' in section && section.type === 'fieldWithSeeMore') {
return [section.primaryField, ...section.additionalFields].map((field) => (
return (
<>
<Collapsible defaultOpen>
<CollapsibleTrigger className="w-full flex items-center justify-between pr-4 [&[data-state=open]>svg]:-rotate-180!">
<DetailSectionHeader title={config.title} icon={config.icon} />
<ChevronDown
className="transition-transform duration-200"
strokeWidth={1.5}
size={14}
/>
</CollapsibleTrigger>
<CollapsibleContent className="[&>*:nth-child(odd)]:bg-surface-100/50">
{config.primaryFields?.map((field) => (
<FieldRow
key={field.id}
config={field}
Expand All @@ -87,27 +94,52 @@ export function createBlock(config: BlockConfig) {
filterFields={filterFields}
table={table}
/>
))
}
))}
{data.log_type !== 'auth' &&
seeMoreFieldsSections.map((section) => {
return [section.primaryField, ...section.additionalFields].map((field) => (
<FieldRow
key={field.id}
config={field}
data={data}
enrichedData={enrichedData}
isLoading={isLoading}
filterFields={filterFields}
table={table}
/>
))
})}
</CollapsibleContent>
</Collapsible>

const blockSection = section as BlockSection
return (
<span key={blockSection.title} className="contents">
<DetailSectionHeader title={blockSection.title} icon={blockSection.icon} />
{blockSection.fields.map((field) => (
<FieldRow
key={field.id}
config={field}
data={data}
enrichedData={enrichedData}
isLoading={isLoading}
filterFields={filterFields}
table={table}
/>
))}
</span>
)
})}
{data.log_type !== 'auth' &&
otherSections.map((section) => {
return (
<Collapsible key={section.title}>
<CollapsibleTrigger className="w-full flex items-center justify-between pr-4 [&[data-state=open]>svg]:-rotate-180!">
<DetailSectionHeader title={section.title} icon={section.icon} />
<ChevronDown
className="transition-transform duration-200"
strokeWidth={1.5}
size={14}
/>
</CollapsibleTrigger>
<CollapsibleContent className="[&>*:nth-child(odd)]:bg-surface-100/50">
{section.fields.map((field) => (
<FieldRow
key={field.id}
config={field}
data={data}
enrichedData={enrichedData}
isLoading={isLoading}
filterFields={filterFields}
table={table}
/>
))}
</CollapsibleContent>
</Collapsible>
)
})}
</>
)
})
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { BlockFieldConfig } from '../types'
import { getStorageMetadata } from '../utils/storageUtils'
import { formatBytes } from '@/lib/helpers'
import { formatBytes, tryParseJson } from '@/lib/helpers'

// Helper functions that avoid duplication with existing storage utilities
const getFileName = (path: string): string => {
Expand Down Expand Up @@ -371,28 +371,34 @@ export const postgrestResponseFields: BlockFieldConfig[] = [
// Primary GoTrue/Auth Fields (Always Visible)
export const authPrimaryFields: BlockFieldConfig[] = [
{
id: 'auth_path',
label: 'Auth Path',
id: 'log_id',
label: 'Log ID',
getValue: (data, enrichedData) => {
return enrichedData?.path || enrichedData?.request_path || data?.path
const logId = data?.id || enrichedData?.id
return logId ?? null
},
requiresEnrichedData: true,
},
{
id: 'log_id',
label: 'Log ID',
id: 'status',
label: 'Status',
getValue: (data) => {
return data.status
},
},
{
id: 'auth_path',
label: 'Auth Path',
getValue: (data, enrichedData) => {
const logId = data?.id || enrichedData?.id
return logId ? `${logId.substring(0, 8)}...` : null
return enrichedData?.path || enrichedData?.request_path || data?.path || data?.pathname
},
},
{
id: 'referer',
label: 'Referer',
getValue: (_data, enrichedData) => {
return enrichedData?.headers_referer || null
getValue: (data, enrichedData) => {
const eventMessage = tryParseJson(data.event_message)
return eventMessage?.referer || enrichedData?.headers_referer || null
},
requiresEnrichedData: true,
},
]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,21 +158,19 @@ export function ServiceFlowPanel({
table={table}
/>
) : (
<div className="[&>*:nth-child(even)]:bg-surface-100/50">
<div>
<DetailSectionHeader
title="Request started"
icon={Clock}
summary={formattedTime ?? undefined}
/>

<MemoizedNetworkBlock
data={selectedRow}
enrichedData={serviceFlowData?.result?.[0]}
isLoading={isLoading}
filterFields={filterFields}
table={table}
/>

{serviceFlowType === 'auth' ? (
<MemoizedGoTrueBlock
data={selectedRow}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,10 +136,8 @@ export function parseAuthLogEventMessage(value: string | undefined): string | un

const msg = parsed.msg
if (typeof msg === 'string' && msg.trim()) {
const authEvent =
'action' in parsed || 'auth_event_action' in parsed
? (parsed.action || parsed.auth_event.action).replaceAll('_', ' ')
: undefined
const action = parsed.action ?? parsed.auth_event?.action
const authEvent = typeof action === 'string' ? action.replaceAll('_', ' ') : undefined
return `${authEvent ? `${authEvent}: ` : ''}${msg}`
}
}
Expand Down
5 changes: 3 additions & 2 deletions apps/studio/data/logs/otel-inspection.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,10 @@ export function flattenOtelInspectionRow(
): UnifiedLogInspectionEntry & Record<string, unknown> {
const attrs: Record<string, any> = row.log_attributes ?? {}

const host = attrs['request.host'] ?? attrs['host'] ?? ''
// Path key differs across sources: edge_logs uses `request.path`,
// function_edge_logs uses `request.pathname`. Coalesce.
const requestPath = attrs['request.path'] ?? attrs['request.pathname']
const requestPath = attrs['request.path'] ?? attrs['request.pathname'] ?? attrs['path'] ?? ''
// Status: HTTP response code for gateway rows, Postgres SQL state code for postgres rows.
const status =
row.source === 'postgres_logs' ? attrs['parsed.sql_state_code'] : attrs['response.status_code']
Expand Down Expand Up @@ -70,9 +71,9 @@ export function flattenOtelInspectionRow(
level,

// Network -- flat aliases the panel reads as `enrichedData.request_path` etc.
host,
method: attrs['request.method'] ?? '',
path: requestPath ?? '',
host: attrs['request.host'] ?? '',
status_code: status != null ? String(status) : '',
request_path: requestPath ?? null,
request_host: attrs['request.host'] ?? null,
Expand Down
Loading