feat: Add coordinator log viewer to cluster UI
- Created /api/cluster/logs endpoint to read coordinator.log - Added real-time log display in cluster UI (updates every 3s) - Shows last 100 lines of coordinator.log in terminal-style display - Includes manual refresh button - Improves debugging experience - no need to SSH for logs User feedback: 'why dont we add the output of the log at the bottom of the page so i know whats going on' This addresses poor visibility into coordinator errors and failures. Next step: Fix SSH timeout issue blocking worker execution.
This commit is contained in:
@@ -56,6 +56,8 @@ export default function ClusterPage() {
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
const [controlLoading, setControlLoading] = useState(false)
|
||||
const [controlMessage, setControlMessage] = useState<string | null>(null)
|
||||
const [coordinatorLog, setCoordinatorLog] = useState<string>('')
|
||||
const [logLoading, setLogLoading] = useState(false)
|
||||
|
||||
const fetchStatus = async () => {
|
||||
try {
|
||||
@@ -71,6 +73,21 @@ export default function ClusterPage() {
|
||||
}
|
||||
}
|
||||
|
||||
const fetchLog = async () => {
|
||||
try {
|
||||
setLogLoading(true)
|
||||
const response = await fetch('/api/cluster/logs')
|
||||
const data = await response.json()
|
||||
if (data.success) {
|
||||
setCoordinatorLog(data.log)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch coordinator log:', error)
|
||||
} finally {
|
||||
setLogLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
const handleControl = async (action: 'start' | 'stop') => {
|
||||
setControlLoading(true)
|
||||
setControlMessage(null)
|
||||
@@ -94,8 +111,13 @@ export default function ClusterPage() {
|
||||
|
||||
useEffect(() => {
|
||||
fetchStatus()
|
||||
const interval = setInterval(fetchStatus, 30000) // Refresh every 30s
|
||||
return () => clearInterval(interval)
|
||||
fetchLog()
|
||||
const statusInterval = setInterval(fetchStatus, 30000) // Refresh status every 30s
|
||||
const logInterval = setInterval(fetchLog, 3000) // Refresh log every 3s
|
||||
return () => {
|
||||
clearInterval(statusInterval)
|
||||
clearInterval(logInterval)
|
||||
}
|
||||
}, [])
|
||||
|
||||
if (loading) {
|
||||
@@ -211,6 +233,28 @@ export default function ClusterPage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Coordinator Log */}
|
||||
<div className="bg-gray-900 rounded-lg p-6 border border-gray-800">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<h3 className="text-lg font-semibold">Coordinator Log</h3>
|
||||
<button
|
||||
onClick={fetchLog}
|
||||
disabled={logLoading}
|
||||
className="px-3 py-1 bg-gray-800 hover:bg-gray-700 rounded text-sm disabled:opacity-50"
|
||||
>
|
||||
{logLoading ? '⏳ Loading...' : '🔄 Refresh'}
|
||||
</button>
|
||||
</div>
|
||||
<div className="bg-black rounded-lg p-4 overflow-auto max-h-96">
|
||||
<pre className="text-xs text-green-400 font-mono whitespace-pre-wrap">
|
||||
{coordinatorLog || 'No log output available'}
|
||||
</pre>
|
||||
</div>
|
||||
<div className="mt-2 text-xs text-gray-500">
|
||||
Updates automatically every 3 seconds
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Worker Details */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6">
|
||||
{status.workers.map((worker) => (
|
||||
|
||||
Reference in New Issue
Block a user