Files
Jordi Enric be030229bf feat(studio): add workers to unified logs FE-4281 (#49682)
## Problem

Unified Logs does not expose Workers logs, so users cannot search
Workers ingress, runtime, or build events alongside other services.

## Fix

Add a Workers log type that classifies all three Workers OTEL streams.
Gate the option and any persisted Workers filters with the existing
Workers feature flag.

## How to test

- Enable the Workers feature flag and open Unified Logs.
- Select Workers from the Log Type filter.
- Expected result: Unified Logs shows ingress, runtime, and build events
with the Workers icon.
- Disable the Workers feature flag and load a URL containing
`log_type:eq:workers`.
- Expected result: the Workers option and filter are removed, and
Workers logs are not queried.
- Run `./node_modules/.bin/vitest --run
components/interfaces/UnifiedLogs/UnifiedLogs.queries.test.ts
components/interfaces/UnifiedLogs/UnifiedLogs.utils.test.ts
data/workers/worker-logs-query.test.ts` from `apps/studio`.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
  * Added Workers as a selectable log type in Unified Logs.
* Unified Logs now combines worker ingress, guest, and API streams under
the Workers category.
  * Added a dedicated Workers icon and worker log filtering.

* **Improvements**
  * Worker filters and URL parameters respect feature availability.
* Worker details show relevant metadata while omitting unavailable HTTP
fields.
  * Improved handling of worker log levels, statuses, and raw data.
  * Added stronger validation for unified log data.

* **Tests**
* Added coverage for worker routing, filtering, feature visibility,
parsing, and metadata redaction.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-07 15:53:36 +02:00

77 lines
2.7 KiB
TypeScript

import { useMutation } from '@tanstack/react-query'
import { useFlag } from 'common'
import { toast } from 'sonner'
import { logsAllEndpointUrl, pickLogsQueryBuilder } from './logs-endpoint'
import { getUnifiedLogsISOStartEnd } from './unified-logs-infinite-query'
import { mapUnifiedLogRow, parseUnifiedLogsQueryRows } from './unified-logs.utils'
import { getUnifiedLogsQuery } from '@/components/interfaces/UnifiedLogs/UnifiedLogs.queries'
import { getUnifiedLogsQuery as getUnifiedLogsQueryBq } from '@/components/interfaces/UnifiedLogs/UnifiedLogs.queries.bq'
import { QuerySearchParamsType } from '@/components/interfaces/UnifiedLogs/UnifiedLogs.types'
import { handleError, post } from '@/data/fetchers'
import type { ResponseError, UseCustomMutationOptions } from '@/types'
export type getUnifiedLogsVariables = {
projectRef: string
search: QuerySearchParamsType
limit: number
hoursAgo?: number
useOtel?: boolean
}
// [Joshen] Mainly for retrieving logs on demand for downloading
export async function retrieveUnifiedLogs({
projectRef,
search,
limit,
hoursAgo,
useOtel = false,
}: getUnifiedLogsVariables) {
if (typeof projectRef === 'undefined')
throw new Error('projectRef is required for retrieveUnifiedLogs')
const { isoTimestampStart, isoTimestampEnd } = getUnifiedLogsISOStartEnd(search, hoursAgo)
const buildQuery = pickLogsQueryBuilder(useOtel, getUnifiedLogsQuery, getUnifiedLogsQueryBq)
const sql = `${buildQuery(search)} ORDER BY timestamp DESC, id DESC LIMIT ${limit}`
const endpoint = logsAllEndpointUrl(useOtel)
const { data, error } = await post(endpoint, {
params: { path: { ref: projectRef } },
body: { iso_timestamp_start: isoTimestampStart, iso_timestamp_end: isoTimestampEnd, sql },
})
if (error) handleError(error)
const resultData = parseUnifiedLogsQueryRows(data?.result)
const result = resultData.map(mapUnifiedLogRow)
return result
}
type LogDrainCreateData = Awaited<ReturnType<typeof retrieveUnifiedLogs>>
export const useGetUnifiedLogsMutation = ({
onSuccess,
onError,
...options
}: Omit<
UseCustomMutationOptions<LogDrainCreateData, ResponseError, getUnifiedLogsVariables>,
'mutationFn'
> = {}) => {
const useOtel = useFlag('otelUnifiedLogs')
return useMutation<LogDrainCreateData, ResponseError, getUnifiedLogsVariables>({
mutationFn: (vars) => retrieveUnifiedLogs({ ...vars, useOtel: vars.useOtel ?? useOtel }),
async onSuccess(data, variables, context) {
await onSuccess?.(data, variables, context)
},
async onError(data, variables, context) {
if (onError === undefined) {
toast.error(`Failed to retrieve logs: ${data.message}`)
} else {
onError(data, variables, context)
}
},
...options,
})
}