接手一台Ubuntu 22.04服务器之后,我建议你第一件事就是搞清楚这台机器上的日志到底怎么存、怎么查。很多以为自己“运气不好”的故障——服务莫名退出、磁盘空间突然告警、半夜被入侵、应用响应变慢,其实答案早就躺在日志里了。只是大多数人没时间、没习惯、没用对工具去读它。
这篇文章不聊空泛的理论,直接围绕Ubuntu 22.04的日志处理讲透:日志从哪来、在哪个文件里、用哪些命令能快速翻到想要的内容、怎么把日志集中采集出去、怎么防止日志把磁盘塞爆,以及我在实际运维中踩过的坑。无论你是刚上手Ubuntu的新人,还是从CentOS迁移过来的老运维,这篇文章都能让你少走弯路。
先说结论:Ubuntu 22.04的日志体系和老版本区别非常大,理解它的“双轨制”是第一步。
1. 日志从哪来、到哪去
1.1 Ubuntu 22.04的日志体系:三股流量
很多人上来就习惯去翻/var/log/messages,结果发现文件不存在,就开始怀疑系统有问题。其实不是坏了,而是Ubuntu没有这个文件。Ubuntu 22.04的日志体系可以粗略分成三股流量:
第一股是内核和系统服务的日志,由systemd-journald接管。所有通过systemd启动的服务(几乎涵盖所有系统服务),它们的标准输出、标准错误、以及调用syslog接口打印的日志,最终都被journald收集,存成二进制的journal文件,放在/var/log/journal/目录下。你直接用cat去读这些文件会看到乱码,必须用journalctl命令来查。
第二股是传统文本日志,由rsyslog处理。journald虽然收日志,但它本身不做复杂的文件落盘和转发,真正把日志写到/var/log/syslog、/var/log/auth.log这些文本文件里的,是rsyslog。它从journald读取日志,再根据规则分发到对应文件。
第三股是应用自己写的日志,比如Nginx的access.log、MySQL的error.log、Java应用的日志文件。这类日志和系统日志基本无关,由应用自行管理,通常也放在/var/log下或者应用自己的目录里。
理解这三股流量是处理Ubuntu 22.04日志的基础。我见过不少人在/var/log/syslog里找不到某个服务的日志就以为系统出问题了,其实那个服务的日志可能只进了journald,根本没被rsyslog落到文件里。
说起来,Ubuntu 22.04时代默认日志采集链路大致是这样的:进程写日志 → journald收集 → rsyslog监听journal → 写入/var/log下的文本文件。中间任何一环配置变化,都会影响最终结果。
1.2 /var/log核心文件一览
Ubuntu 22.04的/var/log目录里,我平时最常看这些文件:
| 文件路径 | 内容说明 |
|---|---|
/var/log/syslog | 系统全局日志,很多服务都会写到这里,排查问题优先看它 |
/var/log/auth.log | 登录认证日志,包括SSH登录、sudo提权记录,安全检查必看 |
/var/log/kern.log | 内核日志,硬件、驱动、内核模块问题看这里 |
/var/log/dpkg.log | 软件包安装和升级记录,排查“软件莫名其妙没了”的问题 |
/var/log/apt/ | apt更新日志目录,含history.log和term.log |
/var/log/boot.log | 开机时的服务启动信息 |
/var/log/nginx/ | Nginx访问和错误日志(安装Nginx后出现) |
/var/log/mysql/ | MySQL错误日志(安装MySQL后出现) |
/var/log/journal/ | journald的二进制日志目录,用journalctl查询 |
一个很实用的习惯是,不确定某个服务日志在哪的时候,用ls -lt /var/log/ | head看看哪些文件最近有更新,基本就能锁定目标。
2. 用journalctl翻日志,比你想的更好用
2.1 至少要学会的6种journalctl查看方式
journalctl是Ubuntu 22.04上最核心的日志查看命令,也是很多人觉得最不直观的命令。我日常使用频率最高的是下面这些组合:
查看本次开机之后的全部日志:
journalctl -b查看上次开机的日志:
journalctl -b -1查看某个Unit(服务)的日志,这是排查服务问题的王牌命令:
journalctl -u ssh.service看某个服务的最近100行日志,并且持续跟随输出,调试服务时开着它很舒服:
journalctl -u nginx.service -f -n 100只看内核日志,相当于dmesg加了时间戳和持久化:
journalctl -k看过去1小时内的日志:
journalctl --since "1 hour ago"我特别喜欢_PID、_COMM这些字段的过滤方式,比如我只想看到某个进程的崩溃信息:
journalctl _COMM=java --since "2024-01-01" --until "2024-01-02"这里的关键是,journal日志里每个条目都有丰富的元数据,包括进程号、用户ID、来源Unit、时间戳等。你用journalctl -o verbose就能看到每条日志的完整字段,再配合_PID=、_UID=、_SYSTEMD_UNIT=过滤,精度极高。
2.2 “无日志文件夹”是怎么回事
很多人搜过这么一个问题:某服务的日志文件夹不存在,到处找不到日志。我刚开始用systemd的时候也困惑过。
一个systemd服务,如果配置文件里没有指定StandardOutput=和StandardError=,那么它打印到标准输出和标准错误的内容,默认全被journald收走了,不会生成/var/log/xxx.log文件。这不是Bug,而是设计选择。
所以当你发现“无日志文件夹”时,第一反应应该是journalctl -u 你的服务名,日志大概率都在这里。如果你就是想要独立的文件日志,可以在service文件里显式指定:
[Service] StandardOutput=file:/var/log/myapp/app.log StandardError=file:/var/log/myapp/app.log但要注意,journald依然会同时收集一份,所以要控制好日志量。还有就是如果用Docker,容器里服务打印到stdout的日志默认由Docker接管,不走journald,别搞混了。
2.3 查看crontab执行日志的两种办法
我经常在服务器上排查定时任务为什么没跑,发现很多人不知道crontab日志在哪。这里容易有两个坑:一是cron的日志默认落在了syslog里但容易被忽略;二是journald里也有,但得用对过滤条件。
第一种办法,直接搜syslog:
grep CRON /var/log/syslog第二种办法,用journalctl过滤:
journalctl _COMM=cron --since "today"如果发现日志量太大,可以单独给cron一把椅子。在/etc/rsyslog.d/下新建一个配置文件,比如50-cron.conf,写上:
cron.* /var/log/cron.log重启rsyslog之后,cron日志就单独落到文件里了。
如果你用的是系统自带的systemd timer,那又是另一套查看方式:systemctl list-timers看定时器状态,journalctl -u 定时器名字查看执行细节。
3. 把日志统一采集起来
单机翻日志是基本功,但真正上班之后,你很快会遇到更现实的问题:几十台服务器的日志太分散,每台都ssh上去journalctl会疯掉的。这时候就需要把日志统一采集到一个平台。Ubuntu 22.04上最常见的两种方式我要展开讲讲。
3.1 用filebeat做JSON化采集
Elastic公司出的filebeat是目前最常见的轻量日志采集器,在Ubuntu 22.04上配置不算复杂,但有几个细节值得注意。安装方式我习惯直接用Elastic官方仓库:
curl -fsSL https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo apt-key add - echo "deb https://artifacts.elastic.co/packages/8.x/apt stable main" | sudo tee /etc/apt/sources.list.d/elastic-8.x.list sudo apt update && sudo apt install filebeat然后修改/etc/filebeat/filebeat.yml。一个最小可用的配置大概是这样的:
filebeat.inputs: - type: filestream id: my-application enabled: true paths: - /var/log/myapp/*.log fields: service: myapp hostname: "${HOSTNAME}" fields_under_root: true output.elasticsearch: hosts: ["192.168.1.10:9200"] username: "elastic" password: "changeme"配置好之后启动并设置开机自启:
sudo systemctl enable --now filebeat这里我要提醒一个坑:filebeat的旧版配置用- type: log,新版本推荐filestream,两者参数不兼容。网上90%的老教程还在用logtype,照着抄到8.x版本上会直接启动失败。报错信息通常会说unknown key。
另外,filebeat默认会记录读取进度到/var/lib/filebeat/registry/,所以第一次采集会从头读,之后只读新增。如果你改过paths,想重新采集,记得把registry目录清掉:
sudo rm -rf /var/lib/filebeat/registry sudo systemctl restart filebeat3.2 用rsyslog做远程转发
如果你不想引入Java环境、不想维护Elasticsearch集群,只想把多台Ubuntu的日志集中收集到一台机器上,rsyslog远程转发是更轻量的方案。Ubuntu 22.04自带rsyslog,只需要两端做配置。
接收端(日志服务器)在/etc/rsyslog.conf里打开模块:
module(load="imudp") input(type="imudp" port="514") module(load="imtcp") input(type="imtcp" port="514")如果想按发送方主机名区分日志,可以加一行模板规则。比如在/etc/rsyslog.d/60-template.conf里写:
template(name="PerHostLog" type="string" string="/var/log/remote/%HOSTNAME%/%programname%.log")发送端在/etc/rsyslog.d/60-forward.conf里写:
*.* @192.168.1.10:514重启rsyslog服务后,日志就转发过去了。注意这里的协议:单个 @ 表示UDP,两个 @@ 表示TCP。UDP快但有丢包风险,TCP稳但占用稍高,内网采集用TCP更可靠。
我自己的习惯是,小规模(比如10台以内)用rsyslog就够,省心、不占资源。规模大了、需要全文检索和可视化,再上filebeat + Elasticsearch。
3.3 到底选哪个:filebeat和rsyslog对比
| 对比项 | filebeat | rsyslog远程转发 |
|---|---|---|
| 资源占用 | 中等,Go语言实现,单实例约20-40MB内存 | 低,原生进程,内存占用可忽略 |
| 功能 | 支持多输出、多条件过滤、内置模块 | 侧重转发和简单过滤,规则较繁琐 |
| 扩展性 | 配合ES、Logstash、Kibana能力强 | 配合远端rsyslog简单集中,缺乏可视化 |
| 上手难度 | 中等,配置文件YAML清晰 | 中等,配置语法老派,需要查文档 |
| 适用场景 | 中等规模以上、需要搜索和可视化 | 小规模、只要集中存储即可 |
无论选哪个,都建议在采集端就把日志结构化。比如应用能输出JSON最好,实在不行也至少保证一条日志一行,不要用多行日志,否则采集、解析、检索全部难受。
4. 日志轮转与清理:别让日志把磁盘撑爆
4.1 logrotate的工作原理
日志如果只增不减,任何机器都扛不住。Ubuntu 22.04自带的logrotate工具就是为了解决这个问题。它的核心思想很简单:定期把当前日志文件改名、压缩或删除,然后让应用重新写入新文件。
logrotate的配置分为两部分:全局配置/etc/logrotate.conf和各应用自己的配置/etc/logrotate.d/下的文件。系统每天通过cron定时执行一次,/etc/cron.daily/logrotate就是入口。
它的执行逻辑大概是:读取配置 → 判断是否需要轮转(按天数或大小)→ 执行轮转 → 执行postrotate脚本(通常是通知应用重开日志文件,比如systemctl reload nginx)→ 把旧文件按序改名、压缩 → 超过保留数量的删除。
4.2 定制一个logrotate配置
我举一个实际例子。假设你的应用myapp每天会产生100MB日志,你想保留7天,每天轮转一次,并开启压缩。在/etc/logrotate.d/myapp里写:
/var/log/myapp/*.log { daily rotate 7 compress delaycompress missingok notifempty dateext su myapp myapp postrotate systemctl reload myapp endscript }这里逐条说明:
daily:每天轮转一次。如果要按大小,换成size 100M,两者选一个。rotate 7:保留7个旧文件,第8个会被删掉。compress:轮转后的旧文件压缩成gz格式。delaycompress比较巧妙,表示压缩延迟到下一轮,也就是最新那个旧文件不压,等下轮一起压。这么做主要防止像Nginx这类还在往旧文件写日志的应用出问题。missingok:日志文件不存在时不要报错,避免黑屏刷错误。notifempty:文件为空就不轮转。dateext:轮转后的文件名带上日期,比如app.log-20240101.gz,比默认的.1、.2后缀直观得多。su myapp myapp:以指定用户执行,防止权限问题导致轮转失败。postrotate:轮转完成后重载应用,让它重新打开新的日志文件。
算一笔账:每天100MB,保留7天且压缩。压缩率按0.2算,日志占用大约是7个旧日志压缩后 ≈ 100MB × 0.2 × 7 ≈ 140MB,加上当天原始日志100MB,总共不到250MB。如果不开压缩,那就是800MB,差距巨大。
所以我强烈建议所有能开压缩的日志都开压缩。反正解压也只是日志平台采集端的举手之劳。
4.3 手动清空和滚动日志的注意事项
线上总会遇到一些奇葩场景:某个应用不写日志到文件而是持续刷屏,日志文件已经几个GB了,logrotate因为rotate数量还没到或者应用不配合一直没转。这时候你需要手动干预。
最简单的做法是清空文件而不是删除:
> /var/log/myapp/big.log这个命令用Shell重定向直接清空文件内容。我特别强调,不要用rm删除日志文件,特别当应用还持有文件句柄时。删除后,应用会继续往那个已经“消失”的inode上写数据,磁盘空间不会释放,而且新文件根本不会创建。这是Linux下最常见的“明明删了日志磁盘空间还是满”的原因。
正确清空一个正在被写的大日志,还有一种更自动化的方式:
truncate -s 0 /var/log/myapp/big.log甚至想保留部分内容,可以:
truncate -s 100M /var/log/myapp/big.log这样文件会缩短到100MB,之后的日志继续追加。应用进程完全无感知,也不需要重启。
不过要注意有些应用会记录自己在文件中的偏移位置,比如tail -c这种,突然截断可能导致它继续从旧偏移写,产生文件空洞。我遇到过Java应用配合log4j时,truncate后再往里写会出现一大串NUL字符。稳妥的做法还是truncate之后观察一下日志是否正常增长,如果异常就重启应用。
5. 常见问题与排查技巧
5.1 journald日志把磁盘占满
这是我在Ubuntu 22.04上见过最多的问题。journald默认会把日志存在/var/log/journal里,而且没有做严格的大小限制,如果某个服务疯狂刷日志,journal文件能涨到几个GB甚至占满整个根分区。
最直接的查看方式:
journalctl --disk-usage然后你可以手动清理:
journalctl --vacuum-size=200M这会把日志总量压缩到200MB以内,只保留更近的日志。还可以按时间清理,只保留最近7天:
journalctl --vacuum-time=7d但从根本上解决问题,还是要修改journald的配置。编辑/etc/systemd/journald.conf,找到这几行并改成:
SystemMaxUse=200M SystemMaxFileSize=50M MaxRetentionSec=7daySystemMaxUse是journal能够占用的最大磁盘总量,SystemMaxFileSize是单个journal文件上限,MaxRetentionSec是日志最大保留时间。改完重启服务:
sudo systemctl restart systemd-journald注意重启journald会丢掉内存中的部分日志,但磁盘上已有的journal仍在。这里有个顺带坑,改配置后建议顺手journalctl --verify检查一下journal文件的完整性,我遇到过断电后journal文件损坏,执行此命令能提前发现。
5.2 日志时间戳和实际时间对不上
刚装完Ubuntu 22.04,很多人发现日志时间和当前时间差8小时,这通常不是日志系统坏了,而是时区没配。systemd时代系统时钟管理已经统一了,配置方法如下:
sudo timedatectl set-timezone Asia/Shanghai设置完用timedatectl查看确认。之后journald和rsyslog记录的日志时间戳就会跟随系统时区。但这里有个深坑:journald内部存储的时间戳实际上是UTC时间,只展示的时候转换成本地时间。所以如果你把系统时区改了,之前记录的日志展示时间也会跟着变,内容不会错乱。
如果你用的是Docker容器,容器里的日志时间出问题又是一种解法:启动容器时必须挂载时区文件:
docker run -v /etc/localtime:/etc/localtime:ro ...否则容器默认UTC,日志自然比宿主机快8小时。
5.3 filebeat采集不到日志
遇到filebeat装了但采集不到日志,我通常按这个顺序排查:
第一步看进程有没有起来:
systemctl status filebeat第二步看filebeat的日志有没有报错:
journalctl -u filebeat -n 50第三步测试能否连上目标:
curl -s http://192.168.1.10:9200第四步看是否有权限问题,filebeat默认以root运行,但如果你在配置里指定了user: myapp,那它读不了/var/log/myapp/下权限600的日志文件。
还有一个我踩过的坑:paths里写的是/var/log/myapp/*.log,但应用实际生成的日志后缀是log.20240101,不匹配,导致filebeat永远扫不到。解决方法是把通配符改成/var/log/myapp/*或者指定具体文件名。
5.4 重启日志服务导致日志丢失
很多人不知道restart rsyslog和restart systemd-journald的区别。journald重启会丢失一部分正在内存中的日志,因为这些日志还没落盘。所以不要没事就重启journald,尤其是排查问题的时候,重启一次可能就把关键线索弄丢了。
相比之下,rsyslog重启影响小一些,因为它只是读取journal再写文件,已经在文件里的日志不丢,但journald那边没落盘的依然丢。
如果确实需要清理日志且不想丢关键信息,建议先执行journalctl --flush,把内存日志强制写入磁盘,再进行重启。
另外,每次大版本升级之后,我建议抽查几个服务的日志轮转是否正常。Ubuntu的自动更新可能替换了logrotate版本,或者某些包自带的配置覆盖了你定制的内容。我遇到过升级后/etc/logrotate.d/nginx配置被整体替换,结果自定义的dateext和保留数量全丢了,日志又开始越拉越长。
日志处理这件事,说大不大,说小不小。它不产生业务价值,但业务出了问题,第一个要找的就是它。我在Ubuntu 22.04上摸索了几个月,踩过不少坑之后最大的体会是:先把journald弄明白,把rsyslog的转发和logrotate的轮转配好,最后再考虑上采集平台。大多数中小规模的日志问题,靠这几样就能解决。最后再分享一个小技巧:如果不知道日志量增长有多快,可以用这个命令快速估算今天写了多少日志:
journalctl --since today | wc -l数字一旦稳定增长,你就知道,该去把轮转和采集配好了。