1. 日志基础:什么是日志、日志的级别、日志的作用与价值

好,咱们直接开始。日志这东西,说白了就是程序运行时的「黑匣子」。你想想看,一个系统跑在生产环境,你没法像调试本地代码那样打断点。那出了问题怎么办?靠的就是日志。

我个人习惯把日志看作系统的「体检报告」。它记录了什么时间、在哪个模块、发生了什么事。没有日志,你就像蒙着眼睛修车——全靠摸。

1.1 什么是日志

日志就是程序在运行过程中输出的、带有时间戳的文本记录。它可以是简单的「服务启动成功」,也可以是复杂的协议报文转储。

举个例子,一个 HTTP 请求进来,日志可能会记录:

2025-01-15 14:23:01.456 [http-nio-8080-exec-3] INFO  com.example.OrderController - 收到下单请求,订单号: ORD20250115001
2025-01-15 14:23:01.789 [http-nio-8080-exec-3] DEBUG com.example.OrderService - 查询用户余额: userId=1024, balance=500.00
2025-01-15 14:23:02.012 [http-nio-8080-exec-3] WARN  com.example.PaymentService - 支付接口响应超时,触发重试,第1次
2025-01-15 14:23:02.345 [http-nio-8080-exec-3] ERROR com.example.OrderController - 下单失败: 支付超时,订单已回滚

你看,每一行都包含了时间、线程、级别、类名和具体消息。这就是日志的原始形态。

我的经验: 刚入行时我总觉得日志是写给机器看的,后来才发现——日志首先是写给人看的。尤其是凌晨三点被电话叫醒的时候,一份结构清晰的日志能让你少掉一半头发。

1.2 日志的五个级别

日志级别不是随便定的。它有一套通用的优先级体系,从低到高依次是:

级别 含义 典型场景 生产环境是否开启
DEBUG 调试信息 变量值、函数入口/出口、SQL 参数 通常关闭
INFO 普通信息 服务启动、请求到达、任务完成 开启
WARN 警告 接口超时、资源即将耗尽、降级触发 开启
ERROR 错误 数据库连接失败、业务异常、请求处理失败 开启
FATAL 致命错误 内存溢出、配置加载失败、进程即将退出 开启

这里有个关键点:级别是过滤用的。你设置日志级别为 WARN,那么 DEBUG 和 INFO 的日志就不会输出。这能帮你控制日志量,避免磁盘被撑爆。

1.3 每个级别怎么用

DEBUG —— 开发阶段的主力。我在项目中习惯把关键变量的值、分支判断的结果、循环的迭代次数都打出来。但注意,千万别把敏感信息(密码、token)打进去。我曾经见过同事把用户密码明文打到了 DEBUG 日志里,结果被安全审计抓了个正着。

INFO —— 系统运行的「脉搏」。服务启动、请求到达、任务完成、定时调度执行,这些都应该用 INFO 记录。它告诉你系统在正常工作。

WARN —— 潜在风险的「预警灯」。比如接口响应时间超过阈值但还没失败、磁盘使用率达到 80%、重试机制被触发。WARN 不是错误,但值得关注。

ERROR —— 需要立即处理的问题。数据库连不上、请求处理抛异常、消息消费失败。ERROR 日志通常意味着功能受损,需要人工介入。

FATAL —— 最严重的级别。进程即将退出、JVM 内存溢出、核心配置加载失败。FATAL 一出,基本意味着系统不可用。

避坑指南: 我曾经接手过一个项目,所有异常都打成了 FATAL。结果日志里全是 FATAL,真正的致命问题反而被淹没了。记住:FATAL 是留给「程序活不下去」的情况的,别滥用。

1.4 日志的作用与价值

日志的价值,我总结为四个字:追、定、监、审。

  • 追(问题追踪):线上出 Bug 了,日志是第一手证据。通过日志可以还原请求链路、定位异常代码行、分析数据状态。
  • 定(系统定界):一个请求跨了 5 个服务,到底哪个环节慢了?日志里的时间戳能帮你精确到毫秒级。
  • 监(监控告警):ERROR 日志数量突然飙升,告警系统立刻通知你。没有日志,你连系统挂了都不知道。
  • 审(安全审计):谁在什么时间操作了什么数据?日志是合规审计的基石。

说白了,日志就是系统的「后悔药」。没有它,出了问题你只能靠猜。

1.5 日志体系结构图

下面这张图展示了日志从产生到消费的完整链路。我把它画出来,方便你建立整体认知。

日志体系结构图 日志产生层 应用程序代码 → 日志框架(Logback / Log4j2 / SLF4J) 日志级别过滤 DEBUG → INFO → WARN → ERROR → FATAL 日志输出层 控制台(Console) | 文件(File) | 数据库(DB) | 远程(Socket) 日志消费层 问题追踪 | 性能分析 | 监控告警 | 安全审计 每一层都可以独立配置,互不影响

嗯,这张图你看懂了吗?从上到下,日志从代码里产生,经过级别过滤,输出到不同目的地,最后被我们用来做各种分析。每一层都可以独立配置,这也是日志框架灵活的地方。

1.6 日志配置示例

光说不练假把式。下面是一个 Logback 的配置片段,展示了如何设置日志级别和输出:

<configuration>
    <!-- 控制台输出 -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <!-- 文件输出,按天滚动 -->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>/var/log/myapp/app.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>/var/log/myapp/app.%d{yyyy-MM-dd}.log</fileNamePattern>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <!-- 根日志级别 -->
    <root level="INFO">
        <appender-ref ref="CONSOLE"/>
        <appender-ref ref="FILE"/>
    </root>

    <!-- 某个包单独设置 DEBUG 级别 -->
    <logger name="com.example.myapp.service" level="DEBUG"/>
</configuration>
关键点:
  • 根日志级别设为 INFO,生产环境不会输出 DEBUG 日志
  • 某个包可以单独调低级别,方便调试
  • 文件按天滚动,保留 30 天,避免磁盘撑爆

1.7 日志的「坑」与建议

最后聊几个我踩过的坑,你注意避开:

  • 别打无意义日志:比如在循环里打「处理第 i 条数据」,数据量一大,日志文件直接爆炸。我见过一个项目,一天产生了 200GB 日志,全是这种废话。
  • 别吞异常:catch 块里只打日志不处理,等于掩耳盗铃。我曾经排查过一个线上问题,发现 ERROR 日志打了,但业务逻辑继续往下走,数据全乱了。
  • 别忘加时间戳:没有时间戳的日志,等于没有坐标的地图。你根本不知道问题发生在什么时候。
  • 别用 System.out.println:这玩意儿不走日志框架,没法控制级别,也没法归档。生产环境千万别用。

好了,日志基础就讲到这里。记住一句话:日志是你和系统对话的语言。写得好,系统帮你说话;写得差,系统对你沉默。