lnav 是一个终端里的日志浏览器。打开一个日志文件,它自动识别格式、给每行上色、把时间戳对齐,然后按 ; 就能用 SQL 查这份日志。

拿 33.4 MB、20 万行的 nginx 访问日志做实测:索引花 2.17 秒,之后一条分组聚合的 SQL 1.49 秒出结果。同一件事写成 Python 脚本只要 0.34 秒,但那是两码事。

27 MB 的 exe 加一个 dll

lnav -V
lnav 0.14.1-dirty

bin\lnav.exe        26868000 字节
bin\msys-2.0.dll    19917272 字节

整个目录 45 MB,两个文件就能跑。msys-2.0.dll 是 MSYS2 的运行时,lnav 在 Windows 上靠它提供 POSIX 环境。没有安装程序,解压放进 PATH 或直接写全路径都行。

格式识别是自动的。nginx 访问日志、syslog、JSON 日志、Java 堆栈,它内置了几十种格式,打开就知道该怎么切字段。识别不出来的也能用正则自己定义。

20 万行日志,索引 2.17 秒

lnav -n -c :quit access.log
项目数值
日志体积33406826 字节
日志行数200000 行
建索引耗时2.17 秒
SQL 聚合耗时1.49 秒
Python 脚本对照0.34 秒
lnav.exe26868000 字节
msys-2.0.dll19917272 字节
目录总体积45 MB
索引速度15.4 MB/s
lnav 相对 Python 脚本4.4 倍

2.17 秒是「打开文件到可以开始查」的时间,它在这期间解析每行的时间戳并按列建好索引。索引会缓存在本地,关掉再打开就快得多。

200000 行、33406826 字节摊下来是每秒 15.4 MB。这个速度对日常排查够用:几十 MB 的日志点开就能看,不用先去 grep 再 grep。

按一个键就用 SQL 查

;SELECT sc_status, count(*) AS n, sum(sc_bytes) AS bytes
 FROM access_log GROUP BY sc_status ORDER BY n DESC
状态码请求数响应字节
2001558633776902037
304180250
404160768494884
30160610
42920181068130
50019571035803

lnav 把日志行当成表,字段名来自格式定义:nginx 日志里就有 sc_status、sc_bytes、cs_method、cs_uri_stem 这些。不用自己建表,也不用导入,打开文件就能查。

同样的查询写成命令行也行:lnav -n -c "SELECT ..." access.log,结果直接打到 stdout,适合放进脚本。

跟写脚本比,它慢但省事

同一份日志、同一个聚合,Python 逐行正则 0.34 秒,lnav 1.49 秒。原因不复杂:lnav 每次启动都要重新索引 33406826 字节,而 Python 只是顺序读一遍。

所以在自动化脚本里,处理固定格式的日志还是写代码快。lnav 的位置在另一头:来不及写脚本、或者根本不知道要查什么的时候。

排查现场通常是后者。日志里冒出一堆 500,想先看是哪些 URL、集中在哪个时间段、有没有共同的 UA,这些查询各写一遍脚本要十几分钟,在 lnav 里就是连按几次 ; 改 WHERE。

几个真用得上的操作

; 打开 SQL 输入框,/ 是正则过滤,i 切到直方图视图,e 和 E 在错误行之间前后跳。上面那条聚合查询从按下 ; 到出结果用 1.49 秒,其中大头是首次索引的 2.17 秒已经在打开文件时付过了。

直方图视图把 200000 行按时间切桶统计,柱子突然高一截的地方就是问题开始的时间点,回车跳到对应的行。20 万行摊到 24 小时,平均每小时 8333 行,某个桶突然翻几倍就说明那会儿出了状况。

底部状态栏还会提示「Press e/E to move forward/backward through error messages」,这是它已经扫到了 1957 条 500 响应之后给的入口,不用自己敲正则搜 ERROR。

两个会直接退出的情况

在 TERM=dumb 的环境里启动,lnav 打不开界面,直接退出:

error: unable to open TUI
reason: unable to initialize notcurses No such file or directory
help: The TERM value of "dumb" is not known.

如果在 TERM=dumb 的环境里启动,lnav 会报错退出,一个日志字符都渲染不出来。脚本里调用必须先设 TERM=xterm-256color,CI 容器和部分 SSH 会话默认就是 dumb。

另一个是文件路径。传 Windows 绝对路径 D:\logs\access.log 会被当成 URL,报 no defined handler for URL scheme: d。先进到文件所在目录用相对路径,或者改成正斜杠写法。