From 7f77f16fb20f880f4be3bc71c1ff3cfce17f80fb Mon Sep 17 00:00:00 2001 From: Jonathan Summers-Muir Date: Tue, 13 May 2025 14:17:31 +0800 Subject: [PATCH] added logs and ui to view logs if any --- .../data-table-column-status-code.tsx | 4 +- .../DataTableDemo/providers/controls.tsx | 2 +- .../components/interfaces/logs-new/client.tsx | 204 +++++++++++------- .../logs-new/components/function-logs-tab.tsx | 57 +++++ .../interfaces/logs-new/constants.tsx | 7 + .../interfaces/logs-new/logs.queries.ts | 21 +- .../interfaces/logs-new/query-options-new.ts | 1 + .../components/interfaces/logs-new/schema.ts | 2 + 8 files changed, 208 insertions(+), 90 deletions(-) create mode 100644 apps/studio/components/interfaces/logs-new/components/function-logs-tab.tsx diff --git a/apps/studio/components/interfaces/DataTableDemo/components/data-table/data-table-column/data-table-column-status-code.tsx b/apps/studio/components/interfaces/DataTableDemo/components/data-table/data-table-column/data-table-column-status-code.tsx index e5a61c8e62c..d463b5a0f0c 100644 --- a/apps/studio/components/interfaces/DataTableDemo/components/data-table/data-table-column/data-table-column-status-code.tsx +++ b/apps/studio/components/interfaces/DataTableDemo/components/data-table/data-table-column/data-table-column-status-code.tsx @@ -6,9 +6,11 @@ import { color } from 'framer-motion' export function DataTableColumnStatusCode({ value, level, + className, }: { value?: number | string level?: string + className?: string }) { // console.log('level', level, level === 'warning' ? value : 'no warning') const colors = getStatusColor(level) @@ -17,7 +19,7 @@ export function DataTableColumnStatusCode({ } return ( -
+
{children} diff --git a/apps/studio/components/interfaces/logs-new/client.tsx b/apps/studio/components/interfaces/logs-new/client.tsx index 6554d1311d8..da438eba99f 100644 --- a/apps/studio/components/interfaces/logs-new/client.tsx +++ b/apps/studio/components/interfaces/logs-new/client.tsx @@ -64,6 +64,7 @@ import { filterFields as defaultFilterFields, sheetFields } from './constants' import { useParams } from 'common' import { TraceDetailTab } from './components/trace-detail-tab' import { Tabs, TabsContent, TabsList, TabsTrigger } from 'ui/src/components/shadcn/ui/tabs' +import { FunctionLogsTab } from './components/function-logs-tab' // Debug mode flag - set to true to enable detailed logs const DEBUG_FILTER_PROCESSING = false @@ -360,92 +361,133 @@ export function Client() { {/* Use ResizablePanelGroup for the log list and details */} -
- - -
- { - if (!liveMode.timestamp) return null - if (props?.row.original.uuid !== liveMode?.row?.uuid) return null - return - }} - setColumnOrder={setColumnOrder} - setColumnVisibility={setColumnVisibility} - /> -
-
+ {/*
*/} + + + + +
+ { + if (!liveMode.timestamp) return null + if (props?.row.original.uuid !== liveMode?.row?.uuid) return null + return + }} + setColumnOrder={setColumnOrder} + setColumnVisibility={setColumnVisibility} + /> +
+
+ {/* + + hello world +
+                      {JSON.stringify(selectedRow?.original, null, 2)}
+                    
+
*/} + {selectedRow?.original?.logs && selectedRow?.original?.logs?.length > 0 && ( + <> + + +
+
+

+ Function Logs ( + {selectedRow?.original?.logs && + Array.isArray(selectedRow?.original?.logs) + ? selectedRow?.original?.logs?.length + : 0} + ) +

+
+
+ +
+
+
+ + )} +
+
- {selectedRowKey && ( - <> - - -
- - + + + + +
+ - - Log Details - Trace - - - - - + + Log Details + Trace + - - {selectedRow?.original?.has_trace ? ( - - ) : ( -
-

- No trace found for this log -

-
- )} -
- -
-
-
- - )} -
-
+ + + + + + {selectedRow?.original?.has_trace ? ( + + ) : ( +
+

+ No trace found for this log +

+
+ )} +
+ + +
+ +
+ + + )} +
+ {/*
*/} diff --git a/apps/studio/components/interfaces/logs-new/components/function-logs-tab.tsx b/apps/studio/components/interfaces/logs-new/components/function-logs-tab.tsx new file mode 100644 index 00000000000..1e0b28d9ce3 --- /dev/null +++ b/apps/studio/components/interfaces/logs-new/components/function-logs-tab.tsx @@ -0,0 +1,57 @@ +import React from 'react' + +import { DataTableColumnStatusCode } from 'components/interfaces/DataTableDemo/components/data-table/data-table-column/data-table-column-status-code' +import { HoverCardTimestamp } from 'components/interfaces/DataTableDemo/infinite/_components/hover-card-timestamp' + +interface FunctionLogEntry { + id: string + timestamp: string + event_message: string + level: string + event_type: string +} + +interface FunctionLogsTabProps { + logs?: FunctionLogEntry[] +} + +export const FunctionLogsTab: React.FC = ({ logs = [] }) => { + if (!logs || logs.length === 0) { + return ( +
+

No function logs found

+
+ ) + } + + return ( +
+
+ {logs.map((log) => { + const date = new Date(Number(log.timestamp) / 1000) + // Map the log level to our standard levels + + return ( +
+
+
+ + + + {log.event_type || 'Log'} +
+
+ {log.event_message} +
+
+
+ ) + })} +
+
+ ) +} diff --git a/apps/studio/components/interfaces/logs-new/constants.tsx b/apps/studio/components/interfaces/logs-new/constants.tsx index c83db738636..61bd1ad43d0 100644 --- a/apps/studio/components/interfaces/logs-new/constants.tsx +++ b/apps/studio/components/interfaces/logs-new/constants.tsx @@ -170,6 +170,13 @@ export const sheetFields = [ component: (props) => , className: 'flex-row items-end justify-end w-full', }, + // { + // id: 'logs', + // label: 'Logs', + // type: 'readonly', + // component: (props) =>
{JSON.stringify(props.logs, null, 2)}
, + // skeletonClassName: 'w-full', + // }, { id: 'status', label: 'Status', diff --git a/apps/studio/components/interfaces/logs-new/logs.queries.ts b/apps/studio/components/interfaces/logs-new/logs.queries.ts index 2c440293e1c..719bfcc93cc 100644 --- a/apps/studio/components/interfaces/logs-new/logs.queries.ts +++ b/apps/studio/components/interfaces/logs-new/logs.queries.ts @@ -250,7 +250,8 @@ export const getEdgeLogsQuery = () => { edge_logs_request.method as method, authorization_payload.role as api_role, COALESCE(sb.auth_user, null) as auth_user, - null as log_count + null as log_count, + null as logs from edge_logs as el cross join unnest(metadata) as edge_logs_metadata cross join unnest(edge_logs_metadata.request) as edge_logs_request @@ -284,7 +285,8 @@ export const getPostgresLogsQuery = () => { null as method, 'api_role' as api_role, null as auth_user, - null as log_count + null as log_count, + null as logs from postgres_logs as pgl cross join unnest(pgl.metadata) as pgl_metadata cross join unnest(pgl_metadata.parsed) as pgl_parsed @@ -313,7 +315,8 @@ export const getEdgeFunctionLogsQuery = () => { fel_request.method as method, authorization_payload.role as api_role, COALESCE(sb.auth_user, null) as auth_user, - function_logs_agg.function_log_count as log_count + function_logs_agg.function_log_count as log_count, + function_logs_agg.logs as logs from function_edge_logs as fel cross join unnest(metadata) as fel_metadata cross join unnest(fel_metadata.response) as fel_response @@ -326,7 +329,8 @@ export const getEdgeFunctionLogsQuery = () => { SELECT fl_metadata.execution_id, COUNT(fl.id) as function_log_count, - ANY_VALUE(fl.event_message) as last_event_message + ANY_VALUE(fl.event_message) as last_event_message, + ARRAY_AGG(STRUCT(fl.id, fl.timestamp, fl.event_message, fl_metadata.level, fl_metadata.event_type)) as logs FROM function_logs as fl CROSS JOIN UNNEST(fl.metadata) as fl_metadata WHERE fl_metadata.execution_id IS NOT NULL @@ -357,7 +361,8 @@ export const getAuthLogsQuery = () => { el_in_al_request.method as method, authorization_payload.role as api_role, COALESCE(sb.auth_user, null) as auth_user, - null as log_count + null as log_count, + null as logs from auth_logs as al cross join unnest(metadata) as al_metadata left join ( @@ -397,7 +402,8 @@ export const getSupavisorLogsQuery = () => { null as method, 'api_role' as api_role, null as auth_user, - null as log_count + null as log_count, + null as logs from supavisor_logs as svl cross join unnest(metadata) as svl_metadata ` @@ -444,7 +450,8 @@ SELECT method, api_role, auth_user, - log_count + log_count, + logs FROM unified_logs ${finalWhere} ` diff --git a/apps/studio/components/interfaces/logs-new/query-options-new.ts b/apps/studio/components/interfaces/logs-new/query-options-new.ts index 4d22ad45c2b..b67b7a80206 100644 --- a/apps/studio/components/interfaces/logs-new/query-options-new.ts +++ b/apps/studio/components/interfaces/logs-new/query-options-new.ts @@ -496,6 +496,7 @@ export const dataOptions = (search: SearchParamsType, projectRef: string) => { latency: row.latency || 0, log_count: row.log_count || null, has_trace: Math.random() < 0.5, // Simple 50% random chance + logs: row.logs || [], } }) diff --git a/apps/studio/components/interfaces/logs-new/schema.ts b/apps/studio/components/interfaces/logs-new/schema.ts index 171662f770c..c8eceb39363 100644 --- a/apps/studio/components/interfaces/logs-new/schema.ts +++ b/apps/studio/components/interfaces/logs-new/schema.ts @@ -60,6 +60,7 @@ export const columnSchema = z.object({ log_count: z.number().optional(), // used to count function logs for a given execution_id // percentile: z.number().optional(), has_trace: z.boolean().optional(), + logs: z.array(z.any()).optional(), // array of function logs }) // .merge(timingSchema) @@ -126,6 +127,7 @@ export const columnFilterSchema = z.object({ .pipe(z.coerce.date().array()) .optional(), has_trace: z.boolean().optional(), + logs: z.array(z.any()).optional(), // array of function logs }) export type ColumnFilterSchema = z.infer