import React, { useEffect, useMemo, useState } from 'react' import { api } from '../services/api' import './LogMonitor.css' const GROUPS = [ { key: 'error', label: '错误' }, { key: 'warning', label: '警告' }, { key: 'info', label: '信息' }, ] const LEVELS = ['', 'ERROR', 'CRITICAL', 'WARNING', 'INFO'] const SERVICES = ['', 'backend', 'trading_system'] function formatCount(item) { const c = Number(item?.count || 1) return c > 1 ? `×${c}` : '' } export default function LogMonitor() { const [items, setItems] = useState([]) const [loading, setLoading] = useState(false) const [error, setError] = useState('') const [group, setGroup] = useState('error') const [overview, setOverview] = useState(null) const [saving, setSaving] = useState(false) const [pageStart, setPageStart] = useState(0) const [pageMeta, setPageMeta] = useState({ key: '', llen_total: 0, has_more: false, next_start: 0 }) const [limit, setLimit] = useState(200) const [service, setService] = useState('') const [level, setLevel] = useState('') const [autoRefresh, setAutoRefresh] = useState(true) const [refreshSec, setRefreshSec] = useState(5) const params = useMemo(() => { const p = { limit: String(limit), group, start: String(pageStart) } if (service) p.service = service if (level) p.level = level return p }, [limit, service, level, group, pageStart]) const loadOverview = async () => { try { const res = await api.getLogsOverview() setOverview(res) } catch (e) { // 概览失败不阻塞日志列表 } } const load = async () => { setLoading(true) setError('') try { const res = await api.getSystemLogs(params) setItems(res?.items || []) setPageMeta({ key: res?.key || '', llen_total: Number(res?.llen_total || 0), has_more: !!res?.has_more, next_start: Number(res?.next_start || 0), }) await loadOverview() } catch (e) { setError(e?.message || '获取日志失败') } finally { setLoading(false) } } useEffect(() => { load() // eslint-disable-next-line react-hooks/exhaustive-deps }, [params]) useEffect(() => { if (!autoRefresh) return const sec = Number(refreshSec) if (!sec || sec <= 0) return const t = setInterval(() => load(), sec * 1000) return () => clearInterval(t) // eslint-disable-next-line react-hooks/exhaustive-deps }, [autoRefresh, refreshSec, params]) const maxLen = overview?.config?.max_len || {} const enabled = overview?.config?.enabled || {} const llen = overview?.stats?.llen || {} const addedToday = overview?.stats?.added_today || {} const day = overview?.stats?.day || '' const [maxLenDraft, setMaxLenDraft] = useState({ error: 2000, warning: 2000, info: 2000 }) useEffect(() => { if (maxLen?.error || maxLen?.warning || maxLen?.info) { setMaxLenDraft({ error: Number(maxLen.error || 2000), warning: Number(maxLen.warning || 2000), info: Number(maxLen.info || 2000), }) } }, [maxLen?.error, maxLen?.warning, maxLen?.info]) const saveConfig = async () => { setSaving(true) setError('') try { await api.updateLogsConfig({ max_len: maxLenDraft }) await loadOverview() } catch (e) { setError(e?.message || '更新日志配置失败') } finally { setSaving(false) } } const writeTest = async () => { setError('') try { await api.writeLogsTest() await load() } catch (e) { setError(e?.message || '写入测试日志失败') } } const goFirstPage = () => setPageStart(0) const goNextPage = () => setPageStart(pageMeta?.next_start || 0) const goPrevPage = () => setPageStart(Math.max(0, pageStart - Number(limit || 200))) return (
{pageMeta.key || '-'}
{it.exc_text}