mirror of
https://github.com/YspCoder/clawgo.git
synced 2026-05-21 09:37:31 +08:00
fix logs page crash: normalize entries and guard missing time/level
This commit is contained in:
@@ -18,7 +18,7 @@ const Logs: React.FC = () => {
|
|||||||
if (!r.ok) return;
|
if (!r.ok) return;
|
||||||
const j = await r.json();
|
const j = await r.json();
|
||||||
if (Array.isArray(j.logs)) {
|
if (Array.isArray(j.logs)) {
|
||||||
setLogs(j.logs as LogEntry[]);
|
setLogs(j.logs.map(normalizeLog));
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('load recent logs failed', e);
|
console.error('load recent logs failed', e);
|
||||||
@@ -48,11 +48,11 @@ const Logs: React.FC = () => {
|
|||||||
|
|
||||||
lines.forEach(line => {
|
lines.forEach(line => {
|
||||||
try {
|
try {
|
||||||
const log: LogEntry = JSON.parse(line);
|
const log = normalizeLog(JSON.parse(line));
|
||||||
setLogs(prev => [...prev.slice(-1000), log]);
|
setLogs(prev => [...prev.slice(-1000), log]);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Fallback for non-JSON logs
|
// Fallback for non-JSON logs
|
||||||
setLogs(prev => [...prev.slice(-1000), { time: new Date().toISOString(), level: 'INFO', msg: line }]);
|
setLogs(prev => [...prev.slice(-1000), normalizeLog({ time: new Date().toISOString(), level: 'INFO', msg: line })]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -82,8 +82,28 @@ const Logs: React.FC = () => {
|
|||||||
|
|
||||||
const clearLogs = () => setLogs([]);
|
const clearLogs = () => setLogs([]);
|
||||||
|
|
||||||
|
const normalizeLog = (v: any): LogEntry => ({
|
||||||
|
time: typeof v?.time === 'string' && v.time ? v.time : new Date().toISOString(),
|
||||||
|
level: typeof v?.level === 'string' && v.level ? v.level : 'INFO',
|
||||||
|
msg: typeof v?.msg === 'string' ? v.msg : JSON.stringify(v),
|
||||||
|
...v,
|
||||||
|
});
|
||||||
|
|
||||||
|
const formatTime = (raw: string) => {
|
||||||
|
try {
|
||||||
|
if (!raw) return '--:--:--';
|
||||||
|
if (raw.includes('T')) {
|
||||||
|
const right = raw.split('T')[1] || '';
|
||||||
|
return (right.split('.')[0] || right).trim() || '--:--:--';
|
||||||
|
}
|
||||||
|
return raw;
|
||||||
|
} catch {
|
||||||
|
return '--:--:--';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const getLevelColor = (level: string) => {
|
const getLevelColor = (level: string) => {
|
||||||
switch (level.toUpperCase()) {
|
switch ((level || 'INFO').toUpperCase()) {
|
||||||
case 'ERROR': return 'text-red-400';
|
case 'ERROR': return 'text-red-400';
|
||||||
case 'WARN': return 'text-amber-400';
|
case 'WARN': return 'text-amber-400';
|
||||||
case 'DEBUG': return 'text-blue-400';
|
case 'DEBUG': return 'text-blue-400';
|
||||||
@@ -135,8 +155,8 @@ const Logs: React.FC = () => {
|
|||||||
)}
|
)}
|
||||||
{logs.map((log, i) => (
|
{logs.map((log, i) => (
|
||||||
<div key={i} className="group flex gap-4 hover:bg-zinc-900/50 rounded px-2 py-0.5 transition-colors">
|
<div key={i} className="group flex gap-4 hover:bg-zinc-900/50 rounded px-2 py-0.5 transition-colors">
|
||||||
<span className="text-zinc-600 shrink-0 select-none">[{log.time.split('T')[1].split('.')[0]}]</span>
|
<span className="text-zinc-600 shrink-0 select-none">[{formatTime(log.time)}]</span>
|
||||||
<span className={`font-bold shrink-0 select-none w-12 ${getLevelColor(log.level)}`}>{log.level.toUpperCase()}</span>
|
<span className={`font-bold shrink-0 select-none w-12 ${getLevelColor(log.level)}`}>{(log.level || 'INFO').toUpperCase()}</span>
|
||||||
<span className="text-zinc-300 break-all">{log.msg}</span>
|
<span className="text-zinc-300 break-all">{log.msg}</span>
|
||||||
{Object.keys(log).filter(k => !['time', 'level', 'msg'].includes(k)).map(k => (
|
{Object.keys(log).filter(k => !['time', 'level', 'msg'].includes(k)).map(k => (
|
||||||
<span key={k} className="text-zinc-500 italic shrink-0 select-none">{k}={JSON.stringify(log[k])}</span>
|
<span key={k} className="text-zinc-500 italic shrink-0 select-none">{k}={JSON.stringify(log[k])}</span>
|
||||||
|
|||||||
Reference in New Issue
Block a user