feat: logflare workflow run logs

This commit is contained in:
Alaister Young committed 2024-10-17 13:59:51 +08:00
1 parent 99c147a56c
commit 4dcd552e49
9 files changed
+43 -235

No files matched your search

@@ -38,7 +38,6 @@ import {
} from 'ui'
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
import BranchStatusBadge from './BranchStatusBadge'
import WorkflowLogs from './WorkflowLogs'
interface BranchManagementSectionProps {
header: string
@@ -234,7 +233,13 @@ export const BranchRow = ({
View Repository
</Link>
</Button>
{branchingWorkflowLogsEnabled && <WorkflowLogs projectRef={branch.project_ref} />}
{branchingWorkflowLogsEnabled && (
<Button type="default" asChild>
<Link href={`/project/${branch.project_ref}/logs/workflow-run-logs`}>
View Logs
</Link>
</Button>
)}
<DropdownMenu modal={false}>
<DropdownMenuTrigger asChild>
<Button type="text" icon={<MoreVertical />} className="px-1" />
@@ -289,7 +294,11 @@ export const BranchRow = ({
</Link>
</Button>
) : (
<WorkflowLogs projectRef={branch.project_ref} />
<Button type="default" asChild>
<Link href={`/project/${branch.project_ref}/logs/workflow-run-logs`}>
View Logs
</Link>
</Button>
)}
<DropdownMenu modal={false}>
@@ -1,141 +0,0 @@
import { useState } from 'react'
import AlertError from 'components/ui/AlertError'
import { useWorkflowRunLogsQuery } from 'data/workflow-runs/workflow-run-logs-query'
import { useWorkflowRunsQuery } from 'data/workflow-runs/workflow-runs-query'
import dayjs from 'dayjs'
import { ArrowLeft, ArrowRight } from 'lucide-react'
import {
Button,
cn,
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogSection,
DialogSectionSeparator,
DialogTitle,
DialogTrigger,
} from 'ui'
import { GenericSkeletonLoader } from 'ui-patterns'
import BranchStatusBadge from './BranchStatusBadge'
interface WorkflowLogsProps {
projectRef: string
}
const WorkflowLogs = ({ projectRef }: WorkflowLogsProps) => {
const [isOpen, setIsOpen] = useState(false)
const {
data: workflowRuns,
isSuccess: isWorkflowRunsSuccess,
isLoading: isWorkflowRunsLoading,
isError: isWorkflowRunsError,
error: workflowRunsError,
} = useWorkflowRunsQuery(
{
projectRef,
},
{
enabled: isOpen,
}
)
const [selectedWorkflowRunId, setSelectedWorkflowRunId] = useState<string | undefined>(undefined)
const {
data: workflowRunLogs,
isSuccess: isWorkflowRunLogsSuccess,
isLoading: isWorkflowRunLogsLoading,
isError: isWorkflowRunLogsError,
error: workflowRunLogsError,
} = useWorkflowRunLogsQuery(
{
workflowRunId: selectedWorkflowRunId,
},
{
enabled: isOpen && selectedWorkflowRunId !== undefined,
}
)
return (
<Dialog open={isOpen} onOpenChange={setIsOpen}>
<DialogTrigger asChild>
<Button type="default">View Logs</Button>
</DialogTrigger>
<DialogContent size="xlarge">
<DialogHeader>
<DialogTitle>Workflow Logs</DialogTitle>
<DialogDescription>Select a workflow run to view logs</DialogDescription>
</DialogHeader>
<DialogSectionSeparator />
<DialogSection className={cn('px-0', isWorkflowRunLogsSuccess ? 'py-0 pt-2' : '!py-0')}>
{selectedWorkflowRunId === undefined ? (
<>
{isWorkflowRunsLoading && <GenericSkeletonLoader className="py-2 px-5" />}
{isWorkflowRunsError && <AlertError error={workflowRunsError} />}
{isWorkflowRunsSuccess &&
(workflowRuns.length > 0 ? (
<ul className="divide-y">
{workflowRuns.map((workflowRun) => (
<li key={workflowRun.id} className="py-3 px-5">
<button
type="button"
onClick={() => setSelectedWorkflowRunId(workflowRun.id)}
className="flex items-center gap-2 w-full justify-between"
>
<div className="flex items-center gap-4">
<BranchStatusBadge status={workflowRun.status} />
<span className="text-sm">
{dayjs(workflowRun.created_at).format('DD MMM, YYYY HH:mm')}
</span>
</div>
<ArrowRight size={16} />
</button>
</li>
))}
</ul>
) : (
<p className="text-center text-sm text-foreground-light py-4">
No workflow runs found.
</p>
))}
</>
) : (
<div className="flex flex-col gap-2 py-2">
<Button
onClick={() => setSelectedWorkflowRunId(undefined)}
type="text"
icon={<ArrowLeft />}
className="self-start mx-5"
>
Back to workflow runs
</Button>
{isWorkflowRunLogsLoading && <GenericSkeletonLoader className="py-2 px-5" />}
{isWorkflowRunLogsError && (
<AlertError
className="rounded-none"
subject="Failed to retrieve workflow logs"
error={workflowRunLogsError}
/>
)}
{isWorkflowRunLogsSuccess && (
<pre className="whitespace-pre max-h-[500px] overflow-scroll px-5 pb-5">
{workflowRunLogs}
</pre>
)}
</div>
)}
</DialogSection>
</DialogContent>
</Dialog>
)
}
export default WorkflowLogs
@@ -363,6 +363,7 @@ export enum LogsTableName {
POSTGREST = 'postgrest_logs',
SUPAVISOR = 'supavisor_logs',
WAREHOUSE = 'warehouse_logs',
WORKFLOW_RUN = 'workflow_run_logs',
}
export const LOGS_TABLES = {
@@ -376,6 +377,7 @@ export const LOGS_TABLES = {
postgrest: LogsTableName.POSTGREST,
supavisor: LogsTableName.SUPAVISOR,
warehouse: LogsTableName.WAREHOUSE,
workflow_run: LogsTableName.WORKFLOW_RUN,
}
export const LOGS_SOURCE_DESCRIPTION = {
@@ -88,6 +88,7 @@ export type QueryType =
| 'supavisor'
| 'postgrest'
| 'warehouse'
| 'workflow_run'
export type Mode = 'simple' | 'custom'
@@ -163,6 +163,12 @@ export function LogsSidebarMenuV2() {
url: `/project/${ref}/logs/edge-functions-logs`,
items: [],
},
{
name: 'Workflow Run Logs',
key: 'workflow-run-logs',
url: `/project/${ref}/logs/workflow-run-logs`,
items: [],
},
]
const filteredLogs = BASE_COLLECTIONS.filter((collection) => {
-3
View File
@@ -1,3 +0,0 @@
export const workflowRunKeys = {
list: (projectRef: string | undefined) => ['projects', projectRef, 'workflow-runs'] as const,
}
@@ -1,51 +0,0 @@
import { useQuery, UseQueryOptions } from '@tanstack/react-query'
import { get, handleError } from 'data/fetchers'
import type { ResponseError } from 'types'
import { workflowRunKeys } from './keys'
export type WorkflowRunLogsVariables = {
workflowRunId?: string
}
export async function getWorkflowRunLogs(
{ workflowRunId }: WorkflowRunLogsVariables,
signal?: AbortSignal
) {
if (!workflowRunId) throw new Error('workflowRunId is required')
const { data, error } = await get(`/platform/workflow-runs/{workflow_run_id}/logs`, {
params: {
path: {
workflow_run_id: workflowRunId,
},
},
parseAs: 'text',
headers: {
Accept: 'text/plain',
},
signal,
})
if (error) handleError(error)
return data
.split('\n')
.flatMap((line) => line.split('\r'))
.join('\n')
.trim()
}
export type WorkflowRunLogsData = Awaited<ReturnType<typeof getWorkflowRunLogs>>
export type WorkflowRunLogsError = ResponseError
export const useWorkflowRunLogsQuery = <TData = WorkflowRunLogsData>(
{ workflowRunId }: WorkflowRunLogsVariables,
{
enabled = true,
...options
}: UseQueryOptions<WorkflowRunLogsData, WorkflowRunLogsError, TData> = {}
) =>
useQuery<WorkflowRunLogsData, WorkflowRunLogsError, TData>(
workflowRunKeys.list(workflowRunId),
({ signal }) => getWorkflowRunLogs({ workflowRunId }, signal),
{ enabled: enabled && typeof workflowRunId !== 'undefined', staleTime: 0, ...options }
)
@@ -1,37 +0,0 @@
import { useQuery, UseQueryOptions } from '@tanstack/react-query'
import { get, handleError } from 'data/fetchers'
import type { ResponseError } from 'types'
import { workflowRunKeys } from './keys'
export type WorkflowRunsVariables = {
projectRef?: string
}
export async function getWorkflowRuns({ projectRef }: WorkflowRunsVariables, signal?: AbortSignal) {
if (!projectRef) throw new Error('projectRef is required')
const { data, error } = await get(`/platform/workflow-runs`, {
params: {
query: {
project_ref: projectRef,
},
},
signal,
})
if (error) handleError(error)
return data
}
export type WorkflowRunsData = Awaited<ReturnType<typeof getWorkflowRuns>>
export type WorkflowRunsError = ResponseError
export const useWorkflowRunsQuery = <TData = WorkflowRunsData>(
{ projectRef }: WorkflowRunsVariables,
{ enabled = true, ...options }: UseQueryOptions<WorkflowRunsData, WorkflowRunsError, TData> = {}
) =>
useQuery<WorkflowRunsData, WorkflowRunsError, TData>(
workflowRunKeys.list(projectRef),
({ signal }) => getWorkflowRuns({ projectRef }, signal),
{ enabled: enabled && typeof projectRef !== 'undefined', staleTime: 0, ...options }
)
@@ -0,0 +1,22 @@
import { useParams } from 'common'
import { LogsTableName } from 'components/interfaces/Settings/Logs/Logs.constants'
import LogsPreviewer from 'components/interfaces/Settings/Logs/LogsPreviewer'
import LogsLayout from 'components/layouts/LogsLayout/LogsLayout'
import type { NextPageWithLayout } from 'types'
export const LogPage: NextPageWithLayout = () => {
const { ref } = useParams()
return (
<LogsPreviewer
projectRef={ref as string}
condensedLayout={true}
tableName={LogsTableName.WORKFLOW_RUN}
queryType={'workflow_run'}
/>
)
}
LogPage.getLayout = (page) => <LogsLayout title="Workflow Run Logs">{page}</LogsLayout>
export default LogPage