From 6364a702159c77060b21d410e55b0bb790e5330b Mon Sep 17 00:00:00 2001 From: opsx Date: Wed, 28 Oct 2026 16:18:48 +0000 Subject: [PATCH] =?UTF-8?q?tune(logs):=20=E6=AF=8F=E9=A1=B5=E6=9D=A1?= =?UTF-8?q?=E6=95=B0=E9=BB=98=E8=AE=A4=201000=E2=86=92200=EF=BC=8C?= =?UTF-8?q?=E4=B8=8A=E4=B8=8B=E6=96=87=E9=BB=98=E8=AE=A4=205=20=E6=9D=A1?= =?UTF-8?q?=EF=BC=88=E4=BE=9D=E6=8D=AE=E5=AE=9E=E6=B5=8B=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit === 一、每页条数:1000 太多,实测依据 === 1. 每次查询响应体 671 B/条 → 1000 条 = **655 KB** 2. 前端结果表**没有虚拟滚动**(无 react-window / tanstack virtual), 1000 行会全部渲染成 DOM 节点 → 滚动卡顿 3. 后端 maxLogQueryLimit 本来就是 1000,我上一版 UI 给了 2000/5000 选项 是"假选项"(会被静默收敛成 1000),本次去掉 4. 密度参考:cex-ipublic-gateway 每天 3.28 亿条 ≈ **3800 条/秒**, 1000 条只覆盖 0.26 秒 —— 靠翻页浏览无意义,应靠时间范围/LogQL 收窄 调整:默认 1000 → **200**(后端自身默认值,约 131KB);选项 [100/200/500/1000] 提示文案改为"日志量大时先收窄时间范围或加过滤,比调大每页条数更有效" === 二、上下文档数:1 条不够,默认用 5 === 实测一个 SpringBoot 异常被拆成 **4 条独立日志**: 16:15:35.259 [reactor-http-epoll-3] ERROR ...AbstractErrorWebExceptionHandler org.springframework.web.reactive.resource.NoResourceFoundException: 404 NOT_FOUND Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: *__checkpoint ⇢ HTTP HEAD "/" [ExceptionHandlingWebHandler] → 前后各 1 条(共 2 条)连一个完整异常都拼不出来 → 前后各 5 条(共 10 条)能看到"异常 + 前后业务日志",是排障的最小可用集 → 完整 Java 堆栈(20+ 帧)需要 20-50 条 调整:默认 2 → **5**;选项增加 1 → [1/2/5/10/20/50] 镜像:lghost/lokistack:latest + v0.1 = sha256:2bcc86b8e30d --- backend/services/log_query.go | 3 +++ frontend/src/i18n/locales/en/logs.ts | 2 +- frontend/src/i18n/locales/zh/logs.ts | 2 +- .../src/pages/logs/components/LogContextDialog.tsx | 2 +- frontend/src/pages/logs/index.tsx | 10 ++++++---- 5 files changed, 12 insertions(+), 7 deletions(-) diff --git a/backend/services/log_query.go b/backend/services/log_query.go index 2c4d16f..4e027c1 100644 --- a/backend/services/log_query.go +++ b/backend/services/log_query.go @@ -23,6 +23,9 @@ import ( "lokistack/repository" ) +// maxLogQueryLimit 单页硬上限。依据:实测每条日志约 671B—— +// 1000 条 ≈ 655KB 响应;而前端结果表**没有虚拟滚动**,1000 行会全部渲染成 +// DOM 节点导致卡顿。前端下拉只给到 1000,超过会被这里静默收敛。 const maxLogQueryLimit = 1000 const logQueryTimeout = 120 * time.Second diff --git a/frontend/src/i18n/locales/en/logs.ts b/frontend/src/i18n/locales/en/logs.ts index c09308a..4336548 100644 --- a/frontend/src/i18n/locales/en/logs.ts +++ b/frontend/src/i18n/locales/en/logs.ts @@ -20,7 +20,7 @@ export default { sourceAria: 'Data source', sourceEmptyOption: 'No data sources available', limitLabel: 'Per page', - limitHint: 'Up to 1000 entries per page; paging keeps only the current page.', + limitHint: 'Default 200 entries (~0.7KB each). For very dense logs, narrow the time range or add filters instead of raising the page size.', rangeInvalid: 'Select a valid start and end time', queryFailed: 'Query failed', }, diff --git a/frontend/src/i18n/locales/zh/logs.ts b/frontend/src/i18n/locales/zh/logs.ts index 58a2e97..56f6e71 100644 --- a/frontend/src/i18n/locales/zh/logs.ts +++ b/frontend/src/i18n/locales/zh/logs.ts @@ -19,7 +19,7 @@ export default { sourceAria: '数据源', sourceEmptyOption: '暂无可用数据源', limitLabel: '每页条数', - limitHint: '每页最多 1000 条,翻页仅保留当前页。', + limitHint: '默认 200 条(每条约 0.7KB)。日志量很大时先收窄时间范围或加过滤条件,比调大每页条数更有效。', rangeInvalid: '请选择有效的起止时间', queryFailed: '查询失败', }, diff --git a/frontend/src/pages/logs/components/LogContextDialog.tsx b/frontend/src/pages/logs/components/LogContextDialog.tsx index 4312dc5..921ecc9 100644 --- a/frontend/src/pages/logs/components/LogContextDialog.tsx +++ b/frontend/src/pages/logs/components/LogContextDialog.tsx @@ -41,7 +41,7 @@ export function LogContextDialog({ context, terms, lines, onLinesChange, onClose diff --git a/frontend/src/pages/logs/index.tsx b/frontend/src/pages/logs/index.tsx index 3ea23fe..e9924e5 100644 --- a/frontend/src/pages/logs/index.tsx +++ b/frontend/src/pages/logs/index.tsx @@ -68,7 +68,9 @@ export default function LogsPage() { const [executedRange, setExecutedRange] = useState<{ start: number; end: number } | null>(null) const [start, setStart] = useState(() => toLocal(new Date(Date.now() - 300000))) const [end, setEnd] = useState(() => toLocal(new Date())) - const [limit, setLimit] = useState(1000) + // 默认 200:实测 671B/条,200 条约 131KB、DOM 200 行渲染顺畅。 + // 后端 maxLogQueryLimit=1000,所以下拉不给 2000/5000(否则静默截断成 1000,是假选项) + const [limit, setLimit] = useState(200) const [queryHistory, setQueryHistory] = useState(readQueryHistory) const [entries, setEntries] = useState([]) const [searched, setSearched] = useState(false) @@ -198,10 +200,10 @@ export default function LogsPage() { return () => window.clearInterval(timer) }, [tail, sourceId, query, limit]) - /** 上下文前后条数:默认 2 条(原来写死 5),选择后记住,弹窗内可随时改 */ + /** 上下文前后条数:默认 5 条(Java 一个异常常拆成 4 条日志,2 条拼不完整),选择后记住 */ const [contextLines, setContextLines] = useState(() => { const saved = Number(localStorage.getItem('logContextLines')) - return saved > 0 && saved <= 50 ? saved : 2 + return saved > 0 && saved <= 50 ? saved : 5 }) /** 记住当前查看上下文的那一行,切换条数时原地重新拉取 */ const [contextRow, setContextRow] = useState(null) @@ -273,7 +275,7 @@ export default function LogsPage() {