// CIL[BEGIN chunk_id="TraceTree_module_header_1785000131" type="module_header" identity="module_header" parent="none"] // CIL[DOC-MACRO purpose="Fetch and render the spans for a single trace"] // CIL[DOC-RESPONSIBILITY responsibility="Display a trace tree as a flat list of spans with timing and status"] // CIL[DOC-CONTEXT module="workbench.components.TraceTree" layer="frontend-component" related_files="[\"custom/apps/workbench/functions/custom_workbench-observability.ts\",\"custom/apps/workbench/pages/observe/PerformancePage.tsx\"]"] // CIL[DOC-CONTRACT inputs="{ traceId }" outputs="JSX span table" errors="fetch errors rendered inline" side_effects="calls /api/custom_workbench-observability?action=trace"] // CIL[DOC-MICRO logic_summary="Fetches trace tree on mount/trace change, renders a sortable table of spans, and shows duration and status" notable_decisions=[]] // CIL[DOC-CONFIDENCE pass="first" score="0.90" flags="none"] // CIL[DOC-STABILITY stability="experimental"] // CIL[DOC-AUDIENCE audience="developer"] import { useEffect, useState } from 'react' import { apiFetch } from '@core/lib/api' import { formatDateTime } from '@core/lib/utils' import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@core/components/ui/table' import { LoadingSpinner } from '@core/components/ui/LoadingSpinner' // CIL[END chunk_id="TraceTree_module_header_1785000131"] // CIL[BEGIN chunk_id="TraceTree_tracetree_1785000131" type="function" identity="TraceTree" parent="TraceTree_module_header_1785000131"] // CIL[DOC-MACRO purpose="Trace tree viewer"] // CIL[DOC-RESPONSIBILITY responsibility="Load and display spans for a trace"] // CIL[DOC-CONTEXT module="workbench.components.TraceTree" layer="frontend-component" related_files="[\"custom/apps/workbench/pages/observe/UptimePage.tsx\"]"] // CIL[DOC-CONTRACT inputs="traceId" outputs="JSX" errors="fetch errors rendered inline" side_effects="network request"] // CIL[DOC-MICRO logic_summary="Calls the trace tree RPC, formats durations, and renders a status badge per span" notable_decisions=[]] // CIL[DOC-CONFIDENCE pass="first" score="0.90" flags="none"] // CIL[DOC-STABILITY stability="experimental"] // CIL[DOC-AUDIENCE audience="developer"] interface SpanRow { span_id: string parent_span_id?: string span_name: string kind?: string level?: string source?: string started_at: string ended_at?: string duration_ms?: number status: string outcome?: string message?: string } export function TraceTree({ traceId }: { traceId: string }) { const [spans, setSpans] = useState([]) const [loading, setLoading] = useState(false) const [error, setError] = useState(null) useEffect(() => { if (!traceId) return setLoading(true) setError(null) apiFetch(`/api/custom_workbench-observability?action=trace&trace_id=${encodeURIComponent(traceId)}`) .then((r) => r.json()) .then((json) => { if (json.error) throw new Error(json.error) setSpans((json.data || []).map((s: any) => ({ ...s, duration_ms: Number(s.duration_ms || 0), }))) }) .catch((e) => setError(e.message || 'Failed to load trace tree')) .finally(() => setLoading(false)) }, [traceId]) if (loading) { return (
) } if (error) { return

{error}

} if (spans.length === 0) { return

No spans for this trace.

} return (
Depth Span Kind Started Duration Status {spans.map((span, idx) => ( {idx + 1} {span.span_name || '—'} {span.kind || '—'} {formatDateTime(span.started_at)} {(span.duration_ms || 0).toFixed(0)} ms {span.status === 'error' || span.outcome === 'failure' ? 'Error' : 'OK'} ))}
) } // CIL[END chunk_id="TraceTree_tracetree_1785000131"]