Add scheduler log clear button

- POST /orb/auto/clear-log: clears in-memory log lines and deletes
  the orb_scheduler.log file on disk
- ORBAutoScheduler.clear_log(): implements the wipe
- Log panel header now has an Eraser icon button on the right;
  disabled when log is empty

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
main
I Luk Kim 4 months ago
parent 1d97595607
commit 55a9618b20

@ -240,6 +240,16 @@ class ORBAutoScheduler:
if self._task and not self._task.done():
self._task.cancel()
def clear_log(self) -> None:
"""Wipe in-memory log and the persisted log file."""
self._log_lines = []
try:
lf = self._log_file_path()
if lf.exists():
lf.unlink()
except Exception:
pass
def get_log(self, lines: int = 200) -> str:
return "\n".join(self._log_lines[-lines:])

@ -332,6 +332,13 @@ async def start_auto(req: AutoStartRequest) -> dict[str, Any]:
return {"started": True, "dry_run": req.dry_run, "sessions": req.sessions}
@router.post("/auto/clear-log")
def clear_auto_log() -> dict[str, Any]:
from apps.web.orb_trading_service import orb_auto_scheduler
orb_auto_scheduler.clear_log()
return {"cleared": True}
@router.post("/auto/stop")
async def stop_auto() -> dict[str, Any]:
from apps.web.orb_trading_service import orb_auto_scheduler

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -5,7 +5,7 @@
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Fithia2</title>
<script type="module" crossorigin src="/assets/index-CNWrJRnR.js"></script>
<script type="module" crossorigin src="/assets/index-2wtBT0D_.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-AVDbFxMk.css">
</head>
<body>

@ -902,6 +902,8 @@ export const orbTradingApi = {
autoStop: () => request<{ stopped: boolean }>('/orb/auto/stop', { method: 'POST' }),
clearLog: () => request<{ cleared: boolean }>('/orb/auto/clear-log', { method: 'POST' }),
// Strategies
strategies: () => request<{ strategies: OrbStrategyInfo[] }>('/orb/strategies'),
};

@ -5,7 +5,7 @@ import {
} from 'recharts';
import {
Zap, Play, Square, Plus, Trash2, Pause, RotateCcw,
TrendingUp, TrendingDown, ChevronDown, PlayCircle,
TrendingUp, TrendingDown, ChevronDown, PlayCircle, Eraser,
} from 'lucide-react';
import {
orbTradingApi,
@ -77,6 +77,11 @@ function SchedulerPanel({ selectedSessions }: { selectedSessions: string[] }) {
onError: (e: Error) => alert(`스케줄러 중지 실패: ${e.message}`),
});
const clearLogMut = useMutation({
mutationFn: orbTradingApi.clearLog,
onSuccess: () => qc.invalidateQueries({ queryKey: ['orb-auto-status'] }),
});
const logLines = data?.log_tail ?? [];
// Auto-scroll to bottom when new log lines arrive (must be before early return)
@ -207,20 +212,30 @@ function SchedulerPanel({ selectedSessions }: { selectedSessions: string[] }) {
{/* Log */}
<div>
<button
style={{ ...btn('ghost'), width: '100%', justifyContent: 'space-between', borderRadius: 0, padding: '8px 20px' }}
onClick={() => setShowLog(v => !v)}
>
<span style={{ fontSize: 12 }}>
{logLines.length > 0 && (
<span style={{ marginLeft: 8, color: 'var(--text3)', fontWeight: 400 }}>
({logLines.length})
</span>
)}
</span>
<ChevronDown size={13} style={{ transform: showLog ? 'rotate(180deg)' : undefined, transition: 'transform 0.2s' }} />
</button>
<div style={{ display: 'flex', alignItems: 'center', borderTop: '1px solid var(--border)' }}>
<button
style={{ ...btn('ghost'), flex: 1, justifyContent: 'space-between', borderRadius: 0, border: 'none', padding: '8px 20px' }}
onClick={() => setShowLog(v => !v)}
>
<span style={{ fontSize: 12 }}>
{logLines.length > 0 && (
<span style={{ marginLeft: 8, color: 'var(--text3)', fontWeight: 400 }}>
({logLines.length})
</span>
)}
</span>
<ChevronDown size={13} style={{ transform: showLog ? 'rotate(180deg)' : undefined, transition: 'transform 0.2s' }} />
</button>
<button
style={{ ...btn('ghost'), borderRadius: 0, border: 'none', borderLeft: '1px solid var(--border)', padding: '8px 14px', color: 'var(--text3)' }}
onClick={() => clearLogMut.mutate()}
disabled={clearLogMut.isPending || logLines.length === 0}
title="로그 초기화"
>
<Eraser size={13} />
</button>
</div>
{showLog && (
<pre ref={logRef} style={{
margin: 0, padding: '10px 20px', maxHeight: 480, overflowY: 'auto',

Loading…
Cancel
Save