2、日志分析基础:日志级别、日志格式、常用日志框架

做接口超时排查,第一步不是看代码,而是看日志。说句实在话,很多同学看到日志文件就头大。密密麻麻的文本,完全不知道该从哪下手。今天我就带你把日志这块理清楚,让它变成你手里最顺手的工具。

2.1 日志级别:重要信息的筛选器

日志级别这东西,说白了就是给信息贴个标签。不同标签代表不同紧急程度。我刚开始接手线上问题时,最爱干的事就是把所有级别都打开,结果呢?日志文件一天十几个G,真要查问题时反而找不着北。

下面是Java生态里最常用的日志级别,我按优先级从低到高排了个表:

级别 含义 生产环境建议 我的经验
TRACE 最细粒度的跟踪信息 关闭 开发环境调试用,线上千万别开
DEBUG 调试信息,开发人员用 关闭(按需开启) 排查特定问题时可以临时打开某个类的DEBUG
INFO 正常运行状态信息 开启 请求入口、关键流程节点必须打印
WARN 潜在问题或异常情况 开启 超时重试、降级开关这类行为
ERROR 错误事件,但应用仍可运行 开启 接口超时、数据库连接失败等
FATAL 严重错误,应用可能崩溃 开启 我上次遇到OOM就是这个级别
我的小技巧:线上只开INFO以上级别。但如果某段时间某个接口频繁超时,我会临时把那个接口对应的类改成DEBUG,等收集到足够日志后再调回来。切忌全局开DEBUG,磁盘会爆炸的。
避坑指南:我曾经在生产环境忘记改回日志级别,结果一晚写了100多G日志,直接把磁盘撑炸了。从那以后,我每次上线前都要检查logback.xml里root level是不是INFO。

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,效率翻倍。

还有一个细节:超时日志文件不要太大。我一般设定每个文件最多200MB,保留30天。超过30天的压缩存储。磁盘空间不够的时候,优先清理老的压缩包,保留最近7天的原始文件。

2.4 知识体系总览

说了这么多,我把这章的核心逻辑梳理成了一张图。你顺着这个脉络理解,后面几章的内容就顺了。

日志分析基础核心结构 日志产生 日志级别:TRACE/DEBUG/INFO/WARN/ERROR 日志格式:时间|线程|级别|类名|追踪ID|消息 日志框架:SLF4J + Logback / Log4j2 日志输出:控制台 / 文件 / 集中平台 本章关键点 • 超时问题只看ERROR和WARN • 格式必须包含追踪ID • 超时日志单独分文件 • 推荐SLF4J + Logback • 异步日志提升性能 • 配置滚动策略防磁盘满

你看这个图,从上到下是一个完整的链路。日志产生后,经过级别筛选,按固定格式记录,再由日志框架处理,最后输出到指定位置。每一步都有讲究。

我在做接口超时排查时,拿到日志文件后,第一件事就是确认这几个要素:级别是否合理?格式是否完整?框架配置是否正确?这三个基础问题搞清楚了,后面的分析才能站得住脚。