用 jq --stream 流式筛选超大 JSON 日志:路径输出、对象重组与限流
【摘要】 面对超大 JSON 日志,直接用 jq 过滤会先构建完整值导致内存压力。本文用 --stream 逐条输出路径与值,配合 truncate_stream 与 fromstream 重组匹配对象,并给出限制输出条数的命令与注意事项。
用 jq --stream 流式筛选超大 JSON 日志
问题场景
日志文件可能是一个顶层对象,真正要扫描的是 records 数组。直接执行:
jq '.records[] | select(.level == "ERROR")' app.json
jq 会先把整个文件解析成内存中的值,再执行过滤。文件达到数百 MB 或更大时,内存占用会明显上升。--stream 的思路是让 jq 以流的方式输出“路径 + 叶子值”,过滤表达式逐条处理,避免先构造完整文档。
可运行的最小示例
先准备一个小文件:
cat > sample.json <<'JSON'
{
"records": [
{"id": 1, "level": "INFO", "msg": "start", "meta": {"ip": "10.0.0.1"}},
{"id": 2, "level": "ERROR", "msg": "timeout", "meta": {"ip": "10.0.0.2"}},
{"id": 3, "level": "ERROR", "msg": "retry", "meta": {"ip": "10.0.0.3"}}
],
"tail": {"ok": true}
}
JSON
--stream 输出的每一项通常形如 [路径数组, 叶子值],空对象或空数组还会输出只有路径的结束标记。因此可以用 length == 2 先筛选出叶子值。
逐条输出路径与值
jq -c --stream '
select(length == 2)
| {
path: (.[0] | map(tostring) | join(".")),
value: .[1]
}
' sample.json
输出示例:
{"path":"records.0.id","value":1}
{"path":"records.0.level","value":"INFO"}
{"path":"records.1.level","value":"ERROR"}
如果只关心某些路径,可以在流上继续筛选,例如只取 records 下的 level:
jq -c --stream '
select(length == 2)
| select(.[0][0] == "records" and .[0][-1] == "level")
| {path: (.[0] | map(tostring) | join(".")), value: .[1]}
' sample.json
用 fromstream 重组匹配对象
只按路径筛叶子值,无法根据同一个对象里的 level 字段决定是否保留整条记录。这时可以先把 records 的每个元素重组出来,再对对象做 select:
jq -c -n --stream '
limit(2;
fromstream(
2 | truncate_stream(
inputs
| select((.[0] | length) >= 2)
| select(.[0][0] == "records")
)
)
| select(.level == "ERROR")
)
' sample.json
这里的关键是 2 | truncate_stream(...):先去掉 records 和数组下标两层路径,让每个数组元素成为 fromstream 的根;limit(2; ...) 表示最多输出两条匹配记录。运行后应得到两条 level 为 ERROR 的对象。
限制输出条数
如果只想快速查看前若干条流式结果,可以直接用 head:
jq -c --stream '
select(length == 2)
| {path: (.[0] | map(tostring) | join(".")), value: .[1]}
' sample.json | head -n 20
也可以在 jq 表达式内部使用 limit,减少后续输出:
jq -c --stream '
limit(20;
select(length == 2)
| {path: (.[0] | map(tostring) | join(".")), value: .[1]}
)
' sample.json
边界与限制
length == 2只保留标量叶子值;空对象、空数组只有路径结束标记。如果需要保留空容器,要额外处理length == 1的情况。fromstream依赖完整的子树流。不要在重组前按“值”过滤叶子,否则重组出的对象会缺少字段;应只按路径前缀过滤。- 用
join(".")展示路径时,如果键名本身含点号,路径会有歧义。需要精确表达时可输出路径数组,或使用tojson。 --stream不会把单个超大字符串拆开;一个超长字符串值仍会作为整体被读取。- jq 的数字处理存在精度边界,超大整数可能不适合直接依赖默认数值表示,业务侧应做校验。
- 使用
head提前关闭管道时,jq 可能因管道中断而以非零状态退出,例如 141;这是管道行为,不是 JSON 解析错误。 truncate_stream、fromstream的行为请以本机jq --version和手册为准,先在样例文件上验证路径层级。
【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)