搜索页用法与 LogsQL 速查
2026-10-11 · 适用于 Osmanthe 开发预览版 · 查询语言是 VictoriaLogs 的 LogsQL
1. 为什么之前只看到 200 条
旧页面每次请求都写死了 limit=200,而且只显示“返回了多少条”,没有显示“一共匹配多少条”,看起来就像只有 200 条。数据一直都在,用 * | stats count() 可以看到 demo 租户里有五百多万条。
现在的做法和 Splunk、Kibana 一样:
- 状态栏显示匹配总数(例如“匹配 5,907,081 条 · 已显示 100 条”),总数由单独的计数接口算出,不受每页条数限制
- 每页条数可选 50 / 100 / 200 / 500,底部“加载更多”继续往下翻
- 最多翻到第 100,000 条。再往后不是好的查法:点分布图里的柱子,把时间范围缩小到那一段,再看
2. 搜索页怎么用
地址:https://coding.lab.sigmax-group.com,用 demo 租户的账号登录(账号由管理员开)。默认时间范围是“最近 24 小时”。
| 功能 | 怎么用 |
|---|---|
| 语法提示 | 点搜索框旁边的“语法”,弹出常用写法,点任意一个例子就直接搜 |
| 时间分布图 | 搜索后出现柱状图,每根柱子是一个时间段的条数。电脑上鼠标移上去看具体数字;点一根柱子,时间范围就缩小到这一段 |
| 列表视图 | 每条日志一张卡片:时间、级别、服务、消息。点“全部字段”展开所有字段 |
| 表格视图 | 一行一条,适合横向对比。手机上可以左右滑动。统计类查询(带 stats)会自动用表格显示 |
| JSON 视图 | 每行一条原始 JSON,方便复制给开发或贴进工单 |
| 选列 | 点“字段”,列出这次结果里所有字段和出现次数。勾选的字段会成为表格里的一列,选择会记在浏览器里,下次还在。“恢复默认列”可以还原 |
| 看字段的常见值 | 在“字段”面板里点字段名,显示出现最多的 10 个值和条数 |
| 一键加过滤 | 字段值旁边的 + 表示“只看这个值”,− 表示“排除这个值”,会自动加到查询里并重新搜索。展开日志的“全部字段”里也有这两个按钮 |
| 时区 | 默认按手机/电脑的本地时间显示,勾选 UTC 改为 UTC |
| 分享查询 | 查询和时间范围会写进网址 # 后面,把网址发给别人(对方要有 API Key)就能打开同一个搜索 |
3. 显示形式能改吗
能改,这一层完全是我们自己的代码,和 VictoriaLogs 无关。这次已经做了三种视图和自选列。后面还可以按需要加,例如:
- 关键词高亮、按级别给整行上色
- 保存常用查询和列配置,按租户或用户共享
- 点某条日志看前后上下文(同一个 host / trace_id 前后 N 条)
- 按
trace_id串起一次请求经过的所有服务 - 统计结果直接画成折线图或柱状图,再进一步就是仪表盘
你想先要哪种,告诉我就行。
4. LogsQL 速查
4.1 找日志(过滤)
| 写法 | 意思 |
|---|---|
* | 全部 |
timeout | 消息里有单词 timeout。按整词匹配,time 匹配不到 timeout;区分大小写 |
i(TIMEOUT) | 不区分大小写 |
"card declined" | 精确短语 |
*clined* | 子串匹配(慢一些,数据量大时少用) |
~"timeout|refused" | 正则 |
level:=ERROR | 字段完全等于某值 |
service:payment | 字段里有这个单词(: 是按词匹配,:= 是完全相等) |
http.path:"/api/pay"* | 前缀匹配 |
service:in(payment, checkout) | 等于其中任意一个 |
http.status_code:>=500 | 数值比较,也可以用 > < <= |
duration_ms:range[1000, 5000] | 数值区间(含两端) |
client.ip:ipv4_range(10.0.0.0/8) | IP 段 |
error.type:* | 有这个字段(值不为空) |
{host="payment-1"} | 按日志流过滤,最快。HEC 写入时 index、sourcetype、host、source 是流字段;OTLP 写入时 service.name、host.name 等是流字段 |
_time:1h | 最近 1 小时(和页面选的时间范围取交集) |
4.2 组合条件
| 写法 | 意思 |
|---|---|
service:=payment level:=ERROR | 空格就是 AND |
service:=payment AND level:=ERROR | 同上,写明 AND |
"timeout" OR "refused" | 或 |
failed -service:=gateway | 排除,-、!、NOT 都可以 |
(service:=payment OR service:=user) level:=ERROR | 括号分组 |
4.3 统计和加工(管道 |)
过滤条件后面接 |,把结果交给下一步处理,和 Splunk 的写法很像。
| 写法 | 意思 |
|---|---|
level:=ERROR | stats by (service) count() as n | sort by (n desc) | 每个服务的错误数,从多到少 |
* | stats by (http.status_code) count() as n | 状态码分布 |
* | stats by (service) avg(duration_ms) as avg_ms, quantile(0.99, duration_ms) as p99 | 平均和 P99 延迟(分位数是近似值,每次可能略有差别) |
* | stats by (service) count() as total, count() if (level:=ERROR) as errors | math errors*100/total as pct | sort by (pct desc) | 每个服务的错误率(%) |
service:=payment | stats by (_time:1h) count() as n | sort by (_time desc) | 按小时统计条数 |
* | stats by (service) count() as n | filter n:>40000 | 统计后再过滤 |
level:=ERROR | top 5 by (error.type) | 最常见的 5 种错误 |
* | stats count_uniq(user.id) as users | 不同用户数 |
* | fields _time, service, user.id, _msg | 只保留这几个字段 |
level:=ERROR | sort by (duration_ms desc) | 按耗时从大到小 |
sort、stats、top、uniq、limit 这类决定顺序的管道,就按你写的顺序返回,网关不会再按时间重排。如果只用了 fields 这类管道又想按时间排,自己加 | sort by (_time desc)。
4.4 从 Splunk SPL 过来
| Splunk SPL | LogsQL |
|---|---|
index=main error | {index="main"} error |
status=500 | status:=500 |
status>=500 | status:>=500 |
NOT host=web-1 | -host:=web-1 |
| stats count by service | | stats by (service) count() |
| stats dc(user) as users | | stats count_uniq(user) as users |
| stats perc95(duration) | | stats quantile(0.95, duration) |
| timechart span=1h count | | stats by (_time:1h) count() |
| top 5 error_type | | top 5 by (error_type) |
| table _time host msg | | fields _time, host, _msg |
| sort -count | | sort by (count desc) |
| where n > 100 | | filter n:>100 |
| eval pct=errors*100/total | | math errors*100/total as pct |
| head 10 | | limit 10 |
SPL 兼容(让客户直接写 SPL)还没做,这张表就是将来翻译器要做的事情的大致范围。
4.5 常见坑
:和:=不一样:service:pay搜不到 payment,因为:是整词匹配。要前缀用service:"pay"*- 单词搜索区分大小写:
Timeout搜不到 timeout,用i(Timeout) - 字段名有点号直接写,例如
user.id:=u00042;字段名里有空格或特殊字符要加双引号 - 消息字段叫
_msg,时间叫_time - 查询报错时,页面会显示 VictoriaLogs 返回的原始错误,通常会指出出错的位置
完整语法:VictoriaLogs 官方文档 docs.victoriametrics.com/victorialogs/logsql/(英文)。
5. 用接口查(curl)
网页能做的,接口都能做。先登录拿令牌(API Key 只能写日志,不能查),令牌放在 X-API-Key 头里:
# 登录,12 小时不用就失效
TOKEN=$(curl -s https://域名/api/v1/login -H 'Content-Type: application/json' \
-d '{"username":"你的用户名","password":"你的密码"}' | python3 -c 'import sys,json; print(json.load(sys.stdin)["token"])')
# 第 2 页,每页 100 条
curl -G https://域名/api/v1/search -H "X-API-Key: $TOKEN" \
--data-urlencode 'q=level:=ERROR' -d limit=100 -d offset=100 \
-d start=2026-10-10T00:00:00Z
# 总数和每小时分布
curl -G https://域名/api/v1/hits -H "X-API-Key: $TOKEN" \
--data-urlencode 'q=level:=ERROR' -d step=1h -d start=2026-10-10T00:00:00Z
# 字段列表 / 某字段最常见的值
curl -G https://域名/api/v1/fields -H "X-API-Key: $TOKEN" --data-urlencode 'q=*'
curl -G https://域名/api/v1/field_values -H "X-API-Key: $TOKEN" -d field=service -d limit=10
现在的网址有一层密码保护,从外部用 curl 要先过那层认证;在服务器本机可以直接用 http://127.0.0.1:18101。
6. 演示数据
为了让上面的例子能查到东西,用 tools/demo_data.py 往 demo 租户写了 20 万条模拟的业务日志,分布在 2026-10-10 晚到 10-11 白天的 24 小时里,白天多、夜里少。另外还有之前压测留下的约 570 万条(内容基本都是 “the message”,service.name 是 tg-load 等),选“全部”时会混在一起。
| 字段 | 例子 | 说明 |
|---|---|---|
service | payment | checkout / payment / user / search / gateway;payment 的错误率故意设得高一些(约 6%) |
level | ERROR | DEBUG / INFO / WARN / ERROR |
http.method http.path http.status_code | POST /api/pay 502 | |
duration_ms | 165.6 | 耗时,出错时明显变长 |
user.id | u00042 | 约 2 万个不同用户 |
trace_id | 32 位十六进制 | 每条都不同,高基数 |
client.ip region | 10.119.83.49 cn-hongkong | |
error.type | timeout | 只有 ERROR 有。注意:这批数据里的值是从错误信息取的第一个词(如 upstream、connect()),之后已把生成器改成规范的类型名(timeout、card_declined 等),重新灌数据后生效 |
| 流字段 | index=demo, sourcetype=app/nginx, host=payment-1 | 可以用 {index="demo"} 只看演示数据 |
想再灌一批:python3 tools/demo_data.py --events 200000 --hours 24。