diff --git a/apps/studio/components/interfaces/Settings/Logs/LogColumnRenderers/UnifiedLogsColumnRender.tsx b/apps/studio/components/interfaces/Settings/Logs/LogColumnRenderers/UnifiedLogsColumnRender.tsx index 0bb96d769dc..bcee5fd1af2 100644 --- a/apps/studio/components/interfaces/Settings/Logs/LogColumnRenderers/UnifiedLogsColumnRender.tsx +++ b/apps/studio/components/interfaces/Settings/Logs/LogColumnRenderers/UnifiedLogsColumnRender.tsx @@ -1,4 +1,5 @@ import { LogData } from 'components/interfaces/Settings/Logs/Logs.types' +import { Badge } from 'ui' import { TimestampInfo } from 'ui-patterns/TimestampInfo' interface UnifiedLogsColumnRenderProps { @@ -9,6 +10,7 @@ interface UnifiedLogsColumnRenderProps { method?: string code?: string level?: string + api_role?: string } } @@ -45,6 +47,9 @@ export const UnifiedLogsColumnRender = ({ log }: UnifiedLogsColumnRenderProps) = return (
+ {log.method && {log.method}} + {log.auth_user && User: {log.auth_user}} + {log.api_role && {log.api_role}} {logLevel && ( )} - {log_type} + {log_type} + {/* {log_type} */} {logCode && {logCode}}
{logPath &&
{logPath}
} diff --git a/apps/studio/components/interfaces/Settings/Logs/Logs.utils.ts b/apps/studio/components/interfaces/Settings/Logs/Logs.utils.ts index 42e0a05ba48..ada62da7f70 100644 --- a/apps/studio/components/interfaces/Settings/Logs/Logs.utils.ts +++ b/apps/studio/components/interfaces/Settings/Logs/Logs.utils.ts @@ -206,39 +206,9 @@ limit ${limit} return `select id, ${table}.timestamp, event_message from ${table} ${joins} ${where} ${orderBy} limit ${limit}` case 'unified_logs': - if (IS_PLATFORM === false) { - return ` -select - id, - el.timestamp as timestamp, - 'edge' as log_type, - edge_logs_response.status_code as code, - 'undefined' as level, - edge_logs_request.path as path, - 'undefined' as event_message -from edge_logs as el -cross join unnest(el.metadata) as edge_logs_metadata -cross join unnest(edge_logs_metadata.request) as edge_logs_request -cross join unnest(edge_logs_metadata.response) as edge_logs_response - -union all - -select - id, - pgl.timestamp as timestamp, - 'postgres' as log_type, - 'undefined' as code, - 'undefined' as level, - 'undefined' as path, - 'undefined' as event_message -from postgres_logs as pgl - -${where} -${orderBy} -limit ${limit} -` - } return ` + +-- edge logs select id, el.timestamp as timestamp, @@ -246,14 +216,22 @@ select CAST(edge_logs_response.status_code AS STRING) as code, 'undefined' as level, edge_logs_request.path as path, - 'undefined' as event_message + 'undefined' as event_message, + edge_logs_request.method as method, + authorization_payload.role as api_role, + COALESCE(sb.auth_user, null) as auth_user, from edge_logs as el cross join unnest(metadata) as edge_logs_metadata cross join unnest(edge_logs_metadata.request) as edge_logs_request cross join unnest(edge_logs_metadata.response) as edge_logs_response +left join unnest(edge_logs_request.sb) as sb +left join unnest(sb.jwt) as jwt +left join unnest(jwt.authorization) as auth +left join unnest(auth.payload) as authorization_payload union all +-- postgres logs select id, pgl.timestamp as timestamp, @@ -261,11 +239,143 @@ select pgl_parsed.sql_state_code as code, 'undefined' as level, null as path, - 'undefined' as event_message + 'undefined' as event_message, + 'undefined' as method, + 'api_role' as api_role, + null as auth_user, from postgres_logs as pgl cross join unnest(pgl.metadata) as pgl_metadata cross join unnest(pgl_metadata.parsed) as pgl_parsed +union all + +-- function event logs +select + id, + fl.timestamp as timestamp, + 'function logs' as log_type, + 'undefined' as code, + fl_metadata.level as level, + null as path, + fl.event_message as event_message, + 'undefined' as method, + 'api_role' as api_role, + null as auth_user, + -- fl_metadata.function_id as function_id, + -- fl_metadata.event_type as event_type, +from function_logs as fl +cross join unnest(metadata) as fl_metadata + +union all + +-- function edge logs +select + id, + fel.timestamp as timestamp, + 'edge function' as log_type, + CAST(fel_response.status_code as STRING) as code, + 'undefined' as level, + fel_request.url as path, + 'undefined' as event_message, + 'undefined' as method, + 'api_role' as api_role, + null as auth_user, + -- fel.event_message as event_message, + -- fel_request.path as path, + -- fel_metadata.function_id as function_id, + -- fel_metadata.execution_time_ms as execution_time_ms, + -- fel_metadata.deployment_id as deployment_id, + -- fel_metadata.version as version +from function_edge_logs as fel +cross join unnest(metadata) as fel_metadata +cross join unnest(fel_metadata.response) as fel_response +cross join unnest(fel_metadata.request) as fel_request + +union all + +-- OLDER QUERY FOR AUTH LOGS +-- +-- auth logs +-- select +-- id, +-- al.timestamp as timestamp, +-- 'auth' as log_type, +-- REGEXP_EXTRACT(al_metadata.msg, r'^(\d+):') as code, +-- -- 'undefined' as code, +-- al_metadata.level as level, +-- al_metadata.path as path, +-- 'undefined' as event_message, + -- al_metadata.status as status, + -- 'undefined' as method, +-- from auth_logs as al +-- cross join unnest(metadata) as al_metadata + +-- NEW QUERY FOR AUTH LOGS +-- THIS USES EDGE LOGS AS WELL TO GET STATUS AND PATH +-- +select + al.id as id, + el_in_al.timestamp as timestamp, + el_in_al_request.path as path, + 'auth' as log_type, + CAST(el_in_al_response.status_code as STRING) as code, + 'undefined' as level, + null as path, + el_in_al_request.method as method, + authorization_payload.role as api_role, + COALESCE(sb.auth_user, null) as auth_user, +from auth_logs as al +cross join unnest(metadata) as al_metadata left join ( + edge_logs as el_in_al + cross join unnest (metadata) as el_in_al_metadata + cross join unnest (el_in_al_metadata. response) as el_in_al_response + cross join unnest (el_in_al_response.headers) as el_in_al_response_headers + cross join unnest (el_in_al_metadata. request) as el_in_al_request + left join unnest(el_in_al_request.sb) as sb + left join unnest(sb.jwt) as jwt + left join unnest(jwt.authorization) as auth + left join unnest(auth.payload) as authorization_payload +) +on al_metadata. request_id = el_in_al_response_headers.cf_ray +-- filter out auth logs without a request_id +-- it looks like most auth logs are logged twice, one of them has a request_id +where al_metadata.request_id is not null + +union all + +-- supavisor logs +select + id, + svl.timestamp as timestamp, + 'supavisor' as log_type, + 'undefined' as code, + svl_metadata.level as level, + null as path, + 'undefined' as event_message, + 'undefined' as method, + 'api_role' as api_role, + null as auth_user, +from supavisor_logs as svl +cross join unnest(metadata) as svl_metadata + +union all + +-- pg_upgrade logs +select + id, + pgul.timestamp as timestamp, + 'postgres upgrade' as log_type, + 'undefined' as code, + 'undefined' as level, + -- pgul.level as level, + null as path, + 'undefined' as event_message, + 'undefined' as method, + 'api_role' as api_role, + null as auth_user, +from pg_upgrade_logs as pgul + + ${where} ${orderBy} limit ${limit} @@ -405,6 +515,109 @@ export const genChartQuery = ( let joins = genCrossJoinUnnests(table) + // For unified logs, we need a special query to combine data from multiple sources + if (table === LogsTableName.UNIFIED) { + return ` +WITH unified_data AS ( + -- edge logs + SELECT + el.timestamp as timestamp, + CAST(edge_logs_response.status_code AS INT64) as status_code, + 'undefined' as level + FROM edge_logs as el + CROSS JOIN UNNEST(metadata) as edge_logs_metadata + CROSS JOIN UNNEST(edge_logs_metadata.request) as edge_logs_request + CROSS JOIN UNNEST(edge_logs_metadata.response) as edge_logs_response + WHERE el.timestamp > '${startOffset.toISOString()}' + + UNION ALL + + -- postgres logs + SELECT + pgl.timestamp as timestamp, + CASE + WHEN pgl_parsed.error_severity IN ('ERROR', 'FATAL', 'PANIC') THEN 500 + WHEN pgl_parsed.error_severity IN ('WARNING') THEN 400 + ELSE 200 + END as status_code, + pgl_parsed.error_severity as level + FROM postgres_logs as pgl + CROSS JOIN UNNEST(pgl.metadata) as pgl_metadata + CROSS JOIN UNNEST(pgl_metadata.parsed) as pgl_parsed + WHERE pgl.timestamp > '${startOffset.toISOString()}' + + UNION ALL + + -- function logs + SELECT + fl.timestamp as timestamp, + CASE + WHEN fl_metadata.level IN ('error', 'fatal') THEN 500 + WHEN fl_metadata.level IN ('warning') THEN 400 + ELSE 200 + END as status_code, + fl_metadata.level as level + FROM function_logs as fl + CROSS JOIN UNNEST(metadata) as fl_metadata + WHERE fl.timestamp > '${startOffset.toISOString()}' + + UNION ALL + + -- function edge logs + SELECT + fel.timestamp as timestamp, + CAST(fel_response.status_code AS INT64) as status_code, + 'undefined' as level + FROM function_edge_logs as fel + CROSS JOIN UNNEST(metadata) as fel_metadata + CROSS JOIN UNNEST(fel_metadata.response) as fel_response + WHERE fel.timestamp > '${startOffset.toISOString()}' + + UNION ALL + + -- auth logs + SELECT + al.timestamp as timestamp, + CASE + WHEN al_metadata.level = 'error' THEN 500 + WHEN al_metadata.level = 'warning' THEN 400 + ELSE 200 + END as status_code, + al_metadata.level as level + FROM auth_logs as al + CROSS JOIN UNNEST(metadata) as al_metadata + WHERE al.timestamp > '${startOffset.toISOString()}' + + UNION ALL + + -- supavisor logs + SELECT + svl.timestamp as timestamp, + CASE + WHEN svl_metadata.level = 'error' THEN 500 + WHEN svl_metadata.level = 'warning' THEN 400 + ELSE 200 + END as status_code, + svl_metadata.level as level + FROM supavisor_logs as svl + CROSS JOIN UNNEST(metadata) as svl_metadata + WHERE svl.timestamp > '${startOffset.toISOString()}' +) + +SELECT + timestamp_trunc(timestamp, ${trunc}) as timestamp, + count(CASE WHEN NOT (status_code >= 500 OR level IN ('error', 'fatal')) AND NOT (status_code >= 400 AND status_code < 500 OR level = 'warning') THEN 1 END) as ok_count, + count(CASE WHEN status_code >= 500 OR level IN ('error', 'fatal') THEN 1 END) as error_count, + count(CASE WHEN status_code >= 400 AND status_code < 500 OR level = 'warning' THEN 1 END) as warning_count +FROM + unified_data +GROUP BY + timestamp +ORDER BY + timestamp ASC +` + } + const q = ` SELECT -- log-event-chart @@ -678,6 +891,8 @@ function getErrorCondition(table: LogsTableName): string { return "metadata.level IN ('error', 'fatal')" case 'pg_cron_logs': return "parsed.error_severity IN ('ERROR', 'FATAL', 'PANIC')" + case 'unified_logs': + return "(code >= '500' OR level = 'error' OR level = 'fatal')" default: return 'false' } @@ -695,6 +910,8 @@ function getWarningCondition(table: LogsTableName): string { return 'response.status_code >= 400 AND response.status_code < 500' case 'function_logs': return "metadata.level IN ('warning')" + case 'unified_logs': + return "(code >= '400' AND code < '500') OR level = 'warning'" default: return 'false' }