2、日志分析基础:日志级别、日志格式、常用日志框架
做接口超时排查,第一步不是看代码,而是看日志。说句实在话,很多同学看到日志文件就头大。密密麻麻的文本,完全不知道该从哪下手。今天我就带你把日志这块理清楚,让它变成你手里最顺手的工具。
2.1 日志级别:重要信息的筛选器
日志级别这东西,说白了就是给信息贴个标签。不同标签代表不同紧急程度。我刚开始接手线上问题时,最爱干的事就是把所有级别都打开,结果呢?日志文件一天十几个G,真要查问题时反而找不着北。
下面是Java生态里最常用的日志级别,我按优先级从低到高排了个表:
| 级别 | 含义 | 生产环境建议 | 我的经验 |
|---|---|---|---|
| TRACE | 最细粒度的跟踪信息 | 关闭 | 开发环境调试用,线上千万别开 |
| DEBUG | 调试信息,开发人员用 | 关闭(按需开启) | 排查特定问题时可以临时打开某个类的DEBUG |
| INFO | 正常运行状态信息 | 开启 | 请求入口、关键流程节点必须打印 |
| WARN | 潜在问题或异常情况 | 开启 | 超时重试、降级开关这类行为 |
| ERROR | 错误事件,但应用仍可运行 | 开启 | 接口超时、数据库连接失败等 |
| FATAL | 严重错误,应用可能崩溃 | 开启 | 我上次遇到OOM就是这个级别 |
2.2 日志格式:解析超时信息的密码本
光有级别不够,还得有统一格式。你想想看,如果每个开发人员按自己喜好写日志,那排查问题简直就是噩梦。我个人习惯用这种格式:
2024-01-15 14:23:45.678 [http-nio-8080-exec-3] INFO com.example.OrderService - [TRACE_ID:abc123] 调用用户服务超时, url=http://user-service/api/info, cost=3020ms
这个格式拆开看,每个字段都很讲究:
- 时间戳:精确到毫秒,方便定位时间点
- 线程名:排查并发问题必备,知道是哪条线程在干活
- 级别:一目了然知道严重程度
- 类名:快速定位是哪个类出的问题
- 追踪ID:这个最关键,我后面专门说
- 消息体:包含调用耗时、接口地址、错误详情
为什么追踪ID这么关键?一个请求可能经过服务A、B、C三个节点。没有追踪ID,你只能看到每个节点的日志片段。有了它,就能把一次请求的完整链路串起来。接口超时排查,追踪ID就是你的探照灯。
所以我在项目里强制要求:每个对外接口的请求入口和出口都必须打印INFO日志,格式固定为:
[调用方] -> [接口名] -> [请求参数] -> [耗时] -> [结果/异常]
为什么要这样做?因为排查超时时,我最需要的信息就是两个:谁调了谁,花了多久。格式统一后,写个简单的grep和awk脚本就能统计出所有超时接口。
2.3 常用日志框架:工具选对了,事半功倍
Java生态里日志框架不少,但主流就那几个。我这些年换过好几套方案,说下我的真实体会。
| 框架 | 优势 | 劣势 | 适用场景 |
|---|---|---|---|
| Log4j2 | 性能极好,异步日志出色 | 配置相对复杂 | 高并发、大流量系统 |
| Logback | Spring Boot默认,上手快 | 异步性能略逊Log4j2 | 中小型项目、快速开发 |
| SLF4J | 门面模式,统一接口 | 需要绑定具体实现 | 所有Java项目(强烈推荐) |
我现在最喜欢的是SLF4J + Logback组合。SLF4J提供接口,Logback负责实现。换框架时只需要换依赖,不用改业务代码。
给你看一个我常用的Logback配置片段,专门针对接口超时场景:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 超时日志单独分文件 -->
<appender name="TIMEOUT" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/timeout.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/timeout.%d{yyyy-MM-dd}.log.gz</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level - %msg%n</pattern>
</encoder>
</appender>
<!-- 超时相关类单独配置级别 -->
<logger name="com.example.timeout" level="INFO" additivity="false">
<appender-ref ref="TIMEOUT"/>
</logger>
<root level="INFO">
<appender-ref ref="MAIN_LOG"/>
</root>
</configuration>
这样做的好处:把超时日志单独隔离到一个文件。正常业务日志和超时日志不再混在一起。我排查超时问题时直接看timeout.log,效率翻倍。
2.4 知识体系总览
说了这么多,我把这章的核心逻辑梳理成了一张图。你顺着这个脉络理解,后面几章的内容就顺了。
你看这个图,从上到下是一个完整的链路。日志产生后,经过级别筛选,按固定格式记录,再由日志框架处理,最后输出到指定位置。每一步都有讲究。
我在做接口超时排查时,拿到日志文件后,第一件事就是确认这几个要素:级别是否合理?格式是否完整?框架配置是否正确?这三个基础问题搞清楚了,后面的分析才能站得住脚。