mirror of
https://github.com/supabase/supabase.git
synced 2026-09-08 19:08:44 +08:00
## Problem
The Edge Functions report (`observability/edge-functions`) only queries
BigQuery. As part of the broader Reports→ClickHouse OTEL migration
(DEBUG-73), we need each report migrated one at a time behind the
`otelReports` flag.
## Fix
Adds a ClickHouse OTEL SQL variant (`METRIC_SQL_OTEL`) for the 4 Edge
Functions metrics (TotalInvocations, ExecutionStatusCodes,
InvocationsByRegion, ExecutionTime), querying the unified `logs` table
filtered to `source = 'function_edge_logs'`, with fields read from
`log_attributes` (`function_id`, `response.status_code`,
`response.headers.x_sb_edge_region`, `execution_time_ms`) — the same
mapping already used by `edge-functions-last-hour-stats-query.ts`.
`edgeFunctionReports()` now takes a `useOtel` flag that picks between
the BQ and OTEL query sets and forwards it to `fetchLogs`. The page
wires this up via `useFlag('otelReports')`, matching the pattern used
for the Auth report. No behavior change while the flag is off — report
still fetches from BigQuery.
Also removed two pieces of dead code spotted in `report.utils.ts` while
touching it: the unused `useEdgeFnIdToName` hook and a
`STATUS_CODE_COLORS` map that was an exact duplicate of
`REPORT_STATUS_CODE_COLORS` (the one actually imported elsewhere).
This PR is standalone — no dependency on the in-flight Auth report OTEL
stack.
## How to test
- `pnpm vitest run data/reports/v2/edge-functions.config.otel.test.ts` —
9 new tests covering the OTEL SQL shape (single logs table,
unix-microsecond timestamp bucketing, field mapping, filters).
- `pnpm vitest run data/reports` and `pnpm vitest run
data/edge-functions components/interfaces/Reports` — existing suites (46
+ 54 tests) still pass, confirming no regression to the BQ path.
- Manually: with `otelReports` flag enabled, visit a project's Edge
Functions observability report and confirm charts render from the
ClickHouse endpoint.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added OpenTelemetry support for Edge Functions observability metrics,
including invocations, status codes, regional activity, and execution
time.
* Reports can now dynamically use either the standard or OpenTelemetry
logs source.
* **Bug Fixes**
* Improved filtering and timestamp handling for OpenTelemetry-based Edge
Functions metrics.
* Added coverage for status, execution time, function, and region
filters.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
85 lines
2.6 KiB
TypeScript
85 lines
2.6 KiB
TypeScript
import { type ComparisonOperator } from '@/components/interfaces/Reports/v2/ReportsNumericFilter'
|
|
import { AnalyticsInterval } from '@/data/analytics/constants'
|
|
import { executeAnalyticsSql } from '@/data/logs/execute-analytics-sql'
|
|
import { logsAllEndpointUrl } from '@/data/logs/logs-endpoint'
|
|
import { safeSql, type SafeLogSqlFragment } from '@/data/logs/safe-analytics-sql'
|
|
|
|
export type Granularity = 'minute' | 'hour' | 'day'
|
|
|
|
/**
|
|
* Pre-branded SQL fragments for the closed set of granularity tokens that
|
|
* `analyticsIntervalToGranularity` may return. Use to splice a granularity into
|
|
* a `safeSql` template without re-validating at the call site.
|
|
*/
|
|
export const SAFE_GRANULARITY_SQL: Record<Granularity, SafeLogSqlFragment> = {
|
|
minute: safeSql`minute`,
|
|
hour: safeSql`hour`,
|
|
day: safeSql`day`,
|
|
}
|
|
|
|
/**
|
|
* Pre-branded SQL fragments for the closed set of numeric comparison operators
|
|
* accepted by `ReportsNumericFilter`. Use to splice an operator into a
|
|
* `safeSql` template without re-validating at the call site.
|
|
*/
|
|
export const SAFE_COMPARISON_OPERATOR_SQL: Record<ComparisonOperator, SafeLogSqlFragment> = {
|
|
'=': safeSql`=`,
|
|
'>=': safeSql`>=`,
|
|
'<=': safeSql`<=`,
|
|
'>': safeSql`>`,
|
|
'<': safeSql`<`,
|
|
'!=': safeSql`!=`,
|
|
}
|
|
|
|
export function analyticsIntervalToGranularity(interval: AnalyticsInterval): Granularity {
|
|
switch (interval) {
|
|
case '1m':
|
|
return 'minute'
|
|
case '5m':
|
|
return 'minute'
|
|
case '10m':
|
|
return 'minute'
|
|
case '30m':
|
|
return 'minute'
|
|
case '1h':
|
|
return 'hour'
|
|
case '1d':
|
|
return 'day'
|
|
default:
|
|
return 'hour'
|
|
}
|
|
}
|
|
|
|
export const REPORT_STATUS_CODE_COLORS: { [key: string]: { light: string; dark: string } } = {
|
|
'400': { light: '#FFD54F', dark: '#FFF176' },
|
|
'401': { light: '#FF8A65', dark: '#FFAB91' },
|
|
'403': { light: '#FFB74D', dark: '#FFCC80' },
|
|
'404': { light: '#90A4AE', dark: '#B0BEC5' },
|
|
'409': { light: '#BA68C8', dark: '#CE93D8' },
|
|
'410': { light: '#A1887F', dark: '#BCAAA4' },
|
|
'422': { light: '#FF9800', dark: '#FFB74D' },
|
|
'429': { light: '#E65100', dark: '#F57C00' },
|
|
'500': { light: '#B71C1C', dark: '#D32F2F' },
|
|
'502': { light: '#9575CD', dark: '#B39DDB' },
|
|
'503': { light: '#0097A7', dark: '#4DD0E1' },
|
|
'504': { light: '#C0CA33', dark: '#D4E157' },
|
|
default: { light: '#757575', dark: '#9E9E9E' },
|
|
}
|
|
|
|
export async function fetchLogs(
|
|
projectRef: string,
|
|
sql: SafeLogSqlFragment,
|
|
startDate: string,
|
|
endDate: string,
|
|
useOtel = false
|
|
) {
|
|
return await executeAnalyticsSql({
|
|
projectRef,
|
|
endpoint: logsAllEndpointUrl(useOtel),
|
|
sql,
|
|
iso_timestamp_start: startDate,
|
|
iso_timestamp_end: endDate,
|
|
method: 'get',
|
|
})
|
|
}
|