Skip to content

Commit 4708be9

Browse files
committed
feat(dashboard): implement log buffer size limit and enhance log filtering logic
1 parent 481b3c9 commit 4708be9

File tree

2 files changed

+41
-51
lines changed

2 files changed

+41
-51
lines changed

dashboard/src/components/nodes/line-count-filter.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export function LineCountFilter({ value, onValueChange }: LineCountFilterProps)
2020
}
2121

2222
return (
23-
<div className="flex items-center gap-2">
23+
<div className="flex items-center gap-2 w-full sm:w-3/5 md:w-2/6 lg:w-2/8">
2424
<span className="text-sm text-muted-foreground whitespace-nowrap">{t('nodes.logs.linesLabel')}</span>
2525
<Select dir={dir} value={value.toString()} onValueChange={value => onValueChange(Number(value))}>
2626
<SelectTrigger className="h-9">

dashboard/src/pages/_dashboard.nodes.logs.tsx

Lines changed: 40 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,21 @@ import { TerminalLine } from '@/components/nodes/terminal-line'
1515
import { LineCountFilter } from '@/components/nodes/line-count-filter'
1616
import { SinceLogsFilter, type TimeFilter } from '@/components/nodes/since-logs-filter'
1717
import { StatusLogsFilter } from '@/components/nodes/status-logs-filter'
18+
19+
/** Max raw SSE chunks kept in memory; display "lines" is sliced client-side (no reconnect). */
20+
const RAW_LOG_BUFFER_MAX = 10000
21+
22+
const SINCE_DURATION_MS: Record<Exclude<TimeFilter, 'all'>, number> = {
23+
'1m': 60 * 1000,
24+
'5m': 5 * 60 * 1000,
25+
'15m': 15 * 60 * 1000,
26+
'30m': 30 * 60 * 1000,
27+
'1h': 60 * 60 * 1000,
28+
'2h': 2 * 60 * 60 * 1000,
29+
'6h': 6 * 60 * 60 * 1000,
30+
'12h': 12 * 60 * 60 * 1000,
31+
'24h': 24 * 60 * 60 * 1000,
32+
}
1833
import { parseLogs, getLogType, type LogLine } from '@/utils/logsUtils'
1934
import { EventSource } from 'eventsource'
2035

@@ -42,9 +57,8 @@ export default function NodeLogs() {
4257
const dir = useDirDetection()
4358
const [selectedNode, setSelectedNode] = useState<number>(0)
4459
const [rawLogs, setRawLogs] = React.useState<string[]>([])
45-
const [filteredLogs, setFilteredLogs] = React.useState<LogLine[]>([])
4660
const [autoScroll, setAutoScroll] = React.useState(true)
47-
const [lines, setLines] = React.useState<number>(100)
61+
const [lines, setLines] = React.useState<number>(1000)
4862
const [search, setSearch] = React.useState<string>('')
4963
const [showTimestamp, setShowTimestamp] = React.useState(true)
5064
const [since, setSince] = React.useState<TimeFilter>('all')
@@ -92,17 +106,11 @@ export default function NodeLogs() {
92106
setSearch(e.target.value || '')
93107
}
94108

95-
const handleLines = (lines: number) => {
96-
setRawLogs([])
97-
setFilteredLogs([])
98-
setMessageBuffer([])
99-
setLines(lines)
109+
const handleLines = (nextLines: number) => {
110+
setLines(nextLines)
100111
}
101112

102113
const handleSince = (value: TimeFilter) => {
103-
setRawLogs([])
104-
setFilteredLogs([])
105-
setMessageBuffer([])
106114
setSince(value)
107115
}
108116

@@ -112,7 +120,7 @@ export default function NodeLogs() {
112120
if (messageBuffer.length > 0) {
113121
setRawLogs(prev => {
114122
const combined = [...prev, ...messageBuffer]
115-
return combined.slice(-lines)
123+
return combined.slice(-RAW_LOG_BUFFER_MAX)
116124
})
117125
setMessageBuffer([])
118126
}
@@ -126,7 +134,6 @@ export default function NodeLogs() {
126134
const handleNodeChange = (nodeId: number) => {
127135
setSelectedNode(nodeId)
128136
setRawLogs([])
129-
setFilteredLogs([])
130137
setMessageBuffer([])
131138
setIsPaused(false)
132139
isPausedRef.current = false
@@ -146,7 +153,6 @@ export default function NodeLogs() {
146153
let noDataTimeout: NodeJS.Timeout
147154
setIsLoading(true)
148155
setRawLogs([])
149-
setFilteredLogs([])
150156
setMessageBuffer([])
151157
// Reset pause state when container changes
152158
setIsPaused(false)
@@ -199,7 +205,7 @@ export default function NodeLogs() {
199205
// When not paused, display messages normally
200206
setRawLogs(prev => {
201207
const updated = [...prev, e.data]
202-
return updated.slice(-lines)
208+
return updated.slice(-RAW_LOG_BUFFER_MAX)
203209
})
204210
}
205211

@@ -217,57 +223,41 @@ export default function NodeLogs() {
217223
return () => {
218224
isCurrentConnection = false
219225
if (noDataTimeout) clearTimeout(noDataTimeout)
220-
if (eventSource.readyState === EventSource.OPEN) {
221-
eventSource.close()
222-
}
226+
eventSource.close()
223227
}
224-
}, [selectedNode, lines])
225-
226-
const handleFilter = (logs: LogLine[]) => {
227-
return logs.filter(log => {
228-
const logType = getLogType(log.message).type
229-
230-
// Filter by type
231-
if (typeFilter.length > 0 && !typeFilter.includes(logType)) {
232-
return false
233-
}
234-
235-
// Filter by search term
236-
if (search && !log.message.toLowerCase().includes(search.toLowerCase())) {
237-
return false
238-
}
239-
240-
return true
241-
})
242-
}
228+
}, [selectedNode])
243229

244230
// Sync isPausedRef with isPaused state
245231
useEffect(() => {
246232
isPausedRef.current = isPaused
247233
}, [isPaused])
248234

249-
useEffect(() => {
250-
setRawLogs([])
251-
setFilteredLogs([])
252-
setMessageBuffer([])
253-
}, [selectedNode])
254-
255-
useEffect(() => {
235+
const filteredLogs = useMemo(() => {
256236
const logs = parseLogs(rawLogs.join('\n'))
257237

258-
// Sort logs by their extracted timestamps (not SSE arrival time)
259-
const sortedLogs = logs.sort((a, b) => {
260-
// Logs without timestamps go to the end
238+
const sortedLogs = [...logs].sort((a, b) => {
261239
if (!a.timestamp && !b.timestamp) return 0
262240
if (!a.timestamp) return 1
263241
if (!b.timestamp) return -1
264-
265-
// Sort by actual log timestamp
266242
return a.timestamp.getTime() - b.timestamp.getTime()
267243
})
268244

269-
const filtered = handleFilter(sortedLogs)
270-
setFilteredLogs(filtered)
245+
const cutoffMs = since === 'all' ? null : Date.now() - SINCE_DURATION_MS[since]
246+
247+
return sortedLogs
248+
.filter(log => {
249+
if (typeFilter.length > 0 && !typeFilter.includes(getLogType(log.message).type)) {
250+
return false
251+
}
252+
if (search && !log.message.toLowerCase().includes(search.toLowerCase())) {
253+
return false
254+
}
255+
if (cutoffMs !== null && log.timestamp && log.timestamp.getTime() < cutoffMs) {
256+
return false
257+
}
258+
return true
259+
})
260+
.slice(-lines)
271261
}, [rawLogs, search, lines, since, typeFilter])
272262

273263
useEffect(() => {

0 commit comments

Comments
 (0)