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-level { min-width: 48px; font-weight: bold; }
|
||||||
.ao-log-msg { color: #ccc; word-break: break-all; }
|
.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 ===== */
|
/* ===== Common ===== */
|
||||||
.ao-empty {
|
.ao-empty {
|
||||||
color: #94a3b8;
|
color: #94a3b8;
|
||||||
|
|||||||
@@ -5,19 +5,37 @@ import { getAgentLogs } from '../../../api';
|
|||||||
const LEVEL_STYLE = {
|
const LEVEL_STYLE = {
|
||||||
info: { color: '#60a5fa' },
|
info: { color: '#60a5fa' },
|
||||||
warning: { color: '#fbbf24' },
|
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 }) {
|
export default function LogTab({ agentId, refreshTrigger }) {
|
||||||
const [logs, setLogs] = useState([]);
|
const [logs, setLogs] = useState([]);
|
||||||
const scrollRef = useRef(null);
|
const scrollRef = useRef(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
getAgentLogs(agentId, 50).then(data => {
|
const fetchLogs = () => {
|
||||||
if (!cancelled) setLogs(Array.isArray(data) ? data : (data?.logs || []));
|
getAgentLogs(agentId, 100).then(data => {
|
||||||
});
|
if (cancelled) return;
|
||||||
return () => { cancelled = true; };
|
setLogs(Array.isArray(data) ? data : (data?.logs || []));
|
||||||
|
}).catch(() => {});
|
||||||
|
};
|
||||||
|
fetchLogs();
|
||||||
|
const interval = setInterval(fetchLogs, 5000); // 5초 폴링
|
||||||
|
return () => { cancelled = true; clearInterval(interval); };
|
||||||
}, [agentId, refreshTrigger]);
|
}, [agentId, refreshTrigger]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -30,13 +48,23 @@ export default function LogTab({ agentId, refreshTrigger }) {
|
|||||||
<div className="ao-log-tab" ref={scrollRef}>
|
<div className="ao-log-tab" ref={scrollRef}>
|
||||||
{logs.length === 0 && <div className="ao-empty">No logs yet</div>}
|
{logs.length === 0 && <div className="ao-empty">No logs yet</div>}
|
||||||
{logs.map((log, i) => {
|
{logs.map((log, i) => {
|
||||||
const style = LEVEL_STYLE[log.level] || LEVEL_STYLE.info;
|
const source = log.source || 'agent';
|
||||||
const time = new Date(log.created_at).toLocaleTimeString('ko-KR', { hour: '2-digit', minute: '2-digit', second: '2-digit' });
|
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 (
|
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-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>
|
<span className="ao-log-msg">{log.message}</span>
|
||||||
|
{source === 'access' && (
|
||||||
|
<span className="ao-log-meta">
|
||||||
|
{' '}({log.status} · {log.ms}ms)
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|||||||
Reference in New Issue
Block a user