=== 1. 界面显示裸 key(trend.subtitle / daily.subtitle / table.subtitle)===
根因:中文语言包只加了 title,**没有 subtitle**,而英文包有。
i18next 找不到 key 就把 key 原样输出,所以界面上出现 "trend.subtitle" 这种文字。
已补齐 zh 的 trend/stream/pie/daily/table 五个 subtitle,并把文案改成
人话(说明数据来源与是否需要等待),例如:
trend : 按时间分桶的日志量(来自索引统计,秒级返回)
daily : 近 7 个自然日的每日总量(来自索引统计,不受上方时间范围影响)
table : 按日志条数排前 10 的应用,点「查看日志」跳到查询页
=== 2. 分层加载:上面的先出,下面滚到再算 ===
新增 hooks/useInView.ts(IntersectionObserver,提前 200px 触发,只触发一次)。
- 首屏只跑索引类查询(histogram + index/stats),**合计 <100ms**
- 「每日日志量」「应用排行」「Ingress 分析」三块滚动到视口才发查询
- 排行卡片加载中显示"正在按应用统计(需读取日志内容,通常 20~30 秒)…",
不再是空表格让人以为卡死
=== 3. 修复自己引入的单位混用(重要)===
趋势图数据源从 count_over_time 换成 index/volume_range 后,返回值是**字节**,
但排行表里的值是**条数**,两者混用导致:
- 统计卡显示 7956179750 这种看不懂的裸数字
- 排行占比算成 0.0x%(分子条数、分母字节)
修法:
- 统计卡拆成两张且都走索引接口(实测 histogram 50ms / index-stats 43ms):
日志条数(索引统计) ← index/stats entries,精确条数
日志体积(索引统计) ← volume_range 累计,格式化为 GB
- 排行占比分母改用 apps 自身条数合计
- 去掉"告警行数"卡片:为了把重查询从 3 条降到 2 条已放弃全局 warn 统计,
显示一个恒为 0 的数字比不显示更误导
=== 4. Loki 查询方式实测对比(15 分钟 astralx,370GB/天)===
直接打 Loki,逐个验证卡在哪:
sum(count_over_time({ns}[30s])) step=30s 61s 超时 ← 全集群全局
sum by (app,ns)(count_over_time([30s])) step=30s 19.7s 5.5GB ← 现用
sum by (app,ns)(count_over_time([15m])) step=15m 47s 超时 ← 反直觉!
sum by (app)(count_over_time([30s] |= error)) step=30s 12.7s 5.5GB
单 app gateway count_over_time step=30s 12.6s 0.58GB
index/volume (limit=200) 2.2s ← 只给 selector 级,不能按 app
index/volume_range (step=60s) 133ms
index/stats 112ms ← entries/bytes 精确
两个结论(写进注释,避免以后又"优化"回去):
a) **不要把 range 和 step 一起放大**:`[15m]` + step=15m 看着"点数少",
实际要让 Loki 为单个 evaluate 点加载 15m 的 chunk,比 [30s]+step=30s 慢 2 倍以上
(47s 超时 vs 19.7s)。
b) **按 app 分组无法走索引**:Loki 3.4 的 index/volume 只回落到 selector 标签,
不支持 aggregateBy=app(传了也无效果)。所以 per-app 排行只能读日志内容,
20 秒左右是这台集群的物理下限 —— 只能靠懒加载 + 明确提示来改善体感。
=== 实测(v8-1028-2232 / 7a05683a91ae)===
logs/histogram 50ms logs/stats 43ms
by-app 排行 25077ms(懒加载,滚动到才发)
中文 subtitle 已进包:来自索引统计 / 按量排前 / 各命名空间的日志量占比 /
近 7 个自然日 / 按日志条数排前 10 各命中 1 次
40 lines
1.3 KiB
TypeScript
40 lines
1.3 KiB
TypeScript
import { useEffect, useRef, useState } from 'react'
|
|
|
|
/**
|
|
* useInView 判断元素是否进入视口(默认提前 200px 触发)。
|
|
*
|
|
* 用途:分析页里"按应用统计"这类必须读日志内容的查询很贵(实测大集群
|
|
* 15 分钟约 20~30 秒)。一次性全查会让首屏一起等,用户看到的是整页转圈。
|
|
* 改成滚动到该卡片附近才发查询:上面的总量/趋势先出来,下面的慢数据后补。
|
|
*
|
|
* 只触发一次(进入视口后断开监听):日志分析是只读快照,不需要来回重算。
|
|
*/
|
|
export function useInView<T extends HTMLElement>(rootMargin = '200px') {
|
|
const ref = useRef<T | null>(null)
|
|
const [inView, setInView] = useState(false)
|
|
|
|
useEffect(() => {
|
|
if (inView) return
|
|
const el = ref.current
|
|
if (!el) return
|
|
// 老浏览器/SSR 环境没有 IntersectionObserver:直接当作可见,别把功能卡死
|
|
if (typeof IntersectionObserver === 'undefined') {
|
|
setInView(true)
|
|
return
|
|
}
|
|
const observer = new IntersectionObserver(
|
|
entries => {
|
|
if (entries.some(entry => entry.isIntersecting)) {
|
|
setInView(true)
|
|
observer.disconnect()
|
|
}
|
|
},
|
|
{ rootMargin },
|
|
)
|
|
observer.observe(el)
|
|
return () => observer.disconnect()
|
|
}, [inView, rootMargin])
|
|
|
|
return { ref, inView }
|
|
}
|