ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

journalctl日志查询实战:从入门到持久化配置与磁盘控制

journalctl日志查询实战:从入门到持久化配置与磁盘控制 我用journalctl查个日志本来以为要翻小半天老文件结果一条命令三秒定位问题。这事儿搁五年前根本没法想——那时候排查问题全靠grep /var/log/messages日志一轮就被logrotate切走想找三天前的报错简直大海捞针。现在systemd成了Linux发行版的默认初始化系统journalctl就是系统和应用日志的统一入口。这篇文章我从零开始拆journalctl的常用参数、过滤技巧、持久化配置和磁盘控制顺便把实际排查中踩过的坑一并交代清楚。无论你是刚转Linux运维的新人还是被日志折磨过几次的开发者按这篇文章的思路过一遍再遇到服务为什么起不来半夜谁把机器搞重启了这类问题底气会不一样。1. 为什么我后来离不开journalctl从/var/log/messages到systemd日志的切换背景先聊聊背景不然你没法理解journalctl设计上到底解决了什么痛点。1.1 传统Linux日志体系有哪些毛病我最早接触的服务器还是CentOS 6那一套日志体系完全是各写各的内核日志写进/var/log/kern.log或/var/log/dmesg系统服务日志由syslog统一收写到/var/log/messages应用日志就看应用心情有的写/var/log/nginx/error.log有的干脆用nohup把stdout丢进自定义文件认证日志单独一份/var/log/secure问题来了当系统起不来或者某个服务在启动早期就崩了根本来不及写文件你手上只有黑乎乎的屏幕报错。就算日志写下来了/var/log/messages是纯文本每天凌晨被logrotate切成messages-YYYYMMDD查个一周前的报错得翻好几个文件还得匹配某个具体服务grep出来一堆无关内容。更别说多台服务器分布部署时每台机器日志格式还不统一想快速对比基本靠肉眼。systemd把init进程、服务管理、日志收集三件事打包解决journal就是它的日志子系统。它不依赖外部syslog服务内核消息、系统服务标准输出、stderr、甚至是syslog转发的记录统统收进同一套结构化日志库里。journalctl就是这套日志库的查询前端所有类型日志一个命令全查出来。1.2 journal日志和传统日志文件的本质区别journal底层不是纯文本文件而是一套二进制日志库按索引存储好处非常直观。结构化字段每条日志除了消息本身还带_PID、_COMM、_HOSTNAME、_SYSTEMD_UNIT、PRIORITY这些可选元数据过滤精度比纯文本grep高一个量级。统一入口内核日志、服务日志、用户日志全部进入同一个库不用再记一堆日志文件路径。自带轮转与压缩journal文件到达阈值后自动切割并按需压缩旧日志默认保留一定持久化窗口。访问控制完善root和位于systemd-journal、systemd-adm、adm组内的用户可读取全部日志普通用户只能查看自己的用户会话日志权限边界比直接给所有人放读/var/log/messages要更严谨。我第一次用journalctl查服务日志时最强烈的感受是终于不用关心这个日志写到哪个文件去了。一个命令管所有服务这种统一性带来的效率提升是传统方法比不了的。2. 最先要会的几条journalctl基础命令先跑起来再说不管你是查看整机日志还是单个服务的日志下面这些命令是高频使用的基础组。掌握了它们日常九成场景已经够用。2.1 输出全部日志与翻页查看直接敲journalctl不带任何参数会从最早的日志开始输出所有记录内容量大到直接刷屏。不用慌默认输出会经过less分页器你可以用PageDown翻页、按G跳到底部、按g回到顶部、按q退出。如果只想看最后300行日志用-n参数等价于tail -n 300journalctl -n 300习惯上我先journalctl -n 50扫一眼当前系统有没有新增异常再按具体条件缩小范围。2.2 查看某个服务的日志-u参数这是所有参数中用得最频繁的选项。指定-u加服务名Unit名称journalctl就只输出该服务的日志不管它写没写独立的日志文件。journalctl -u nginx journalctl -u sshd需要说明的是Unit名称不必写全systemd会做前缀匹配。比如你输入journalctl -u ssh它会匹配所有以ssh开头的Unit。多服务过滤也可以叠加多个-u参数journalctl -u nginx -u php-fpm这条命令把Nginx和PHP-FPM的日志合并输出按时间排序。排查一个Nginx报502、PHP-FPM疑似挂掉的问题时两个服务的日志放一起对比不用来回切换文件非常省心。提示-u匹配的是systemd服务Unit名不是普通进程名。查看任意进程日志要用_COMM匹配后面会讲两者别混用。2.3 实时跟踪日志-f参数调试时最常用的就是实时跟踪。加上-fjournalctl会持续输出新产生的日志相当于tail -fjournalctl -f -u nginx我实际调试时几乎总把-f和-u组合用改完配置systemctl restart然后盯着日志看启动是否成功。除了排查服务分析脚本是否被定时器触发、看用户登录记录都是-f的主场。journalctl -f不加任何过滤条件时所有内核和服务的日志都会刷出来噪声比较大。务必配合-u或_COMM使用。2.4 只看本次开机以来的日志-b参数-b是journalctl区别于传统日志工具的最大优势之一。journalctl -b这条命令只显示本次启动以来的全部日志。排查为什么今天开不了机或系统刚启动时哪个服务报警了立刻就能过滤掉上一次运行的干扰信息。多内核引导时代有个经典场景上一次升级内核后网络起不来重启还是起不来你想对比这次和上次的日志差异。journalctl -b -1代表查看上一次启动的日志-b -2看再往前一次。数字越大启动次序越靠前。直接把两次日志导出对比journalctl -b /tmp/this_boot.log journalctl -b -1 /tmp/last_boot.log然后diff两个文件差异一目了然。这个思路在排查升级后出现回归时特别管用。2.5 输出每条日志的时间戳信息-o verbosejournal的日记条目里除了消息文本还存了大量元数据字段。加-o verbose查看单条完整信息journalctl -u nginx -o verbose输出会包含_PID、_UID、_GID、_COMM、_EXE、SYSLOG_FACILITY等信息。当你怀疑这条日志到底是哪个进程打的是不是被别的同名程序干扰了用verbose模式看元数据就能确认。如果只是想要可读性更高的输出用-o short-iso或-o json也行journalctl -u nginx -o short-iso journalctl -u nginx -o json3. 按时间和优先级过滤定位问题时的正确打开方式基础命令能让你看到日志但如果日志量大真正定位问题时时间和优先级过滤才是最核心的手段。3.1--since/--until时间窗口过滤--since定义查询的起始时间--until定义结束时间。两个参数组合就把日志锁定在一个精确的时间窗口里journalctl --since 2025-01-15 10:00:00 journalctl --since 2025-01-15 10:00:00 --until 2025-01-15 10:30:00关键技巧时间的写法支持多种格式date命令能解析的几乎都能用。比如journalctl --since yesterday journalctl --since 2 hours ago journalctl --since 2025-01-14 08:00:00 --until 2025-01-15 08:00:00以yesterdaytodaynow2 hours ago这类相对时间排查时不用精确换算特别顺手。实际经验是先估一个宽一点的时间窗口看有没有嫌疑再逐步收窄。一次性把窗口卡得太死反而容易漏掉事件前的征兆。3.2 优先级过滤-p参数从emerg到debug日志有8个级别数字越小越严重级别名称数值含义常见示例emerg0系统不可用内核崩溃alert1必须立即处理数据库数据损坏crit2严重错误硬件报错err3错误服务启动失败warning4警告磁盘即将写满notice5一般性通知服务正常启动info6常规信息请求处理完成debug7调试信息函数调用细节-p日志级别参数取的是该级别及更严重的日志不是只显示该级别。journalctl -p err journalctl -p warning -u nginx --since today用-p err可以直接略过所有提示信息直取错误记录。定位问题从最高级别往下筛能节省大量时间。3.3 组合时间、优先级、服务三要素我实际工作中的标准排查姿势是三者组合journalctl -u php-fpm --since 30 min ago -p err这条命令的意思是只看PHP-FPM在过去半小时内的错误日志。不用翻冗余信息直接看到报错本身。再配上verbose模式journalctl -u php-fpm --since yesterday -p warning -o verbose每个字段都会展开定位到具体时间点和具体进程。我平时常把这三要素组合输入写成Shell别名本地调试时一行命令就能拿到全貌。3.4 按可执行文件、用户、会话等字段过滤journal日志的元数据字段同样可以参与过滤。用_COMM匹配进程名journalctl _COMMsshd --since today_PID匹配进程号journalctl _PID12345 --since 30 min ago_UID匹配用户IDjournalctl _UID1000 --since today组合字段可以用连接逻辑或关系journalctl _COMMsshd _COMMcrond --since today注意字段匹配用字段名值多个等值条件是或关系。如果需要且的条件要叠加-u、--since这类非字段过滤。我还遇到过一种情况某进程反复重启每次PID都不一样按进程号查会漏掉前面的记录。此时按_COMM匹配反而是最稳的。4. 日志持久化的坑重启后日志消失怎么办journalctl虽好但它的默认行为有一个非常容易踩的坑日志默认不落盘重启一次就没了。4.1 默认行为环形缓冲区内存日志systemd journal默认把日志存在内存文件系统/run/log/journal/下。这个目录的数据只存在于内存中重启后自动消失。这意味着排查上一次开机期间发生了什么时journalctl -b -1干干净净什么都没有系统崩溃前最后几十秒的日志可能因为来不及写盘而丢失内存占用过大的情况下日志可能被自动丢弃为什么默认这样设计为了减少磁盘IO。服务器上大量写实时日志会拖慢磁盘性能开发环境不落盘也够用。但生产环境里如果什么都不配置重启后历史日志全丢这个坑会让人抓狂。4.2 配置持久化创建/var/log/journal目录systemd的几个启动流程里有一个很贴心的设计如果检测到/var/log/journal/目录存在日志会被持久化写入磁盘。所以最简单的方式是手动创建目录并授权sudo mkdir -p /var/log/journal sudo systemd-tmpfiles --create --prefix/var/log/journal sudo systemctl restart systemd-journald这一步做完journal就开始把日志持久化到/var/log/journal/。注意systemd-tmpfiles那条命令它负责给目录设置正确的ACL权限和所有权。如果你直接chown root:systemd-journal也行但用tmpfiles配置更规范。4.3 journal的配置文件在哪改/etc/systemd/journald.conf即使不做上面那步你也可以直接改/etc/systemd/journald.conf这是journald的权威配置入口[Journal] StoragepersistentStorage参数决定journal的行为persistent日志写入/var/log/journal/持久保存volatile日志只写入内存目录重启即失auto/var/log/journal/存在则持久化不存在则用内存默认值none完全不存日志仅转发给其他日志系统生产环境推荐直接写persistent固定行为防止目录被谁不小心删了日志又回到内存模式这种隐性变化。修改后重启服务sudo systemctl restart systemd-journald重启journald不会影响正在运行的服务这点我一开始也有顾虑实测后发现完全安全。提示systemd-journald重启后它会重新扫描现有的日志文件历史日志依然可查不会丢。但如果重启过程中有系统崩溃级别的故障最后几行日志有可能来不及落盘这是物理层面的限制。4.4 验证持久化是否生效重启journald后检查日志文件是否真的写入了磁盘sudo ls -l /var/log/journal/正常情况会看到一串以机器ID命名的目录形如/var/log/journal/3d6d02cee6c1474d9f3d0b8d5e6c1234里面是各种.journal文件。再用reboot测试一下journalctl --list-boots这条命令列出所有引导记录。如果只能看到当前0这一条说明日志没持久化如果看到多条-1、-2说明持久化已生效。4.5 用户会话日志journalctl --user-unit如果你想查看某个用户服务systemctl --user管理的服务的日志用--user-unit参数journalctl --user-unit myuserapp --since today但要注意用户级日志是否持久化取决于用户级journal的存在状况。如果你的应用跑在用户模式下一定也要检查对应用户的持久化目录是否正常否则同样有重启丢日志的问题。5. 日志占满磁盘的隐患journalctl不配置真的会吃满/var持久化解决了丢日志问题但引入了另一个问题日志无上限增长最终吃满/var分区。这个我在线下环境亲自见过数据库服务器被一条持续刷屏的日志干挂排查半天最后发现是磁盘满了。5.1 默认容量限制是多少journald默认行为是对自身容量有限制的日志总量超过SystemMaxUse或RuntimeMaxUse限制后自动清理最旧的日志文件。默认的SystemMaxUse通常是文件系统大小的10%但文件系统大的机器10%同样可能高达几十GB不容小觑。journald还有一个特性如果/var所在分区小且日志持续写入导致磁盘写着写着满了它不会主动停下来而是可能影响到其他进程的写入拖垮整个系统。所以必须主动配置。5.2 控制日志容量的核心配置项在/etc/systemd/journald.conf中常用这几个参数控制持久化日志的上限[Journal] SystemMaxUse500M SystemMaxFileSize100M MaxRetentionSec7daySystemMaxUsejournal日志占用的磁盘总量上限500M是最常见的保守设置SystemMaxFileSize单个journal文件的大小上限超过后自动rotate到新文件MaxRetentionSec日志保留的最长时间超过即视为过期它们之间的关系SystemMaxUse是总闸门SystemMaxFileSize是单个文件的切割阈值MaxRetentionSec是时间维度的额外限制。配置任意一个都能限制日志量按需组合即可。如果内存模式非持久化也需要限制有对应的RuntimeMaxUse和RuntimeMaxFileSize参数[Journal] RuntimeMaxUse200M RuntimeMaxFileSize50M5.3 手动清理日志journalctl --vacuum有时候日志已经满了你想立即清理不用等服务自动回收。journalctl自带--vacuum-*系列参数# 只保留最近2天的日志 journalctl --vacuum-time2d # 只保留500MB日志 journalctl --vacuum-size500M # 只保留最近5个文件 journalctl --vacuum-files5--vacuum-time2d这个命令我几乎每次磁盘告警都用。它会把超过2天的日志文件删除保留2天内的所有记录。生产环境想保留更久按星期算就写4week或30d。还有两种快速清空方式# 删除所有归档日志保留当前正在写入的文件 journalctl --rotate journalctl --vacuum-time1s # 彻底清空所有日志 sudo rm -rf /var/log/journal/* sudo systemctl restart systemd-journald--rotate先手动触发日志文件轮转让正在写的文件先归档再配合vacuum效果更好。rm -rf /var/log/journal/*后重启journald日志库就是全新的但历史日志全部不可恢复操作前想清楚。5.4 推荐的运维配置模板综合全局我给生产服务器推荐这套journald配置[Journal] # 持久化 Storagepersistent # 总容量上限 SystemMaxUse2G # 单个文件大小 SystemMaxFileSize128M # 最大保留时间可选 MaxRetentionSec30day # 压缩默认开启 CompressyesCompressyes默认开启日志文件会以压缩形式存储实际占用比想象中小得多。2G总量在大部分业务服务器上足够覆盖数周的完整日志。注意改完配置文件后必须重启systemd-journald才生效而且重启时它会把已存在的文件自动做一次rotate。5.5 排查日志增长异常的方法日志吃满磁盘往往是某个服务在以极高速率刷日志。定位元凶用这条命令按日志来源分组并统计条数journalctl --since today -o no-pager --outputjson | jq -r ._COMM | sort | uniq -c | sort -nr | head -20如果jq没装可以直接用awk处理verbose输出。统计后你会发现排行前几名的通常就是日志轰炸的源头再去那个服务对应的Unit里下针对性配置而不是一刀切限制所有日志。6. 实战排查案例用journalctl定位Nginx启动失败讲完理论知识用一个真实的排查案例把journalctl的完整操作链串一遍。这个案例发生在某次线上部署Nginx突然启动不了。6.1 故障描述某天下午同事执行systemctl restart nginx后服务状态显示为failed。他的第一反应是看/usr/local/nginx/logs/error.log但新配置的Nginx是从系统源安装的日志文件只有access.log和error.logerror.log里只有短短几行请求错误没有启动错误。看journalctl -u nginx立刻看到问题所在Jan 15 14:32:10 web01 systemd[1]: Starting A high performance web server and a reverse proxy server... Jan 15 14:32:10 web01 nginx[12345]: nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use) Jan 15 14:32:10 web01 systemd[1]: nginx.service: Main process exited, codeexited, status1/FAILURE“Address already in use”80端口被占了。这就是典型的端口冲突。6.2 追查端口占用者拿到了报错我立刻检查80端口到底被谁占着sudo ss -ltnp | grep :80结果显示一个PID为2345的进程占用了80端口而且它不属于nginx。继续拿进程号2345查journalctl _PID2345 --since today | tail -50用_PID2345把进程2345的所有日志拉出来发现这是一个残留的旧版Nginx master进程从三天前就一直在运行。这台机器之前用编译方式装过一个Nginx后来改用apt安装旧进程没停干净新服务启动时自然抢不到端口。6.3 处理与验证清掉旧的master进程sudo kill 2345稳妥一点先用kill -TERM不行再kill -KILL。重新启动Nginxsudo systemctl restart nginx sudo systemctl status nginx确认服务状态是active (running)。再次用journalctl验证最终启动日志journalctl -u nginx --since 5 min ago -p info | tail -20输出里正常出现Started A high performance web server and a reverse proxy server说明服务真正启动成功。6.4 这个案例带给我的经验整个排查过程不超过五分钟比翻error.log快得多。三个关键点值得记住服务启动失败先journalctl -u 服务名 -n 50不要一头扎进应用自己的日志文件里ss -ltnp查端口占用只是第一步用journalctl _PID反查进程的历史才能确定它是不是残留进程凡是配置文件没改服务突然起不来的问题十有八九和端口冲突、权限变化、依赖服务没启动有关这三类问题journalctl都能直接看到报错原因7. 日常运维中的journalctl使用习惯最后分享一下我在实际使用中沉淀的几个习惯不一定适合所有人但长期用下来确实减少了很多无谓操作。7.1 把常用过滤写成别名排查服务问题时我常敲那串服务时间级别组合。把这些固化成Shell别名效率直接翻倍。以bash为例在~/.bashrc中加alias jlogjournalctl -p warning --since 1 hour ago -o no-pager alias jnginxjournalctl -u nginx -f alias jerrjournalctl -p err -n 50 --no-pager alias jbootjournalctl -b -p err --no-pagerjlog查最近1小时所有警告级别以上的日志jnginx实时盯Nginxjerr看当前最新错误jboot查本次启动的错误。用别名把高频查询固定下来每天排查问题的开场白就变成了敲一两个字母的事。7.2 用cron定时导出关键日志journalctl默认保留窗口再长也不能替代定期归档。我一般每天凌晨定时导出关键Unit的日志格式带上日期方便后续分析# crontab -e 0 2 * * * journalctl -u nginx --since yesterday -o short-iso /backup/nginx/nginx_$(date \%F).log这里short-iso格式带完整时间戳适合后续脚本解析。为避免%在cron中的转义问题我在crontab里写了\%F。对高可用要求高的服务器建议把/var/log/journal整个目录挂到独立磁盘或网络存储上防止系统盘损坏连带日志丢失。这一点在事故复盘时价值极大。7.3 journalctl配合systemctl status的快速检查法我的习惯是如下组合systemctl status nginx journalctl -u nginx -n 30 --no-pager第一行看服务当前活没活着第二行看它最近在干什么。两个命令间隔不到一秒就能执行完但信息量远超单看status。这在服务还活着但响应缓慢的场景里特别好用——status显示active (running)不代表一切正常日志里可能已经刷满了超时告警。7.4 遇到日志缺失时的排查方向有时journalctl查不到某条预期中的日志先别急着怀疑工具。按顺序检查journalctl -b是不是启动了持久化如果日志只在内存重启后就没了服务是把日志写进自己的文件还是交给journald有些程序如编译安装的Nginx默认自己写文件根本不走journald如果服务通过syslog转发日志检查/etc/rsyslog.conf里的转发规则有没有和journald冲突用journalctl -o verbose确认日志是否真的存在只是没被默认输出格式显示出来多数时候问题都出在服务本身不走journald这个原因上而不是journald坏了。8. 写在最后的体会journalctl看上去只是一条查询命令但它彻底重新定义了Linux日志的消费方式统一入口、结构化字段、灵活过滤、持久化可配。从第一次用journalctl -u nginx -f实时盯着服务日志起我就没再碰过各服务的分散日志文件。踩过几次坑之后我的核心建议是一定要在生产环境上持久化journal并且显式配置容量上限。否则你可能先经历重启后什么日志都查不到的茫然再经历磁盘被日志刷爆的恐慌。这两个问题说起来都是配置一行的事但真遇上了代价都是事故级别。如果你还没试过journalctl的-o verbose找个空闲时间随便挑一个服务把这几个字段翻一遍。当你看到每条日志背后完整的元数据时大概就能理解为什么Linux日志管理的未来一定要走结构化道路。至于我本人现在排查问题的第一条命令永远是journalctl没有例外。
返回列表