feat(agent-office/LogTab): source 뱃지 + access 메타데이터 표시 + 5초 폴링
This commit is contained in:
@@ -371,6 +371,18 @@
|
||||
.ao-log-level { min-width: 48px; font-weight: bold; }
|
||||
.ao-log-msg { color: #ccc; word-break: break-all; }
|
||||
|
||||
.ao-log-source {
|
||||
margin-left: 6px;
|
||||
font-size: 0.75em;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.ao-log-meta {
|
||||
color: #6b7280;
|
||||
font-size: 0.85em;
|
||||
}
|
||||
|
||||
/* ===== Common ===== */
|
||||
.ao-empty {
|
||||
color: #94a3b8;
|
||||
|
||||
@@ -5,19 +5,37 @@ import { getAgentLogs } from '../../../api';
|
||||
const LEVEL_STYLE = {
|
||||
info: { color: '#60a5fa' },
|
||||
warning: { color: '#fbbf24' },
|
||||
error: { color: '#ef4444' }
|
||||
error: { color: '#ef4444' },
|
||||
};
|
||||
|
||||
const SOURCE_STYLE = {
|
||||
agent: { color: '#9ca3af', label: 'AGENT' },
|
||||
access: { color: '#5eead4', label: 'ACCESS' },
|
||||
log: { color: '#a78bfa', label: 'LOG' },
|
||||
};
|
||||
|
||||
function formatTime(iso) {
|
||||
if (!iso) return '';
|
||||
return new Date(iso).toLocaleTimeString('ko-KR', {
|
||||
hour: '2-digit', minute: '2-digit', second: '2-digit',
|
||||
});
|
||||
}
|
||||
|
||||
export default function LogTab({ agentId, refreshTrigger }) {
|
||||
const [logs, setLogs] = useState([]);
|
||||
const scrollRef = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
getAgentLogs(agentId, 50).then(data => {
|
||||
if (!cancelled) setLogs(Array.isArray(data) ? data : (data?.logs || []));
|
||||
});
|
||||
return () => { cancelled = true; };
|
||||
const fetchLogs = () => {
|
||||
getAgentLogs(agentId, 100).then(data => {
|
||||
if (cancelled) return;
|
||||
setLogs(Array.isArray(data) ? data : (data?.logs || []));
|
||||
}).catch(() => {});
|
||||
};
|
||||
fetchLogs();
|
||||
const interval = setInterval(fetchLogs, 5000); // 5초 폴링
|
||||
return () => { cancelled = true; clearInterval(interval); };
|
||||
}, [agentId, refreshTrigger]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -30,13 +48,23 @@ export default function LogTab({ agentId, refreshTrigger }) {
|
||||
<div className="ao-log-tab" ref={scrollRef}>
|
||||
{logs.length === 0 && <div className="ao-empty">No logs yet</div>}
|
||||
{logs.map((log, i) => {
|
||||
const style = LEVEL_STYLE[log.level] || LEVEL_STYLE.info;
|
||||
const time = new Date(log.created_at).toLocaleTimeString('ko-KR', { hour: '2-digit', minute: '2-digit', second: '2-digit' });
|
||||
const source = log.source || 'agent';
|
||||
const sourceMeta = SOURCE_STYLE[source] || SOURCE_STYLE.agent;
|
||||
const levelStyle = LEVEL_STYLE[log.level] || LEVEL_STYLE.info;
|
||||
const time = formatTime(log.ts || log.created_at);
|
||||
return (
|
||||
<div key={log.id || i} className="ao-log-item">
|
||||
<div key={log.id || `${source}-${i}-${time}`} className="ao-log-item">
|
||||
<span className="ao-log-time">{time}</span>
|
||||
<span className="ao-log-level" style={style}>[{log.level}]</span>
|
||||
<span className="ao-log-source" style={{ color: sourceMeta.color }}>
|
||||
[{sourceMeta.label}]
|
||||
</span>
|
||||
<span className="ao-log-level" style={levelStyle}>[{log.level}]</span>
|
||||
<span className="ao-log-msg">{log.message}</span>
|
||||
{source === 'access' && (
|
||||
<span className="ao-log-meta">
|
||||
{' '}({log.status} · {log.ms}ms)
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
|
||||
Reference in New Issue
Block a user