用 jq --stream 流式筛选超大 JSON 日志:路径输出、对象重组与限流

举报
yd_217513249 发表于 2026/11/02 09:51:55 2026/11/02
【摘要】 面对超大 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

边界与限制

  1. length == 2 只保留标量叶子值;空对象、空数组只有路径结束标记。如果需要保留空容器,要额外处理 length == 1 的情况。
  2. fromstream 依赖完整的子树流。不要在重组前按“值”过滤叶子,否则重组出的对象会缺少字段;应只按路径前缀过滤。
  3. 用 join(".") 展示路径时,如果键名本身含点号,路径会有歧义。需要精确表达时可输出路径数组,或使用 tojson。
  4. --stream 不会把单个超大字符串拆开;一个超长字符串值仍会作为整体被读取。
  5. jq 的数字处理存在精度边界,超大整数可能不适合直接依赖默认数值表示,业务侧应做校验。
  6. 使用 head 提前关闭管道时,jq 可能因管道中断而以非零状态退出,例如 141;这是管道行为,不是 JSON 解析错误。
  7. truncate_stream、fromstream 的行为请以本机 jq --version 和手册为准,先在样例文件上验证路径层级。
【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。