教程

Linux 日志:journalctl、/var/log、rsyslog 与 logrotate

更新于: · Skillaria.Top

在现代 Linux 发行版中,日志由 journald 收集,并通过 journalctl 读取:journalctl -u nginx 显示某个服务的记录,-f 实时跟踪,--since 和 -p 按时间和重要程度过滤。文本日志保存在 /var/log 中,由 rsyslog 或应用本身写入,logrotate 定期压缩并删除旧文件,防止磁盘被占满。

服务在夜里崩溃时,答案几乎总在日志里,关键是知道去哪里找。在现代发行版(Ubuntu、Debian、RHEL、Fedora、Arch)中,日志由 journald 服务收集,用 journalctl 命令读取。同时,许多程序会把普通文本文件写入 /var/log,其大小则由 logrotate 控制。下面介绍如何在实践中使用 journalctl 查看 Linux 日志,其他日志保存在哪里,以及轮转时如何不丢失重要信息。

Linux 日志保存在哪里

日志有三个来源,把它们当作一张地图来记会很有帮助:

  • journald — systemd 的二进制日志。它接收所有服务的 stdout 和 stderr、内核消息以及 syslog。保存在 /var/log/journal/(持久保存)或 /run/log/journal/(直到重启)。
  • /var/log — 文本文件。由 rsyslog(例如 /var/log/syslog、/var/log/auth.log)或应用程序本身写入:nginx、PostgreSQL、MySQL 等。
  • 内核环形缓冲区 — 驱动程序和硬件的消息,可以用 dmesg 或 journalctl -k 查看。
文件内容
/var/log/syslog(Debian/Ubuntu)通用系统日志
/var/log/messages(RHEL/Fedora)Red Hat 系列中的同类日志
/var/log/auth.log 或 /var/log/secure登录、sudo、SSH
/var/log/kern.log内核消息
/var/log/dpkg.log、/var/log/apt/软件包安装记录
/var/log/nginx/Web 服务器的 access.log 和 error.log
/var/log/wtmp、/var/log/btmp二进制的登录记录,通过 last 和 lastb 读取

/var/log 中的大多数文件只有 root 或 adm 组可以读取,因此读取命令往往需要通过 sudo 运行。

journalctl:基本命令

不带参数时,journalctl 会通过分页器 less 输出全部日志,从最早的记录开始。这很少需要——通常会先缩小范围。

journalctl              # 全部日志,从旧到新
journalctl -e           # 直接跳到末尾
journalctl -n 50        # 最后 50 行
journalctl -r           # 倒序,最新的在上面
journalctl --no-pager   # 不使用 less,便于脚本和 grep

在分页器中可以使用熟悉的 less 快捷键:/ 搜索,G 跳到末尾,q 退出。过长的行会按屏幕宽度截断;用左右方向键可以横向滚动。

某个服务的日志:journalctl -u

最常见的场景是查看某个 systemd 单元的输出。不需要查找文件,也不需要用 grep:

$ sudo journalctl -u nginx -n 20
Oct 14 03:12:41 web1 systemd[1]: Starting nginx.service - A high performance web server...
Oct 14 03:12:41 web1 nginx[1834]: nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
Oct 14 03:12:41 web1 systemd[1]: nginx.service: Control process exited, code=exited, status=1/FAILURE
Oct 14 03:12:41 web1 systemd[1]: nginx.service: Failed with result 'exit-code'.

-u 可以重复使用:journalctl -u nginx -u php8.2-fpm 会按时间顺序混合显示两个服务的日志,适合排查发生在两者交界处的错误。查看用户级单元(systemctl --user)的日志时,使用 journalctl --user -u 名称。

实时日志:journalctl -f

sudo journalctl -u nginx -f

这相当于 tail -f:命令先输出最后 10 条记录,然后持续等待新记录。在重启服务或重现错误时,把它放在另一个终端里很方便。按 Ctrl+C 退出。

按时间和启动次数过滤

如果知道故障发生的时间,可以用 --since 和 --until 选择时间段。它们既接受精确日期,也接受相对时间:

journalctl --since "2026-10-14 02:50" --until "2026-10-14 03:30"
journalctl --since "1 hour ago"
journalctl --since yesterday --until today
journalctl -u nginx --since "10 min ago"

另一种有用的切分方式是按系统启动次数。如果服务器自己重启了,原因通常在上一次会话的末尾:

$ journalctl --list-boots
 -2 8f1c...  Sat 2026-10-12 09:01:12 UTC  Sun 2026-10-13 22:47:03 UTC
 -1 a7d2...  Sun 2026-10-13 22:48:30 UTC  Tue 2026-10-14 03:05:51 UTC
  0 c3e9...  Tue 2026-10-14 03:07:02 UTC  Tue 2026-10-14 09:15:40 UTC

