mirror of
https://github.com/crabwise-ai/crabwalk.git
synced 2026-08-14 00:57:52 +00:00
Add log collection feature to SettingsPanel and TRPC router
- Implemented log collection functionality in the SettingsPanel, allowing users to start/stop recording events and view the count of collected logs. - Enhanced the TRPC router with procedures for managing log collection state, downloading collected logs, and clearing logs. - Updated MonitorPage to handle log collection state and integrate new log management features, improving event tracking and user experience.
This commit is contained in:
@@ -1,13 +1,18 @@
|
||||
import { useState } from 'react'
|
||||
import { motion, AnimatePresence } from 'framer-motion'
|
||||
import { Settings, X, History, Wifi, WifiOff, RefreshCw, Terminal } from 'lucide-react'
|
||||
import { Settings, X, History, Wifi, WifiOff, RefreshCw, Terminal, Download, Trash2, Database } from 'lucide-react'
|
||||
|
||||
interface SettingsPanelProps {
|
||||
connected: boolean
|
||||
historicalMode: boolean
|
||||
debugMode: boolean
|
||||
logCollection: boolean
|
||||
logCount: number
|
||||
onHistoricalModeChange: (enabled: boolean) => void
|
||||
onDebugModeChange: (enabled: boolean) => void
|
||||
onLogCollectionChange: (enabled: boolean) => void
|
||||
onDownloadLogs: () => void
|
||||
onClearLogs: () => void
|
||||
onConnect: () => void
|
||||
onDisconnect: () => void
|
||||
onRefresh: () => void
|
||||
@@ -17,8 +22,13 @@ export function SettingsPanel({
|
||||
connected,
|
||||
historicalMode,
|
||||
debugMode,
|
||||
logCollection,
|
||||
logCount,
|
||||
onHistoricalModeChange,
|
||||
onDebugModeChange,
|
||||
onLogCollectionChange,
|
||||
onDownloadLogs,
|
||||
onClearLogs,
|
||||
onConnect,
|
||||
onDisconnect,
|
||||
onRefresh,
|
||||
@@ -161,6 +171,56 @@ export function SettingsPanel({
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Log collection */}
|
||||
<div className="panel-retro p-4">
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<Database size={18} className="text-shell-500" />
|
||||
<span className="font-display text-sm font-medium text-gray-200 uppercase tracking-wide">
|
||||
Log Collection
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<p className="font-console text-[10px] text-shell-500 mb-3">
|
||||
<span className="text-crab-600">></span> collect raw events for export
|
||||
</p>
|
||||
|
||||
{logCount > 0 && (
|
||||
<div className="font-console text-[10px] text-neon-mint mb-3">
|
||||
<span className="text-crab-600">></span> {logCount} events collected
|
||||
</div>
|
||||
)}
|
||||
|
||||
<button
|
||||
onClick={() => onLogCollectionChange(!logCollection)}
|
||||
className={`w-full px-4 py-2 font-display text-xs uppercase tracking-wide rounded-lg border-2 transition-all mb-2 ${
|
||||
logCollection
|
||||
? 'bg-neon-mint/20 border-neon-mint/50 text-neon-mint'
|
||||
: 'bg-shell-800 border-shell-700 text-gray-400 hover:border-shell-600'
|
||||
}`}
|
||||
>
|
||||
{logCollection ? 'Recording...' : 'Start Recording'}
|
||||
</button>
|
||||
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
onClick={onDownloadLogs}
|
||||
disabled={logCount === 0}
|
||||
className="flex-1 flex items-center justify-center gap-2 px-3 py-2 font-display text-xs uppercase tracking-wide bg-shell-800 hover:bg-shell-700 border-2 border-shell-700 rounded-lg transition-all disabled:opacity-40 disabled:cursor-not-allowed"
|
||||
>
|
||||
<Download size={12} />
|
||||
Save
|
||||
</button>
|
||||
<button
|
||||
onClick={onClearLogs}
|
||||
disabled={logCount === 0}
|
||||
className="flex-1 flex items-center justify-center gap-2 px-3 py-2 font-display text-xs uppercase tracking-wide bg-shell-800 hover:bg-crab-900/50 border-2 border-shell-700 hover:border-crab-700 rounded-lg transition-all disabled:opacity-40 disabled:cursor-not-allowed"
|
||||
>
|
||||
<Trash2 size={12} />
|
||||
Clear
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Info panel */}
|
||||
<div className="panel-retro p-4 bg-shell-950/50">
|
||||
<h3 className="font-display text-xs text-gray-400 uppercase tracking-wide mb-3">
|
||||
|
||||
@@ -13,6 +13,10 @@ import {
|
||||
// Server-side debug mode state
|
||||
let debugMode = false
|
||||
|
||||
// Server-side log collection
|
||||
let collectLogs = false
|
||||
const collectedEvents: Array<{ timestamp: number; event: unknown }> = []
|
||||
|
||||
const t = initTRPC.create({
|
||||
transformer: superjson,
|
||||
})
|
||||
@@ -66,6 +70,38 @@ const clawdbotRouter = router({
|
||||
return { debugMode }
|
||||
}),
|
||||
|
||||
// Log collection
|
||||
setLogCollection: publicProcedure
|
||||
.input(z.object({ enabled: z.boolean() }))
|
||||
.mutation(({ input }) => {
|
||||
collectLogs = input.enabled
|
||||
if (input.enabled) {
|
||||
console.log(`[clawdbot] log collection started`)
|
||||
} else {
|
||||
console.log(`[clawdbot] log collection stopped, ${collectedEvents.length} events collected`)
|
||||
}
|
||||
return { collectLogs, eventCount: collectedEvents.length }
|
||||
}),
|
||||
|
||||
getLogCollection: publicProcedure.query(() => {
|
||||
return { collectLogs, eventCount: collectedEvents.length }
|
||||
}),
|
||||
|
||||
downloadLogs: publicProcedure.query(() => {
|
||||
return {
|
||||
events: collectedEvents,
|
||||
count: collectedEvents.length,
|
||||
collectedAt: new Date().toISOString(),
|
||||
}
|
||||
}),
|
||||
|
||||
clearLogs: publicProcedure.mutation(() => {
|
||||
const count = collectedEvents.length
|
||||
collectedEvents.length = 0
|
||||
console.log(`[clawdbot] cleared ${count} collected events`)
|
||||
return { cleared: count }
|
||||
}),
|
||||
|
||||
sessions: publicProcedure
|
||||
.input(
|
||||
z
|
||||
@@ -103,6 +139,14 @@ const clawdbotRouter = router({
|
||||
const client = getClawdbotClient()
|
||||
|
||||
const unsubscribe = client.onEvent((event) => {
|
||||
// Collect raw event when log collection is enabled
|
||||
if (collectLogs) {
|
||||
collectedEvents.push({
|
||||
timestamp: Date.now(),
|
||||
event,
|
||||
})
|
||||
}
|
||||
|
||||
// Log raw event when debug mode is enabled
|
||||
if (debugMode) {
|
||||
console.log('\n[DEBUG] Raw event:', JSON.stringify(event, null, 2))
|
||||
|
||||
@@ -63,6 +63,8 @@ function MonitorPage() {
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
const [historicalMode, setHistoricalMode] = useState(false)
|
||||
const [debugMode, setDebugMode] = useState(false)
|
||||
const [logCollection, setLogCollection] = useState(false)
|
||||
const [logCount, setLogCount] = useState(0)
|
||||
const [selectedSession, setSelectedSession] = useState<string | null>(null)
|
||||
|
||||
// Sidebar collapse state - default to collapsed on mobile
|
||||
@@ -168,6 +170,56 @@ function MonitorPage() {
|
||||
}
|
||||
}
|
||||
|
||||
const handleLogCollectionChange = async (enabled: boolean) => {
|
||||
setLogCollection(enabled)
|
||||
try {
|
||||
const result = await trpc.clawdbot.setLogCollection.mutate({ enabled })
|
||||
setLogCount(result.eventCount)
|
||||
} catch (e) {
|
||||
console.error('Failed to set log collection:', e)
|
||||
}
|
||||
}
|
||||
|
||||
const handleDownloadLogs = async () => {
|
||||
try {
|
||||
const result = await trpc.clawdbot.downloadLogs.query()
|
||||
const blob = new Blob([JSON.stringify(result, null, 2)], { type: 'application/json' })
|
||||
const url = URL.createObjectURL(blob)
|
||||
const a = document.createElement('a')
|
||||
a.href = url
|
||||
a.download = `clawdbot-events-${new Date().toISOString().slice(0, 19).replace(/:/g, '-')}.json`
|
||||
document.body.appendChild(a)
|
||||
a.click()
|
||||
document.body.removeChild(a)
|
||||
URL.revokeObjectURL(url)
|
||||
} catch (e) {
|
||||
console.error('Failed to download logs:', e)
|
||||
}
|
||||
}
|
||||
|
||||
const handleClearLogs = async () => {
|
||||
try {
|
||||
await trpc.clawdbot.clearLogs.mutate()
|
||||
setLogCount(0)
|
||||
} catch (e) {
|
||||
console.error('Failed to clear logs:', e)
|
||||
}
|
||||
}
|
||||
|
||||
// Poll log count while collecting
|
||||
useEffect(() => {
|
||||
if (!logCollection) return
|
||||
const interval = setInterval(async () => {
|
||||
try {
|
||||
const result = await trpc.clawdbot.getLogCollection.query()
|
||||
setLogCount(result.eventCount)
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}, 2000)
|
||||
return () => clearInterval(interval)
|
||||
}, [logCollection])
|
||||
|
||||
const handleToggleSidebar = useCallback(() => {
|
||||
setSidebarCollapsed((prev) => !prev)
|
||||
}, [])
|
||||
@@ -275,8 +327,13 @@ function MonitorPage() {
|
||||
connected={connected}
|
||||
historicalMode={historicalMode}
|
||||
debugMode={debugMode}
|
||||
logCollection={logCollection}
|
||||
logCount={logCount}
|
||||
onHistoricalModeChange={handleHistoricalModeChange}
|
||||
onDebugModeChange={handleDebugModeChange}
|
||||
onLogCollectionChange={handleLogCollectionChange}
|
||||
onDownloadLogs={handleDownloadLogs}
|
||||
onClearLogs={handleClearLogs}
|
||||
onConnect={handleConnect}
|
||||
onDisconnect={handleDisconnect}
|
||||
onRefresh={handleRefresh}
|
||||
|
||||
Reference in New Issue
Block a user