refactor: use LogViewer in AgentInstance page

Replace custom log entry rendering (MonoText, Badge, per-entry HTML) with
the LogViewer composite component. Map mock data to LogEntry interface,
remove formatLogTime helper, and clean up unused CSS classes and imports
(Card, CodeBlock, ProgressBar, sectionHeaderRow, sectionTitle, fdRow).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-03-24 15:22:41 +01:00
parent 043f631eac
commit eb62c80daf
2 changed files with 16 additions and 101 deletions

View File

@@ -51,20 +51,6 @@
font-family: var(--font-mono); font-family: var(--font-mono);
} }
/* Section header — matches /agents */
.sectionHeaderRow {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 12px;
}
.sectionTitle {
font-size: 13px;
font-weight: 600;
color: var(--text-primary);
}
/* Charts 3x2 grid */ /* Charts 3x2 grid */
.chartsGrid { .chartsGrid {
display: grid; display: grid;
@@ -125,12 +111,6 @@
font-weight: 500; font-weight: 500;
} }
.fdRow {
display: flex;
align-items: center;
gap: 8px;
}
/* Log + Timeline side by side */ /* Log + Timeline side by side */
.bottomRow { .bottomRow {
display: grid; display: grid;
@@ -155,52 +135,7 @@
border-bottom: 1px solid var(--border-subtle); border-bottom: 1px solid var(--border-subtle);
} }
.logEntries { /* Empty state (shared) */
max-height: 360px;
overflow-y: auto;
font-size: 11px;
}
.logEntry {
display: flex;
align-items: flex-start;
gap: 8px;
padding: 5px 16px;
border-bottom: 1px solid var(--border-subtle);
font-family: var(--font-mono);
transition: background 0.1s;
}
.logEntry:hover {
background: var(--bg-hover);
}
.logEntry:last-child {
border-bottom: none;
}
.logTime {
flex-shrink: 0;
color: var(--text-muted);
min-width: 60px;
}
.logLogger {
flex-shrink: 0;
color: var(--text-faint);
max-width: 220px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.logMsg {
color: var(--text-primary);
font-family: var(--font-mono);
font-size: 11px;
word-break: break-word;
}
.logEmpty { .logEmpty {
padding: 24px; padding: 24px;
text-align: center; text-align: center;

View File

@@ -12,16 +12,15 @@ import { LineChart } from '../../design-system/composites/LineChart/LineChart'
import { AreaChart } from '../../design-system/composites/AreaChart/AreaChart' import { AreaChart } from '../../design-system/composites/AreaChart/AreaChart'
import { EventFeed } from '../../design-system/composites/EventFeed/EventFeed' import { EventFeed } from '../../design-system/composites/EventFeed/EventFeed'
import { Tabs } from '../../design-system/composites/Tabs/Tabs' import { Tabs } from '../../design-system/composites/Tabs/Tabs'
import { LogViewer } from '../../design-system/composites/LogViewer/LogViewer'
import type { LogEntry } from '../../design-system/composites/LogViewer/LogViewer'
// Primitives // Primitives
import { StatusDot } from '../../design-system/primitives/StatusDot/StatusDot' import { StatusDot } from '../../design-system/primitives/StatusDot/StatusDot'
import { MonoText } from '../../design-system/primitives/MonoText/MonoText' import { MonoText } from '../../design-system/primitives/MonoText/MonoText'
import { Badge } from '../../design-system/primitives/Badge/Badge' import { Badge } from '../../design-system/primitives/Badge/Badge'
import { StatCard } from '../../design-system/primitives/StatCard/StatCard' import { StatCard } from '../../design-system/primitives/StatCard/StatCard'
import { ProgressBar } from '../../design-system/primitives/ProgressBar/ProgressBar'
import { SectionHeader } from '../../design-system/primitives/SectionHeader/SectionHeader' import { SectionHeader } from '../../design-system/primitives/SectionHeader/SectionHeader'
import { Card } from '../../design-system/primitives/Card/Card'
import { CodeBlock } from '../../design-system/primitives/CodeBlock/CodeBlock'
// Global filters // Global filters
import { useGlobalFilters } from '../../design-system/providers/GlobalFilterProvider' import { useGlobalFilters } from '../../design-system/providers/GlobalFilterProvider'
@@ -53,27 +52,23 @@ function buildMemoryHistory(currentPct: number) {
// ── Mock log entries ───────────────────────────────────────────────────────── // ── Mock log entries ─────────────────────────────────────────────────────────
function buildLogEntries(agentName: string) { function buildLogEntries(agentName: string): LogEntry[] {
const now = Date.now() const now = Date.now()
const MIN = 60_000 const MIN = 60_000
return [ return [
{ ts: new Date(now - 1 * MIN).toISOString(), level: 'INFO', logger: 'o.a.c.impl.DefaultCamelContext', msg: `Route order-validation started and consuming from: direct:validate` }, { timestamp: new Date(now - 1 * MIN).toISOString(), level: 'info', message: `[o.a.c.impl.DefaultCamelContext] Route order-validation started and consuming from: direct:validate` },
{ ts: new Date(now - 2 * MIN).toISOString(), level: 'INFO', logger: 'o.a.c.impl.DefaultCamelContext', msg: `Total 3 routes, of which 3 are started` }, { timestamp: new Date(now - 2 * MIN).toISOString(), level: 'info', message: `[o.a.c.impl.DefaultCamelContext] Total 3 routes, of which 3 are started` },
{ ts: new Date(now - 5 * MIN).toISOString(), level: 'WARN', logger: 'o.a.c.processor.errorhandler', msg: `Failed delivery for exchangeId: ID-${agentName}-1710847200000-0-1. Exhausted after 3 attempts.` }, { timestamp: new Date(now - 5 * MIN).toISOString(), level: 'warn', message: `[o.a.c.processor.errorhandler] Failed delivery for exchangeId: ID-${agentName}-1710847200000-0-1. Exhausted after 3 attempts.` },
{ ts: new Date(now - 8 * MIN).toISOString(), level: 'INFO', logger: 'o.a.c.health.HealthCheckHelper', msg: `Health check [routes] is UP` }, { timestamp: new Date(now - 8 * MIN).toISOString(), level: 'info', message: `[o.a.c.health.HealthCheckHelper] Health check [routes] is UP` },
{ ts: new Date(now - 12 * MIN).toISOString(), level: 'INFO', logger: 'o.a.c.health.HealthCheckHelper', msg: `Health check [consumers] is UP` }, { timestamp: new Date(now - 12 * MIN).toISOString(), level: 'info', message: `[o.a.c.health.HealthCheckHelper] Health check [consumers] is UP` },
{ ts: new Date(now - 15 * MIN).toISOString(), level: 'DEBUG', logger: 'o.a.c.component.kafka', msg: `KafkaConsumer[order-events] poll returned 42 records in 18ms` }, { timestamp: new Date(now - 15 * MIN).toISOString(), level: 'debug', message: `[o.a.c.component.kafka] KafkaConsumer[order-events] poll returned 42 records in 18ms` },
{ ts: new Date(now - 18 * MIN).toISOString(), level: 'INFO', logger: 'o.a.c.impl.engine.InternalRouteStartup', msg: `Route order-enrichment started and consuming from: kafka:order-events` }, { timestamp: new Date(now - 18 * MIN).toISOString(), level: 'info', message: `[o.a.c.impl.engine.InternalRouteStartup] Route order-enrichment started and consuming from: kafka:order-events` },
{ ts: new Date(now - 25 * MIN).toISOString(), level: 'WARN', logger: 'o.a.c.component.http', msg: `HTTP endpoint https://payment-api.internal/verify returned 503 — will retry` }, { timestamp: new Date(now - 25 * MIN).toISOString(), level: 'warn', message: `[o.a.c.component.http] HTTP endpoint https://payment-api.internal/verify returned 503 — will retry` },
{ ts: new Date(now - 30 * MIN).toISOString(), level: 'INFO', logger: 'o.a.c.impl.DefaultCamelContext', msg: `Apache Camel ${agentName} (CamelContext) is starting` }, { timestamp: new Date(now - 30 * MIN).toISOString(), level: 'info', message: `[o.a.c.impl.DefaultCamelContext] Apache Camel ${agentName} (CamelContext) is starting` },
{ ts: new Date(now - 32 * MIN).toISOString(), level: 'INFO', logger: 'org.springframework.boot', msg: `Started ${agentName} in 4.231 seconds (process running for 4.892)` }, { timestamp: new Date(now - 32 * MIN).toISOString(), level: 'info', message: `[org.springframework.boot] Started ${agentName} in 4.231 seconds (process running for 4.892)` },
] ]
} }
function formatLogTime(iso: string): string {
return new Date(iso).toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false })
}
// ── Mock JVM / process info ────────────────────────────────────────────────── // ── Mock JVM / process info ──────────────────────────────────────────────────
function buildProcessInfo(agent: typeof agents[0]) { function buildProcessInfo(agent: typeof agents[0]) {
@@ -144,7 +139,7 @@ export function AgentInstance() {
const logEntries = buildLogEntries(agent.name) const logEntries = buildLogEntries(agent.name)
const filteredLogs = logFilter === 'all' const filteredLogs = logFilter === 'all'
? logEntries ? logEntries
: logEntries.filter((l) => l.level === logFilter.toUpperCase()) : logEntries.filter((l) => l.level === logFilter)
const cpuData = buildTimeSeries(agent.cpuUsagePct, 15) const cpuData = buildTimeSeries(agent.cpuUsagePct, 15)
const memSeries = buildMemoryHistory(agent.memoryUsagePct) const memSeries = buildMemoryHistory(agent.memoryUsagePct)
@@ -289,22 +284,7 @@ export function AgentInstance() {
<SectionHeader>Application Log</SectionHeader> <SectionHeader>Application Log</SectionHeader>
<Tabs tabs={LOG_TABS} active={logFilter} onChange={setLogFilter} /> <Tabs tabs={LOG_TABS} active={logFilter} onChange={setLogFilter} />
</div> </div>
<div className={styles.logEntries}> <LogViewer entries={filteredLogs} maxHeight={360} />
{filteredLogs.map((entry, i) => (
<div key={i} className={styles.logEntry}>
<MonoText size="xs" className={styles.logTime}>{formatLogTime(entry.ts)}</MonoText>
<Badge
label={entry.level}
color={entry.level === 'WARN' ? 'warning' : entry.level === 'ERROR' ? 'error' : entry.level === 'DEBUG' ? 'auto' : 'success'}
/>
<MonoText size="xs" className={styles.logLogger}>{entry.logger}</MonoText>
<span className={styles.logMsg}>{entry.msg}</span>
</div>
))}
{filteredLogs.length === 0 && (
<div className={styles.logEmpty}>No log entries match the selected filter.</div>
)}
</div>
</div> </div>
{/* Timeline */} {/* Timeline */}