$ journalctl -b -1 -e      # 上一次启动的结尾
$ journalctl -b            # 仅当前启动

只有日志保存在磁盘上时,才能列出启动记录。如何开启这一功能见下文关于 journald 配置的部分。

按重要程度过滤:journalctl -p

每条记录都有一个 0 到 7 的 syslog 优先级。参数 -p 会显示指定级别及更严重的记录:

编号名称使用场景
0emerg系统不可用
1alert需要立即处理
2crit严重错误
3err错误
4warning警告
5notice重要但属正常的事件
6info信息性消息
7debug调试信息
journalctl -p err -b              # 本次启动以来的所有错误
journalctl -p warning..err -u ssh # 单个服务的级别范围

在重启或更新之后,journalctl -p err -b 是很好的第一项检查:几秒钟内就能看出哪里出了问题。

搜索及其他过滤条件

journald 为每条记录保存结构化字段,可以直接按字段过滤,无需 grep:

journalctl _PID=1834                 # 按进程号
journalctl _COMM=sshd                # 按程序名
journalctl _UID=1000                 # 按用户
journalctl /usr/sbin/sshd            # 按可执行文件路径
journalctl -k                        # 只显示内核消息(类似 dmesg)
journalctl -t CRON                   # 按 syslog 标识符
journalctl -g "Out of memory"        # 按文本搜索,支持正则表达式

参数 -g(--grep)从 systemd 237 开始提供;在较旧的系统上,使用普通管道 journalctl --no-pager | grep ...。不同字段的过滤条件之间是“与”的关系,因此 journalctl -u nginx -p err --since today 只返回今天 nginx 的错误。

输出格式

参数 -o 用于改变输出格式,常用的有:

  • -o short-iso — ISO 8601 格式的时间,便于与应用日志对照。
  • -o verbose — 显示记录的所有字段,包括 _SYSTEMD_UNIT、_PID、_BOOT_ID。
  • -o json 或 json-pretty — 供脚本或 jq 处理。
  • -o cat — 只输出消息文本,不带时间和主机名。
journalctl -u myapp -o json --since today | jq -r '.MESSAGE'

配置 journald:存储与大小

