Skip to content

Commit e102b6c

Browse files
Vikhyath MondretiVikhyath Mondreti
authored andcommitted
improve logging ui
1 parent 50595c5 commit e102b6c

File tree

9 files changed

+116
-230
lines changed

9 files changed

+116
-230
lines changed

apps/sim/app/workspace/[workspaceId]/logs/components/frozen-canvas/frozen-canvas.tsx

Lines changed: 79 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,15 @@
33
import { useEffect, useState } from 'react'
44
import {
55
AlertCircle,
6+
ChevronDown,
67
ChevronLeft,
78
ChevronRight,
9+
ChevronUp,
810
Clock,
911
DollarSign,
1012
Hash,
1113
Loader2,
14+
Maximize2,
1215
X,
1316
Zap,
1417
} from 'lucide-react'
@@ -21,6 +24,69 @@ import type { WorkflowState } from '@/stores/workflows/workflow/types'
2124

2225
const logger = createLogger('FrozenCanvas')
2326

27+
function ExpandableDataSection({ title, data }: { title: string; data: any }) {
28+
const [isExpanded, setIsExpanded] = useState(false)
29+
const [isModalOpen, setIsModalOpen] = useState(false)
30+
31+
const jsonString = JSON.stringify(data, null, 2)
32+
const isLargeData = jsonString.length > 500 || jsonString.split('\n').length > 10
33+
34+
return (
35+
<>
36+
<div>
37+
<div className='mb-2 flex items-center justify-between'>
38+
<h4 className='font-medium text-foreground text-sm'>{title}</h4>
39+
<div className='flex items-center gap-1'>
40+
{isLargeData && (
41+
<button
42+
onClick={() => setIsModalOpen(true)}
43+
className='rounded p-1 text-muted-foreground hover:bg-muted hover:text-foreground'
44+
title="Expand in modal"
45+
>
46+
<Maximize2 className='h-3 w-3' />
47+
</button>
48+
)}
49+
<button
50+
onClick={() => setIsExpanded(!isExpanded)}
51+
className='rounded p-1 text-muted-foreground hover:bg-muted hover:text-foreground'
52+
>
53+
{isExpanded ? <ChevronUp className='h-3 w-3' /> : <ChevronDown className='h-3 w-3' />}
54+
</button>
55+
</div>
56+
</div>
57+
<div
58+
className={cn(
59+
'overflow-y-auto rounded bg-muted p-3 font-mono text-xs transition-all duration-200',
60+
isExpanded ? 'max-h-96' : 'max-h-32'
61+
)}
62+
>
63+
<pre className='whitespace-pre-wrap break-words text-foreground'>{jsonString}</pre>
64+
</div>
65+
</div>
66+
67+
{/* Modal for large data */}
68+
{isModalOpen && (
69+
<div className='fixed inset-0 z-[200] flex items-center justify-center bg-black/50'>
70+
<div className='mx-4 h-[80vh] w-full max-w-4xl rounded-lg border bg-background shadow-lg'>
71+
<div className='flex items-center justify-between border-b p-4'>
72+
<h3 className='font-medium text-foreground text-lg'>{title}</h3>
73+
<button
74+
onClick={() => setIsModalOpen(false)}
75+
className='rounded p-1 text-muted-foreground hover:bg-muted hover:text-foreground'
76+
>
77+
<X className='h-4 w-4' />
78+
</button>
79+
</div>
80+
<div className='h-[calc(80vh-4rem)] overflow-auto p-4'>
81+
<pre className='whitespace-pre-wrap break-words font-mono text-foreground text-sm'>{jsonString}</pre>
82+
</div>
83+
</div>
84+
</div>
85+
)}
86+
</>
87+
)
88+
}
89+
2490
function formatExecutionData(executionData: any) {
2591
const {
2692
inputData,
@@ -160,36 +226,32 @@ function PinnedLogs({ executionData, onClose }: { executionData: any; onClose: (
160226
<span className='text-foreground text-sm'>{formatted.duration}</span>
161227
</div>
162228

163-
{formatted.cost && (
229+
{formatted.cost && formatted.cost.total > 0 && (
164230
<div className='flex items-center gap-2'>
165231
<DollarSign className='h-4 w-4 text-muted-foreground' />
166232
<span className='text-foreground text-sm'>${formatted.cost.total.toFixed(5)}</span>
167233
</div>
168234
)}
169235

170-
{formatted.tokens && (
236+
{formatted.tokens && formatted.tokens.total > 0 && (
171237
<div className='flex items-center gap-2'>
172238
<Hash className='h-4 w-4 text-muted-foreground' />
173239
<span className='text-foreground text-sm'>{formatted.tokens.total} tokens</span>
174240
</div>
175241
)}
176242
</div>
177243

178-
<div>
179-
<h4 className='mb-2 font-medium text-foreground text-sm'>Input</h4>
180-
<div className='max-h-32 overflow-y-auto rounded bg-muted p-3 font-mono text-xs'>
181-
<pre className='text-foreground'>{JSON.stringify(formatted.input, null, 2)}</pre>
182-
</div>
183-
</div>
244+
<ExpandableDataSection
245+
title="Input"
246+
data={formatted.input}
247+
/>
184248

185-
<div>
186-
<h4 className='mb-2 font-medium text-foreground text-sm'>Output</h4>
187-
<div className='max-h-32 overflow-y-auto rounded bg-muted p-3 font-mono text-xs'>
188-
<pre className='text-foreground'>{JSON.stringify(formatted.output, null, 2)}</pre>
189-
</div>
190-
</div>
249+
<ExpandableDataSection
250+
title="Output"
251+
data={formatted.output}
252+
/>
191253

192-
{formatted.cost && (
254+
{formatted.cost && formatted.cost.total > 0 && (
193255
<div>
194256
<h4 className='mb-2 font-medium text-foreground text-sm'>Cost Breakdown</h4>
195257
<div className='space-y-1 text-sm'>
@@ -209,7 +271,7 @@ function PinnedLogs({ executionData, onClose }: { executionData: any; onClose: (
209271
</div>
210272
)}
211273

212-
{formatted.tokens && (
274+
{formatted.tokens && formatted.tokens.total > 0 && (
213275
<div>
214276
<h4 className='mb-2 font-medium text-foreground text-sm'>Token Usage</h4>
215277
<div className='space-y-1 text-sm'>
@@ -242,12 +304,7 @@ interface FrozenCanvasData {
242304
startedAt: string
243305
endedAt?: string
244306
totalDurationMs?: number
245-
blockStats: {
246-
total: number
247-
success: number
248-
error: number
249-
skipped: number
250-
}
307+
251308
cost: {
252309
total: number | null
253310
input: number | null

apps/sim/app/workspace/[workspaceId]/logs/components/sidebar/sidebar.tsx

Lines changed: 2 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -494,42 +494,7 @@ export function Sidebar({
494494
</div>
495495
)}
496496

497-
{/* Enhanced Stats - only show for enhanced logs */}
498-
{log.metadata?.enhanced && log.metadata?.blockStats && (
499-
<div>
500-
<h3 className='mb-1 font-medium text-muted-foreground text-xs'>
501-
Block Execution Stats
502-
</h3>
503-
<div className='space-y-1 text-sm'>
504-
<div className='flex justify-between'>
505-
<span>Total Blocks:</span>
506-
<span className='font-medium'>{log.metadata.blockStats.total}</span>
507-
</div>
508-
<div className='flex justify-between'>
509-
<span>Successful:</span>
510-
<span className='font-medium text-green-600'>
511-
{log.metadata.blockStats.success}
512-
</span>
513-
</div>
514-
{log.metadata.blockStats.error > 0 && (
515-
<div className='flex justify-between'>
516-
<span>Failed:</span>
517-
<span className='font-medium text-red-600'>
518-
{log.metadata.blockStats.error}
519-
</span>
520-
</div>
521-
)}
522-
{log.metadata.blockStats.skipped > 0 && (
523-
<div className='flex justify-between'>
524-
<span>Skipped:</span>
525-
<span className='font-medium text-yellow-600'>
526-
{log.metadata.blockStats.skipped}
527-
</span>
528-
</div>
529-
)}
530-
</div>
531-
</div>
532-
)}
497+
533498

534499
{/* Enhanced Cost - only show for enhanced logs with actual cost data */}
535500
{log.metadata?.enhanced && hasCostInfo && (
@@ -583,7 +548,7 @@ export function Sidebar({
583548
className='w-full justify-start gap-2'
584549
>
585550
<Eye className='h-4 w-4' />
586-
View Frozen Canvas
551+
View Snapshot
587552
</Button>
588553
<p className='mt-1 text-muted-foreground text-xs'>
589554
See the exact workflow state and block inputs/outputs at execution time

apps/sim/app/workspace/[workspaceId]/logs/components/trace-spans/trace-spans-display.tsx

Lines changed: 11 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
'use client'
22

3-
import { useMemo, useState } from 'react'
3+
import { useState } from 'react'
44
import {
55
ChevronDown,
6-
ChevronDownSquare,
76
ChevronRight,
8-
ChevronUpSquare,
97
Code,
108
Cpu,
119
ExternalLink,
@@ -203,39 +201,16 @@ export function TraceSpansDisplay({
203201
// Keep track of expanded spans
204202
const [expandedSpans, setExpandedSpans] = useState<Set<string>>(new Set())
205203

206-
// Function to collect all span IDs recursively (for expand all functionality)
207-
const collectAllSpanIds = (spans: TraceSpan[]): string[] => {
208-
const ids: string[] = []
209204

210-
const collectIds = (span: TraceSpan) => {
211-
const spanId = span.id || `span-${span.name}-${span.startTime}`
212-
ids.push(spanId)
213205

214-
// Process children
215-
if (span.children && span.children.length > 0) {
216-
span.children.forEach(collectIds)
217-
}
218-
}
219-
220-
spans.forEach(collectIds)
221-
return ids
222-
}
223206

224-
const allSpanIds = useMemo(() => {
225-
if (!traceSpans || traceSpans.length === 0) return []
226-
return collectAllSpanIds(traceSpans)
227-
}, [traceSpans])
228207

229208
// Early return after all hooks
230209
if (!traceSpans || traceSpans.length === 0) {
231210
return <div className='text-muted-foreground text-sm'>No trace data available</div>
232211
}
233212

234-
// Format total duration for better readability
235-
const _formatTotalDuration = (ms: number) => {
236-
if (ms < 1000) return `${ms}ms`
237-
return `${(ms / 1000).toFixed(2)}s (${ms}ms)`
238-
}
213+
239214

240215
// Find the earliest start time among all spans to be the workflow start time
241216
const workflowStartTime = traceSpans.reduce((earliest, span) => {
@@ -269,48 +244,12 @@ export function TraceSpansDisplay({
269244
}
270245
}
271246

272-
// Handle expand all / collapse all
273-
const handleExpandAll = () => {
274-
const newExpandedSpans = new Set(allSpanIds)
275-
setExpandedSpans(newExpandedSpans)
276-
277-
if (onExpansionChange) {
278-
onExpansionChange(true)
279-
}
280-
}
281247

282-
const handleCollapseAll = () => {
283-
setExpandedSpans(new Set())
284-
285-
if (onExpansionChange) {
286-
onExpansionChange(false)
287-
}
288-
}
289-
290-
// Determine if all spans are currently expanded
291-
const allExpanded = allSpanIds.length > 0 && allSpanIds.every((id) => expandedSpans.has(id))
292248

293249
return (
294250
<div className='w-full'>
295251
<div className='mb-2 flex items-center justify-between'>
296-
<div className='font-medium text-muted-foreground text-xs'>Trace Spans</div>
297-
<button
298-
onClick={allExpanded ? handleCollapseAll : handleExpandAll}
299-
className='flex items-center gap-1 text-muted-foreground text-xs transition-colors hover:text-foreground'
300-
title={allExpanded ? 'Collapse all spans' : 'Expand all spans'}
301-
>
302-
{allExpanded ? (
303-
<>
304-
<ChevronUpSquare className='h-3.5 w-3.5' />
305-
<span>Collapse</span>
306-
</>
307-
) : (
308-
<>
309-
<ChevronDownSquare className='h-3.5 w-3.5' />
310-
<span>Expand</span>
311-
</>
312-
)}
313-
</button>
252+
<div className='font-medium text-muted-foreground text-xs'>Workflow Execution</div>
314253
</div>
315254
<div className='w-full overflow-hidden rounded-md border shadow-sm'>
316255
{traceSpans.map((span, index) => {
@@ -369,7 +308,8 @@ function TraceSpanItem({
369308
const expanded = expandedSpans.has(spanId)
370309
const hasChildren = span.children && span.children.length > 0
371310
const hasToolCalls = span.toolCalls && span.toolCalls.length > 0
372-
const hasNestedItems = hasChildren || hasToolCalls
311+
const hasInputOutput = Boolean(span.input || span.output)
312+
const hasNestedItems = hasChildren || hasToolCalls || hasInputOutput
373313

374314
// Calculate timing information
375315
const spanStartTime = new Date(span.startTime).getTime()
@@ -389,8 +329,7 @@ function TraceSpanItem({
389329
const safeStartPercent = Math.min(100, Math.max(0, relativeStartPercent))
390330
const safeWidthPercent = Math.max(2, Math.min(100 - safeStartPercent, actualDurationPercent))
391331

392-
// For parent-relative timing display
393-
const _startOffsetPercentage = totalDuration > 0 ? (startOffset / totalDuration) * 100 : 0
332+
394333

395334
// Handle click to expand/collapse this span
396335
const handleSpanClick = () => {
@@ -605,17 +544,17 @@ function TraceSpanItem({
605544
</div>
606545
</div>
607546

608-
{/* Children and tool calls */}
547+
{/* Expanded content */}
609548
{expanded && (
610549
<div>
611550
{/* Block Input/Output Data */}
612551
{(span.input || span.output) && (
613-
<div className='mt-2 ml-8 space-y-3 overflow-hidden'>
552+
<div className='mt-2 ml-8 mr-4 mb-4 space-y-3 overflow-hidden'>
614553
{/* Input Data */}
615554
{span.input && (
616555
<div>
617556
<h4 className='mb-2 font-medium text-muted-foreground text-xs'>Input</h4>
618-
<div className='overflow-hidden rounded-md bg-secondary/30 p-3'>
557+
<div className='overflow-hidden rounded-md bg-secondary/30 p-3 mb-2'>
619558
<BlockDataDisplay data={span.input} blockType={span.type} isInput={true} />
620559
</div>
621560
</div>
@@ -627,7 +566,7 @@ function TraceSpanItem({
627566
<h4 className='mb-2 font-medium text-muted-foreground text-xs'>
628567
{span.status === 'error' ? 'Error Details' : 'Output'}
629568
</h4>
630-
<div className='overflow-hidden rounded-md bg-secondary/30 p-3'>
569+
<div className='overflow-hidden rounded-md bg-secondary/30 p-3 mb-2'>
631570
<BlockDataDisplay
632571
data={span.output}
633572
blockType={span.type}
@@ -639,12 +578,8 @@ function TraceSpanItem({
639578
)}
640579
</div>
641580
)}
642-
</div>
643-
)}
644581

645-
{/* Children and tool calls */}
646-
{expanded && (
647-
<div>
582+
{/* Children and tool calls */}
648583
{/* Render child spans */}
649584
{hasChildren && (
650585
<div>

0 commit comments

Comments
 (0)