tail 与实时日志
tail 默认输出文件末尾 10 行,加 -f 后持续跟踪新写入的内容。跟随的实现方式决定了它与轮转的关系:-f 记住的是已打开的文件描述符,logrotate 把日志改名或删除重建后,它仍停在旧 inode 上;-F 等价于 --follow=name --retry,会按文件名重新打开,文件被轮转或短暂消失后都能接上,因此排障脚本里应默认使用 -F。
基本信息
| 适用平台 | Linux 全发行版与 macOS(coreutils / BSD tail);-F 在 GNU 与 BSD 上均可用 |
|---|---|
| 命令语法 | tail [选项] 文件... |
| 常用选项 | -n 行数(-n +N 表示从第 N 行到末尾)、-c 字节数、-f 跟随描述符、-F 跟随文件名并在失败后重试、--pid 指定进程退出后自动结束、-q 多文件时不打印文件名头、-v 明确标注文件名 |
| 权限要求 | 读取日志需要文件读权限;/var/log 下多数文件只对 root 与 adm、systemd-journal 组开放,普通用户需要 sudo 或加入相应组 |
| 运行特征 | tail 只 seek 到文件末尾,不读取全文,因此几十 GB 的日志也能瞬间显示最后几行 |
典型使用场景
- 复现问题:一个终端 tail -F 盯着日志,另一个终端触发操作,观察报错出现的时序
- 发布验证:应用重启后跟一段时间日志,确认启动完成且没有异常堆栈
- 止血期间观察:故障处理中持续跟踪错误日志,判断修复是否生效
- 多文件对照:同时跟进应用日志与 Nginx 日志,判断请求在哪一层断掉
- 按内容过滤:管道接 grep 实时筛出关键字,减少噪音
- 读取尾部片段:不跟随时只取最后几百行,用于事后复盘
常用示例
常用示例
tail -n 200 /var/log/nginx/error.log
tail -F /var/log/myapp/app.log # 轮转后自动接上新文件
tail -F app.log | grep --line-buffered -i error
tail -F -n 0 app.log # 只要新产生的日志
tail -n +1 access.log | head # 从第 1 行开始看
tail -F a.log b.log # 多文件同时跟随
tail -F --pid=1234 app.log # 目标进程退出后自动结束
sudo tail -F /var/log/secure | grep -i 'Failed password'选项对照
| 选项 | 作用 | 注意 |
|---|---|---|
| -n N | 输出末尾 N 行 | -n +N 表示从第 N 行开始到末尾 |
| -f | 跟随文件描述符 | 日志轮转后会停留在旧文件上 |
| -F | 跟随文件名,失败重试并重新打开 | 排障默认选它 |
| --pid | 指定进程退出后停止跟随 | 配合进程启动观察很实用 |
| -q / -v | 多文件时是否打印文件名分隔头 | 来源混淆时用 -v 强制标注 |
| -c N | 按字节输出末尾内容 | 适合没有换行的日志 |
管道里 grep 默认按块缓冲,实时跟随时会看不到输出,像是卡住了。加 --line-buffered 让 grep 逐行刷出,或对上游命令用 stdbuf -oL 调整缓冲策略。
选择工具要看日志的落点:应用把日志交给 journald 时用 journalctl -f -u 单元名,能顺带拿到结构化字段与启动轮次;应用自写文件时才用 tail -F。两者混用容易只看到一半信息。
在 NFS 等网络文件系统上,跟随的实时性受客户端属性缓存影响,远端写入不一定立刻可见,也不适合长时间挂着;跨主机看日志优先用 SSH 直连源主机执行 tail -F。
logrotate 与 inode:轮转时通常把 app.log 改名成 app.log.1 并创建新的 app.log,持有旧描述符的 -f 会继续读旧文件,-F 则按名字重新打开新文件。若应用自身不支持重开日志文件,轮转后要给它发信号或重载服务(Nginx 约定用 USR1 重新打开日志),否则它会一直写进已改名的旧文件,从新文件看日志就像「不再更新」。
只看末尾内容时 tail 比 cat 高效:它从文件尾部 seek,不读取全文,几十 GB 的日志也能秒开。反过来,统计行数用 wc -l,看开头用 head。在大文件里定位第一处关键字可以配合 grep -m 1,命中一条即停止读取。