配置位于 /etc/systemd/journald.conf(覆盖设置更适合放在 /etc/systemd/journald.conf.d/*.conf 中)。最主要的参数是 Storage:

  • persistent — 日志写入磁盘的 /var/log/journal,重启后依然保留。
  • volatile — 只保存在内存中,位于 /run/log/journal。
  • auto(默认)— 如果 /var/log/journal 目录存在,则写入磁盘。

在一些精简的系统镜像上,该目录不存在,重启后历史记录就会丢失。要开启持久保存:

sudo mkdir -p /var/log/journal
sudo systemd-tmpfiles --create --prefix /var/log/journal
sudo systemctl restart systemd-journald

为了防止日志占满整个磁盘,可以设置上限:

# /etc/systemd/journald.conf.d/size.conf
[Journal]
Storage=persistent
SystemMaxUse=500M
MaxRetentionSec=1month

默认情况下,SystemMaxUse 为文件系统大小的 10%,但不超过 4 GB。

如何清理 journalctl 日志

$ journalctl --disk-usage
Archived and active journals take up 1.8G in the file system.

sudo journalctl --vacuum-size=300M   # 最多保留 300 MB
sudo journalctl --vacuum-time=2weeks # 删除两周以前的记录
sudo journalctl --rotate             # 关闭当前活动文件,使其也可以被清理

不建议用 rm 删除 /var/log/journal 中的文件:vacuum 会安全地完成同样的工作,并且不会动正在写入的活动文件。

rsyslog:经典的文本日志

rsyslog 是一个守护进程,它接收 syslog 消息(包括从 journald 转发来的消息),并按规则把它们写入不同的文件。配置位于 /etc/rsyslog.conf 和 /etc/rsyslog.d/*.conf。每条规则由“来源.级别”选择器和动作组成:

# /etc/rsyslog.d/50-default.conf(节选)
auth,authpriv.*        /var/log/auth.log
*.*;auth,authpriv.none -/var/log/syslog
kern.*                 -/var/log/kern.log

路径前面的减号表示每写入一行不立即同步到磁盘——速度更快,但发生故障时最后几行可能丢失。修改配置后,先检查语法,再重启服务:

sudo rsyslogd -N1
sudo systemctl restart rsyslog

把 rsyslog 与 journald 放在一起的主要原因是把日志转发到中央服务器。一行配置就能通过 TCP 发送全部内容:

# /etc/rsyslog.d/90-remote.conf
*.* @@logs.example.com:514

单个 @ 表示 UDP,两个 @@ 表示 TCP。即使服务器整体故障,日志副本仍保存在另一台机器上。要从脚本中向系统日志写入自己的消息,使用 logger:

logger -t backup -p user.err "数据库备份失败"
journalctl -t backup -n 5

logrotate:日志轮转

文本日志会无限增长,除非有人把它截断。logrotate 负责这件事:每天(通过 cron 或 logrotate.timer 定时器)它会重命名当前文件、压缩旧文件,并删除最旧的文件。通用设置位于 /etc/logrotate.conf,各程序的规则位于 /etc/logrotate.d/。

# /etc/logrotate.d/myapp
/var/log/myapp/*.log {
    daily
    rotate 14
    compress
    delaycompress
    missingok
    notifempty
    create 0640 myapp adm
    postrotate
        systemctl reload myapp > /dev/null 2>&1 || true
    endscript
}
指令含义
daily / weekly / size 100M何时轮转
rotate 14保留多少个旧文件
compress、delaycompress用 gzip 压缩,但最新的归档保持不压缩
missingok、notifempty文件不存在时不报错,空文件不轮转
create 0640 user group新文件使用的权限
copytruncate复制后清空原文件——适用于不会重新打开日志的程序
postrotate ... endscript轮转之后执行的命令,通常是通知应用重新打开文件

可以在不做任何更改的情况下测试规则,也可以立即强制轮转:

sudo logrotate -d /etc/logrotate.d/myapp   # 调试模式的空运行
sudo logrotate -f /etc/logrotate.d/myapp   # 立即轮转

常见错误是忘了 postrotate 或 copytruncate。这时应用会继续通过旧的文件描述符写入已重命名的 app.log.1,而新的 app.log 则始终为空。

阅读文本日志

对于 /var/log 中的文件,标准工具就足够了:

sudo tail -n 100 /var/log/syslog          # 最后几行
sudo tail -F /var/log/nginx/error.log     # 持续跟踪,轮转后仍有效
sudo less +G /var/log/auth.log            # 直接打开到末尾
sudo zgrep "Failed password" /var/log/auth.log.*.gz   # 在压缩归档中搜索
sudo grep -c " 500 " /var/log/nginx/access.log        # 500 响应的数量

注意 tail -F 中的大写 F:与 -f 不同,它在 logrotate 替换文件后会重新打开文件。

分步分析一次故障

“服务夜里崩溃了”这类情况可以按以下顺序排查:

  1. systemctl status myapp — 当前状态以及日志的最后几行。
  2. journalctl -u myapp --since "03:00" --until "03:30" — 服务在崩溃时段写入了什么。
  3. journalctl -p err -b — 同一时间系统中是否还有其他错误。
  4. journalctl -k --since "03:00" -g "Out of memory|Killed process" — 内核的 OOM Killer 是否杀死了该进程。
  5. 应用自身在 /var/log/myapp/ 中的日志,包括 .gz 归档。
  6. 如果服务器曾经重启——journalctl -b -1 -e。

总结

日常处理 Linux 日志只需要几个命令:journalctl -u 查看服务,-f 查看实时输出,--since 和 -p err 缩小范围,-b -1 分析重启。文本日志保存在 /var/log 中,rsyslog 负责分类和转发,而 logrotate 以及 journalctl --vacuum-* 防止日志占满磁盘。journalctl、日志轮转和负载诊断的实际操作,我们在 Linux 课程的讲座中都有演示。

常见问题

为什么 journalctl 只显示上一次启动以来的记录?

日志只保存在内存中(/run/log/journal)。创建 /var/log/journal 目录,或在 journald.conf 中设置 Storage=persistent,然后重启 systemd-journald。

如何让普通用户不用 sudo 也能读取系统日志?

把用户加入 systemd-journal 组(在 Debian 和 Ubuntu 上是 adm 组):sudo usermod -aG systemd-journal 用户名,然后重新登录。

能否查看其他系统的日志,例如挂载磁盘上的日志?

可以,用 -D 参数指定日志目录:journalctl -D /mnt/var/log/journal。参数 --file 可以读取单个 .journal 文件。

有了 journald 还需要 rsyslog 吗?

不一定。如果需要为旧工具提供文本文件,或者需要把日志转发到中央服务器,它就有用;在很多精简镜像上已经不再安装它。

如何把某个服务的日志导出到文件发给同事?

执行 journalctl -u 名称 --since today --no-pager > service.log,得到的就是普通文本文件。

通过视频学习本主题

课程“Linux: с нуля до уверенного администрирования”结合实践详细讲解了这一主题。

前往课程