
日志这个东西平时写代码的时候没人在意真正出问题的时候恨不得把它供起来。我见过太多团队在日志这件事上走极端要么干脆不写排查问题全靠猜要么什么日志都往文件里塞一台机器一天跑出几十GB的日志磁盘直接打满服务挂了还不知道怎么回事。这篇内容就围绕“日志”这个主题把开发阶段的优化技巧、常见的坑以及线上排查时那些真正管用的命令一次性讲清楚。不管你是后端开发、运维还是刚入门没多久的测试同学只要平时需要跟日志打交道这篇文章应该能帮你省下不少冤枉时间。1. 日志管理与优化为什么这么重要先别急着看命令先想清楚一个问题我们到底为什么需要日志很多人对日志的理解就是“出错了用来查的”但实际上日志的价值远不止排障。我还是先把日志这件事拆开讲清楚后面所有的优化和排查手段都离不开这个底层认知。1.1 日志的核心价值排障只是最低要求日志的第一层价值自然是为问题排查留下线索。服务报错了、接口超时了、数据对不上了没有日志就只能人肉猜。第二层价值是行为记录系统在什么时间点发生了什么事情用户做了哪些操作这类审计和追溯能力在很多合规场景下是硬性要求。第三层价值则是趋势分析。通过日志可以看到系统的实时状态比如错误率上升、某个接口的耗时变长、某个客户端的请求量突增。这套玩法再往后走一步就是以日志和指标为基础的监控告警、以及使用类似 ELK 或 Loki 这类日志系统做数据分析和检索。如果只能记住一句话日志是系统在磁盘上留下的“病历本”好的日志能直接告诉医生病情差的日志除了占地方连病因都看不出来。1.2 为什么不是越多越好很多人有个直觉日志当然越详细越好啊万一哪天要用呢这个想法在开发环境里勉强说得通但放到生产环境完全是个灾难。每行日志都要走一次 IO 写入磁盘日志量一大磁盘 IO 和 CPU 都会被拖累。我实测过一个 Java 服务调低了 Maven 的日志级别从 DEBUG 改回 INFO接口的 QPS 和响应时间立刻好了一截。日志写得太多第一个伤害的是性能。第二个伤害是磁盘成本。按一台机器每天产出 20GB 日志算一个 20 台机器的集群一天就是 400GB一年就是 140 多 TB光存储成本就够买几台高配服务器了。要是哪天日志把磁盘写满数据库写不进去、服务直接挂掉这种事故在运维圈里太常见了。第三个伤害容易被忽略日志太多会导致排查效率直线下降。日志是给人看的关键信息被淹没在海量无意义的输出里和没有日志其实是同一种结果。机器可以秒级过滤出海量日志可人的精力是有限的。所以日志优化的核心思路是在正确的时间、正确的位置输出正确量的信息。这句话听起来很虚但下面每一节都是在教你怎么把这句话落到实处。2. 开发阶段就应该做好的日志优化等到线上再优化日志往往已经晚了。开发阶段写好日志是最好的“省钱”方式。这一节我把从日志分级、日志格式到日志框架的选择拆开讲这些都是日常开发里最容易忽略又最影响交付质量的部分。2.1 日志分级DEBUG、INFO、WARN、ERROR 到底怎么用日志分级是整个日志规范的地基。我见过不少团队把 ERROR 当 INFO 打也见过有人把业务状态变化全打在 DEBUG 里导致生产环境什么都看不到。简单说下我的习惯ERROR系统级错误当前请求无法正常完成必须立刻人工关注。比如数据库连不上、接口调用失败、关键数据丢失。WARN有风险但不影响主流程。比如重试了三次才成功、缓存穿透、某个非关键字段解析失败。INFO关键业务节点。用户注册成功、订单创建成功、任务执行完成这类日志适合写 INFO。DEBUG开发期用来定位问题里面可以打非常详细的中间值、函数入参出参但发布到生产前必须关掉。这个分级原则对应到框架配置上生产环境一般开到 INFO开发环境开到 DEBUG。有些团队会做动态调整通过配置中心在线上把某个服务的日志临时调到 DEBUG排查完再改回来这个思路很实用。以 Java 的 Spring Boot 为例常用的配置是在 application.yml 里写logging: level: root: INFO com.example.business: DEBUG这样只在业务包下开启 DEBUG框架自带的日志还是 INFO不会把无关内容全打出来。类似的思路在 Python 里用 logging 的dictConfig也能配出来。值得注意的是和日志级别相关的报错排查常常出现在特殊环境里。比如最近看到有人遇到虚拟机创建快照时报“使虚拟机处于静默状态时出错”最后查日志发现就是磁盘满了快照写入失败。如果日志本身没有分级和容量控制这种问题基本只能碰运气。2.2 日志格式固定的重要性日志格式不统一是日志规范里最容易被忽略的问题。有人打印一行直接写成功了有人用中文冒号有人用英文冒号还有人把多行 JSON 整个塞进日志里。等你哪天把这些日志捞到中心化平台比如 ELK 或 Loki里想按字段检索就知道什么叫绝望了。我建议日志格式至少包含这几个字段时间戳精确到毫秒包含时区日志级别服务名/模块名traceId全链路追踪 ID没有的话至少是请求 ID业务关键信息具体描述用一段伪代码表示就是2025-01-15 10:23:45.123 INFO [order-service] [traceId6ba7b810-9dad-11d1] 订单创建成功 orderId1001 userId88有了这个格式至少能回答几个灵魂问题这条日志是什么时候出的、是哪个服务出的、属于哪一次请求、业务对象是谁。traceId 尤其重要没有它一个跨服务请求出了问题你只能在多个服务之间来回 grep效率极低。2.3 如何选择日志框架Java、Python、C、C# 场景下的建议不同语言的日志框架选型其实遵循同样的原则输出可控、性能可接受、上下文传递方便。Java首选 Logback 或 Log4j2配合 SLF4J 门面。Log4j2 的异步日志性能更好但 Logback 配置简单、兼容性稳按照我的经验没有特殊性能需求就无脑选 SLF4J Logback。Python标准库 logging 其实已经够用配合 dictConfig 可以完成非常精细的配置。需要更高级的功能再考虑 loguru它的输出格式、颜色、旋转记录做得更顺手。Cspdlog 是社区共识头文件引入、支持异步、支持轮转日常开发基本无脑选。C#通常直接用微软的 ILogger 抽象或者搭配 NLog、Serilog 做结构化日志。Serilog 和 OpenTelemetry 的配合在云原生场景里更顺手。框架选型的关键不光是好不好用还要看团队的维护成本和生态兼容性。另外日志轮转这个功能选型时必须重视后面专门说坑。2.4 日志内容的坑不该打的别打打出来的要有用日志内容的设计比想象中更难。最常见的问题是打印了没用信息比如在循环里打了每一条中间日志。我见过一个数据同步程序处理 10 万条数据打了 10 万行 DEBUG直接把日志文件打到几个 GB排查问题的时候连文件都打不开。另一个极端是不敢打日志。业务关键节点比如订单状态从待支付改成已支付这类信息一定要有 INFO 日志记录。出了纠纷要溯源的时候日志就是你唯一能拿出来的证据。还有两个雷区不要打敏感信息。密码、token、手机号、身份证号这些一旦进了日志等于明文保存合规上都过不去。日志脱敏这件事应当在日志工具层做而不是指望每个开发都自觉。不要把大对象整个序列化打进日志。比如打印整个请求报文、整个响应体日志量瞬间翻几倍。真要打截断到关键字段就好。还有个小技巧VS 调试的时候可以把调试信息同时输出到日志文件和控制台窗口方法是在代码里同时挂一个 Debug/File 输出目标这样本地调试不用每次都开调试器看变量效率会高很多。对于 Android 开发调试时用adb logcat抓取日志也是同理先按包名和级别过滤再抓避免全局日志刷屏。3. 线上日志管理那些常见的坑写完代码只是第一步日志真正的挑战在线上。我在这里把运营维护阶段常见的坑集中过一遍每条都是自己或身边同事踩过、折腾过的全都是真金白银换来的经验。3.1 日志磁盘被写满轮转、清理与 binlog 的取舍日志把磁盘写满这个话题每次提都有人点头。最典型的场景是 Elasticsearch 的 log 目录、Nginx 的 access.log或者 Java 服务没有配置滚动策略的 log 文件一跑就是几个月一个文件增大到几十 GB最终磁盘 100%。这类问题的标准解法是日志轮转log rotation。Linux 下用 logrotate 就能定期切割日志配置大概长这样/path/to/app/logs/*.log { daily rotate 30 compress delaycompress missingok notifempty copytruncate }这段配置每天切一次日志文件保留最近 30 份旧的自动压缩使用 copytruncate 保证服务进程持有的文件句柄不会因为重命名而失效。这个配置是所有 Java、Python 服务日志的保命底线。数据库的日志也需要单独关注。MySQL 的 binlog 是二进制日志很多人问“binlog 日志可以删除吗”答案是能删但必须按规则删不能直接 rm。先检查log_bin是否开启、当前日志过期时间和保留策略然后在 MySQL 里执行PURGE BINARY LOGS BEFORE NOW() - INTERVAL 3 DAY;或者设置expire_logs_days。直接删文件会导致主从同步错乱、崩溃恢复失败那才是真正的大事故。同理慢查询日志如果不设置轮转上线没多久就能吃光磁盘。3.2 Windows 生态里的特殊坑安全日志、开关机日志与 NTFS 的诡异读写虽然大多数服务器跑在 Linux 上但 Windows Server 依然有不少存量。Windows 安全日志记录登录成功/失败、权限变更等事件开关机日志则在系统日志里。这类日志的排查统称“Windows 事件日志”用事件查看器或者 PowerShell 都能查。Windows 上最诡异的问题是 NTFS 卷日志$LogFile在后台一直读写磁盘。这个日志是 NTFS 文件系统用来做事务记录的通常不会造成明显的性能问题但如果你用工具查看磁盘活动发现%占用一直很高而且盯不到具体是哪个应用在写多半就是系统在写卷日志。常见诱因包括快速启动关联的休眠文件写回、虚拟内存页频繁交换、Windows Search 索引器扫描文件。排查手段是先禁用索引服务、关闭快速启动再观察是不是消失。如果还不行再考虑检查磁盘错误。这类问题没有银弹只能一个个排除但方向对了总能找到根因。虚拟化场景也经常遇到 Windows 日志相关的坑。VMware 里创建快照时报“使虚拟机处于静默状态时出错”这个报错通常和虚拟机没有安装 VMware Tools 或 Tools 服务异常、系统卷影服务不可用有关有时日志系统里会有详细线索。排查思路是先在客户机事件日志里查卷影复制服务VSS的报错修复后再打快照否则快照要是在静默状态下做数据一致性真没法保证这个坑踩过的人都懂。3.3 网络和捕获类工具使用不当带来的隐性问题日志不只是应用日志还有网络设备的日志。Cisco、华为交换机默认开启大量 console 日志登录设备的时候屏幕上刷屏有时候操作都看不清。很多华为设备可以直接在系统视图下执行命令关闭信息中心输出比如undo info-center enable或单独关闭 console 方向的日志输出info-center source default channel console log level warning这样操作界面就清净了。还有一类隐性问题出现在抓日志的方式上。比如 Putty 这类终端工具不少人开着“保存所有会话输出”功能一开就是一整天文件越滚越大终端越来越卡。其实 Putty 支持只保存当前窗口的输出也可以通过“Session Logging”配置日志轮转和会话结束才落盘避免无意义的文件增长。实践下来常用场景是把网络设备的screen-length 0和 putty 配合保存设备日志时一次性完整输出而不是等终端回卷。另一个网络排查常见操作是持续 ping 记录日志用ping 目标IP ping.log或者循环 ping 加时间戳输出到文件。写法上建议用这种能带时间戳的命令ping -i 1 192.168.1.1 | while read line; do echo $(date %Y-%m-%d %H:%M:%S) $line; done | tee ping_$(date %Y%m%d).log这里-i 1是 ping 的间隔秒数Linux 默认 1 秒Windows 默认 1 秒但没必要改Windows 下可以用ping -t如果想要时间戳就得借助powershell或者用 tcpdump 更直接。这类日志在网络抖动、防火墙丢包的排障里特别有用问题发生时可以先看这个日志判断是链路问题还是应用问题。3.4 日志采集链路的坑filebeat、Loki、ELK 的常见使用误区日志从分散的机器统一收上来通常会走采集代理 消息队列/对象存储 检索平台的架构。常见的组合有 Filebeat Elasticsearch KibanaELKPromtail Loki Grafana还有 Fluentd、Vector 这些。先说 filebeat。很多人直接下默认配置就能跑但它在没有做好配置的情况下最容易出几个问题harvester重新读取文件文件被 logrotate 重命名后filebeat 如果没有识别到会从旧文件重新读一遍相当于日志量翻倍。解决办法是配置close_renamed: true和clean_inactive并且让文件路径和 logrotate 策略匹配。字段类型冲突同一字段在这次日志里是 string下次是 numberES 直接拒绝写入。解决思路是在索引模板里预先定义字段类型。时区错乱filebeat 采集到的日志时间字符串里没有时区时送到 ES 的时间可能差 8 小时。再说 Loki。Loki 的标签设计是决定查询性能的关键。理解这个之前先说 ELKELK 的检索能力强但成本高、资源占用高。 Loki 的核心思路是压缩索引用标签过滤日志流查询时再全量扫描匹配的日志因此成本低很多。但正因为这个设计标签必须克制。你要是把每次请求的 traceId 都做成 Loki 标签索引膨胀不说写入也会变慢。正确的做法是用服务名、环境、日志级别这种维度做标签具体业务字段留在日志内容里查询时用 LogQL 的 parser 提取和过滤即可。最后说日志转发。像 Rocky9 这类系统的 rsyslog 配置并不复杂但容易踩坑的时端口、协议和防火墙。配置/etc/rsyslog.conf开启imudp和imtcp再在/etc/rsyslog.d/下建规则把*.* 远端服务器:514写到转发列表里。注意是 TCP单个是 UDP。之后重启 rsyslog本机日志就转发到远端 syslog 服务器了。这部分的坑通常在目标端接收的 syslog 服务器如果没开对应端口或防火墙拦截日志丢了还不自知。4. 线上排查必须学会的日志命令理论知识讲完了下面是最实用的部分。我按线上排障的实际场景把日志相关的命令整理了一遍平时多练练关键时刻能救命。4.1 Linux 日志排查基础命令组合Linux 日志都在/var/log/下面但真正的业务日志位置按应用来定。排障的第一步是找到日志文件在哪这一步可以用下面几个命令# 查看应用的日志路径 ls -lht /var/log/ # 查看某个进程的日志句柄指向的文件 lsof -p PID | grep -E log|out|err日常排查时最常用的是 tail、grep、awk、sed 的组合。直接看最新日志# 实时跟踪日志输出 tail -f /var/log/myapp/app.log # 查看最后500行 tail -n 500 /var/log/myapp/app.log # 按关键字过滤日志 grep ERROR /var/log/myapp/app.log | tail -n 100 # 带上下文过滤输出匹配行的前5行和后10行 grep -n -B 5 -A 10 NullPointerException /var/log/myapp/app.log # 按时间段过滤需要日志本身带时间字段 grep 2025-01-15 10:2[0-9]: /var/log/myapp/app.log组合使用的方式也很灵活。比如要看某个用户 ID 一天的请求轨迹可以先grep userId88 app.log找到 traceId再grep traceId...把整条请求链路串起来。没有 traceId 就只能靠时间戳和 IP 猜麻烦得多。如果是压缩过的历史日志比如app.log.2.gz用zcat或zgrep就能直接查不用解压zgrep ERROR /var/log/myapp/app.log.2.gz4.2 Java 服务、Nginx、Redis、MySQL 慢查询日志定位技巧Java 服务排查有一个绕不开的命令就是看堆栈。如果日志里有OutOfMemoryError或线程卡死的迹象jstack可以直接把 Java 进程的线程快照导出来。# 导出线程快照 jstack -l PID thread_dump_$(date %s).txt # 查看垃圾回收日志 jstat -gcutil PID 1000 10 # 输出堆转储注意会暂停应用生产环境慎用 jmap -dump:formatb,fileheap.hprof PIDJava 日志本身如果使用 Logback动态调整某一个包的日志级别可以直接读配置也可以在配置中心中改。生产松查问题的临时手段是使用 Spring Boot Actuator 暴露的/loggers端点POST 修改级别用完再改回来。Nginx 的日志路径一般写在 nginx.conf 里的access_log和error_log指令。常见的排查命令是看某个 IP 的请求数# 统计访问量最高的前 20 个 IP awk {print $1} /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -n 20 # 统计最近 5 分钟内返回 500 的次数 awk -v date$(date -d 5 minutes ago %d/%b/%Y:%H:%M) $4 [date $9 500 {count} END {print count} /var/log/nginx/access.logRedis 日志本身默认打在 stdout 或指定日志文件里排查时主要看的是 slowlog。Redis 自带SLOWLOG GET 100命令直接看超过阈值的命令列表。如果发现有很多KEYS、HGETALL这种大 key 操作Cache 雪崩和 RT 飙升就找到根源了。MySQL 的慢查询日志排查是日常必修课。先确认是否开启SHOW VARIABLES LIKE slow_query_log%; SHOW VARIABLES LIKE long_query_time;如果没开启可以在线开启要有 SUPER 权限SET GLOBAL slow_query_log ON; SET GLOBAL long_query_time 2; -- 超过2秒的SQL记录 SET GLOBAL slow_query_log_file /var/log/mysql/mysql-slow.log;之后查看慢查询日志最直接的方式是使用mysqldumpslow工具mysqldumpslow -s at -t 20 /var/log/mysql/mysql-slow.log这个命令能输出执行时间最长的 20 条 SQL 以及它们的平均锁时间、平均扫描行数是优化 SQL 的第一步。再配合EXPLAIN看执行计划慢查询通常能有明确的治理方向。4.3 网络与系统日志排查的命令组合系统级的日志很多人忽略/var/log/messages或/var/log/syslog。出现了进程被杀通常因为 OOM、文件系统只读、网卡 down、磁盘 IO 错误这类问题都会记录在这些系统日志里。# 查看最近的系统内核日志 journalctl -xe # 查看某服务最近的日志 journalctl -u nginx --since 2025-01-15 10:00:00 --until 2025-01-15 10:30:00 # 查看上一次启动的全量日志排查 CPU 飙升、文件系统挂载问题 journalctl -b -1网络设备交换机、路由器、防火墙)如果本身支持 syslog建议把配置加上# 以华为交换机为例 info-center loghost 192.168.100.10 source ip-address 192.168.100.1 info-center loghost 192.168.100.10 facility local0把loghost指向公司内部搭的 syslog 服务器之后设备所有日志都能统一收上来。这一步配置很简单真到排查网络故障的时候会发现省力太多。持续监控一个目标是否可达最稳妥优雅的命令是ping -i 1 IP | tee ping.log一般不建议用循环脚本因为用循环时时间戳、长连接状态不稳定但如果要长期记录断点时间建议换个方式while true; do date %Y-%m-%d %H:%M:%S ping_monitor.log ping -c 1 -W 2 192.168.1.1 ping_monitor.log 21 || echo FAIL ping_monitor.log; sleep 1; done这个脚本每一次 ping 失败都会记录具体时间最终从 ping_monitor.log 里能直观看出哪段时间断了、断了几次比人工盯控制台强一百倍。4.4 集中日志平台上的检索技巧ELK、Loki 的实用查询日志收上进平台之后很多人反而不会查了。记住平台查询的核心逻辑就两个先过滤再聚合。不要把整天的日志全捞出来筛选条件越前置返回越快。ELK 环境里Kibana 的 KQL 查询可以这样写service.name: order-service AND log.level: ERROR AND message: timeout加上时间范围选择后再按字段聚合。比如想看所有 ERROR 集中在哪个节点service.name: order-service AND log.level: ERROR选左侧字段列表里host.name点击可视化就能看到错误分布。Loki 的 LogQL 查询套路则不同。Loki 里使用{service_nameorder-service} | ERROR来过滤流再用正则提取字段{service_nameorder-service} | ERROR | logfmt | duration 2slogfmt 解析器适用于keyvalue格式的日志JSON 日志用| json。这两种解析器是 LogQL 的核心学会了基本能处理日常绝大多数查询场景。配合 Grafana 的告警规则还可以实现“ERROR 日志连续 5 分钟出现 10 次”之类的自动告警。5. 日志排查中的常见问题速查表最后整理一个速查表把日常运维里最常见的日志相关问题和对应的排查思路放一起既能当备忘录也方便新人快速上手。下面的每一行几乎都是从真实事故里提炼出来的。症状常见原因快速排查命令 / 处理建议磁盘空间打满服务无法启动日志文件无轮转或日志量突增df -h查看空间du -sh /var/log/*定位大文件配置 logrotateJava 服务 OOM进程被杀堆内存不足查journalctl -k有无 OOM Killerjmap -heap PID看堆使用接口超时响应变慢SQL 慢查询或 GC 停顿MySQL 开启 slow query logmysqldumpslow -s atjstat -gcutil看 GCELK 里查不到某条日志filebeat 未采集到或过滤丢字段检查 filebeat 配置和 ES 索引 mappingcurl直接试写入 ES 看是否报错Loki 查询特别慢标签设计不合理扫描日志量过大减少标签增加时间范围过滤尽量用 Windows 磁盘莫名一直读写索引服务/快速启动/虚拟内存交换先禁用 Windows Search 和快速启动再观察磁盘活动VMware 快照失败提示静默状态出错VSS 服务异常或 Tools 损坏查看客户机事件日志的 VSS 报错重装 VMware Tools交换机系统日志刷屏console 上所有级别日志都输出华为 ensp/设备上执行undo info-center enable或者单独降低 console 通道级别慢查询日志文件太大开启 slow log 但没做轮转和保留设置long_query_time调高配置 logrotate 切割 slow log日志时间比本地时区晚8小时日志文件没带时区或容器时区错误统一记录 UTC8 或显式带上时区字段容器环境设置TZAsia/Shanghai安全审计要求查看登录记录Windows 安全日志 / Linux auth 日志wevtutil qe Security /q:*[System[(EventID4625)]]last和journalctl -u sshd日志排查还有一个容易被忽略的“隐藏技巧”注意看日志文件的属主和权限。如果使用logrotate切割日志后的新文件没有写权限应用服务会报“Permission denied”但你去看系统日志又看不到任何提示这种情况下应用会一直打日志失败然后你以为没有日志可以排查。类似这种小问题不加留神能浪费一整天。还有一个小经验排查线上问题之前先确认日志文件的 inode 是否被替换了。某些程序打开了一个日志文件后logrotate 把原文件改名程序还在往旧的 inode 上写导致你去查文件时发现最新日志一直不更新。这种时候用lsof -p PID | grep deleted能看到有句柄指向已删除的文件重启服务或者 signal 触发 reopen 就能解决。总结一下我的个人体会做开发和运维这些年最深刻的体会是日志是给“未来的自己”写的不是给“别人”看的。你永远不知道三个月后你会不会因为一条看起来不那么重要的日志救了整个通宵。写日志时多花两秒钟想想格式、级别和落盘策略线上排查就能少熬好几个通宵。最后再分享一个很有用的习惯每次上线前花 10 分钟检查一下生产环境的日志级别、轮转策略、磁盘余量把这些前置检查写进发布 checklist。很多人忽略这一步但真正保障系统稳定的往往就是这些不起眼的功夫。