JSONL 日志的三个隐形坑:BOM、半行、和按字节轮转

JSONL(每行一个 JSON)是做日志的省事选择:追加写、流式读、不用把整个文件读进内存。但它有三个坑,平时不疼,出事时正好是你最需要日志的时候。

坑一:BOM 和半行,让"读日志"这件事先崩掉

用带 BOM 的 UTF-8 写文件,第一行会多出 \ufeff,json.loads 直接抛异常。Python 里读日志统一用 encoding="utf-8-sig",写出时确认用 utf-8。

更常见的是半行:进程被强杀时,最后一行往往只写了一半。如果读取代码写成"整体读入再逐行解析,出错就 return []",那么一次半行就能让你看到零条日志------排查时比没有日志更误导。正确做法是逐行解析,坏行计数并跳过:

python 复制代码
ok = bad = 0
with open(path, encoding="utf-8-sig", errors="replace") as f:
    for line in f:
        line = line.strip()
        if not line:
            continue
        try:
            rec = json.loads(line)
        except json.JSONDecodeError:
            bad += 1
            continue
        ok += 1

关键不是 try,而是把 bad 打出来。静默跳过坏行,等于把"日志被截断"这个事实藏起来。

坑二:多进程同时追加

在 Linux 上,小于 PIPE_BUF(通常 4096 字节)的单次 write 到以 O_APPEND 打开的文件是原子的。超过这个大小就可能交错,两行互相穿插,得到的 JSON 全部报废。Windows 对追加写没有同样的保证。

三种实用做法,按成本排序:单写者 (所有日志打到队列,由唯一进程落盘,最省心)、按进程分片 (app_{pid}.jsonl,读的时候合并,零锁)、文件锁 (不要自己发明,用 filelock 之类的库)。

坑三:轮转时按字节切

日志轮转最常见的写法是"文件超过 10MB 就切"。按字节切会把某一行劈成两半:前半留在旧文件末尾,后半落到新文件开头,两侧都变成坏行。

按行轮转即可:读时累计字节数,超过阈值就在行边界切换输出文件。这样每个文件都是完整的 JSONL。

一张检查清单

  • 读:utf-8-sig + 逐行 try + 坏行计数上报
  • 写:单写者,或按 pid 分片,不要把锁逻辑写进业务代码
  • 转:按行轮转,不按字节
  • 删:明确保留策略,别让日志把磁盘吃满
  • 验:新加一条日志后,回头真的读一遍,确认能解析

最后一句经验:日志系统的正确性,从来不体现在写的时候,而体现在出故障那天你能不能把最后几行读出来。


相关推荐
追光者491 小时前
重试是解药还是毒药?聊聊幂等性
ai编程
打呵欠的猫1 小时前
用 AI 做性能分析:一行命令找出拖慢页面的 5 个组件,优化后首屏快了 40%
前端·性能优化·ai编程
god_cvz2 小时前
第二章:6、对话记忆与引用标注
后端·ai编程
用户125758524362 小时前
Golang后台管理框架推荐:XYGo Admin与devinggo怎么选?
后端·go·ai编程
陽清AI一人企业2 小时前
30岁程序员别再卷算法,转行最缺的是能驻场的活
ai编程·创业
用户842558130872 小时前
退出码 0、日志干净,但一件事也没干成:自动化脚本的 9 种静默失败
ai编程
单向箔2 小时前
08|一套可复制的 AI Coding 流程:从需求到提交怎么走
人工智能·prompt·ai编程
自律懒人2 小时前
openJiuwen 企业级 AgentOS 实测:4 层架构 + 5 步部署,多智能体协作省 60% Token
ai编程
熊出没3 小时前
从Copilot到Agent:AI编程工作流的重构与落地实践
软件工程·copilot·ai编程·ai agent