一、认识日志
1.1 为什么需要系统学习日志
日志是软件开发中不可或缺的组成部分。从初学编程开始,我们就接触到了最简单的日志输出方式------System.out.println()。这种方式虽然直观,但在实际项目开发中却显得力不从心。随着应用规模的扩大和业务复杂度的提升,我们需要更专业、更系统化的日志管理方案。
在项目开发过程中,日志不仅仅是调试工具,更是系统运行状态的"晴雨表"。它记录了程序执行的轨迹,帮助我们追踪问题根源、分析性能瓶颈、监控系统健康状态。特别是在分布式系统和微服务架构中,完善的日志体系更是保障系统稳定运行的关键。
1.2 日志的核心价值与应用场景
除了传统的问题排查功能外,现代日志系统在多个领域发挥着重要作用:
- 性能监控与告警:通过记录关键指标如响应时间、吞吐量、错误率等,可以实时监控系统运行状态。当指标超出预设阈值时自动触发告警,帮助运维团队快速响应潜在问题。
- 用户行为分析:日志记录了用户在系统中的操作轨迹,这些数据对于产品优化、用户体验改进具有重要价值。通过分析用户行为模式,可以优化产品功能设计。
- 业务决策支持:日志中蕴含的业务数据可以为决策提供依据。例如,通过分析访问日志可以了解用户偏好,指导内容推荐策略;通过交易日志可以分析销售趋势,优化库存管理。
- 安全合规审计:在金融、医疗等监管严格的行业,日志记录是满足合规要求的必要手段。完整的操作日志可以追溯每一个关键操作,确保业务流程的可审计性。
1.3 日志在安全审计中的重要性
在数字化时代,信息安全已成为企业运营的生命线。日志审计作为安全体系的重要组成部分,承担着多重职责:
首先,日志审计是发现安全威胁的重要手段。通过分析异常访问模式、频繁失败登录尝试等日志信息,可以及时发现潜在的攻击行为。其次,在发生安全事件时,完整的日志记录是调查取证的关键证据,能够还原攻击路径和影响范围。
从合规角度来看,国内外多项法规标准(如等保2.0、GDPR等)都对日志留存和审计提出了明确要求。企业需要建立完善的日志管理体系,确保日志的完整性、真实性和不可篡改性。
在实际应用中,日志审计不仅关注外部攻击,也关注内部风险。通过记录敏感数据的访问和操作,可以有效防止内部人员滥用权限或泄露信息。例如,在客户关系管理系统中,每一次客户信息的查询、修改都应该被详细记录,确保操作可追溯。
二、日志使用
大家可以先对比一下不同方法所打印的日志

通过上图,我们可以看到System.out.print打印的日志,比SpringBoot打印的日志缺少了很多信息。
因为SpringBoot内置了日志框架Slf4j,我们可以直接在程序中调用Slf4j来输出日志,具体操作请接着往下看
2.1 打印日志
Spring Boot 项目在启动的时候默认就有日志输出,如下图所示:

通过上图,我们可以看到 System.out.print 打印的日志,比 Spring Boot 打印的日志缺少了很多信息。
因为 Spring Boot 内置了日志框架 Slf4j,我们可以直接在程序中调用 Slf4j 来输出日志,具体操作请接着往下看。
打印日志的步骤:
- 在程序中得到日志对象。
- 使用日志对象输出要打印的内容。
2.1.1 在程序中得到日志对象
在程序中获取日志对象需要使用日志工厂 LoggerFactory,如下代码所示:
private static Logger logger = LoggerFactory.getLogger(LoggerController.class);
LoggerFactory.getLogger 需要传递一个参数,标识这个日志的名称。这样可以更清晰的知道是哪个类输出的日志。当有问题时,可以更方便直观的定位到问题类。
注意:Logger 对象是属于 org.slf4j 包下的,不要导入错包。

2.1.2 使用日志对象打印日志
日志对象的打印方法有很多种,我们可以先使用 info() 方法来输出日志,如下代码所示:
java
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class LoggerController {
private static Logger logger = LoggerFactory.getLogger(LoggerController.class);
@RequestMapping("/logger")
public String logger() {
logger.info("--------------要输出日志的内容----------------");
return "打印日志";
}
}
打印日志效果展示:

2.2 日志框架

SLF4J不同于其他日志框架, 它不是⼀个真正的日志实现, 而是⼀个抽象层, 对日志框架制定的⼀种规范, 标准, 接口. 所有SLF4J并不能独立使用, 需要和具体的日志框架配合使用
2.2.1 门面模式(外观模式)
门面模式(Facade Pattern),也称为外观模式,是一种结构型设计模式。它为子系统中的一组接口提供了一个统一的高层接口,使得子系统更容易使用。
在日志框架的上下文中,SLF4J 就扮演了"门面"的角色。它定义了一套统一的日志接口(API),而具体的日志实现(如 Logback、Log4j2、JUL 等)则作为"子系统"。应用程序代码只需要面向 SLF4J 这一套接口编程,无需关心底层具体使用的是哪种日志实现库。
这种设计带来了以下核心优势:
- 解耦:应用程序与具体的日志实现解耦。如果需要更换日志框架(例如从 Logback 切换到 Log4j2),只需更改依赖配置,业务代码无需任何修改。
- 简化使用:开发者只需学习一套 SLF4J API,即可操作各种日志框架,降低了学习成本和使用复杂度。
- 统一管理:通过一个统一的"门面"来管理所有日志操作,便于进行统一的配置、监控和扩展。
下图清晰地展示了门面模式在日志体系中的应用:

因此,SLF4J + 具体日志实现的组合,是 Java 日志领域践行门面模式的经典范例。
2.2.2 SLF4J框架介绍
SLF4J(Simple Logging Facade for Java,Java 简单日志门面)是 Java 社区中最广泛使用的日志门面框架。它由 Ceki Gülcü 创建,旨在为 Java 应用程序提供统一的日志 API,让开发者能够在不修改业务代码的情况下,灵活切换底层日志实现。
SLF4J 的核心设计理念可以概括为以下几点:
- 接口与实现分离:SLF4J 只提供一套标准的日志 API 接口,不包含具体的日志实现。这种设计使得应用程序代码与具体的日志框架(如 Logback、Log4j2、JUL 等)完全解耦。
- 向后兼容:SLF4J 在设计时充分考虑了与现有日志框架的兼容性。通过提供各种适配器(Adapter)和桥接器(Bridge),SLF4J 能够无缝集成几乎所有的 Java 日志框架,包括 Log4j、JUL(java.util.logging)、Jakarta Commons Logging 等。
- 性能优化 :SLF4J API 在设计上注重性能。例如,它提供了参数化日志输出方法(如
logger.debug("User {} logged in at {}", username, timestamp)),只有在日志级别启用时才会进行字符串拼接,避免了不必要的性能开销。
SLF4J 的组成模块主要包括:
- slf4j-api:核心 API 模块,定义了所有日志接口。这是应用程序唯一需要直接依赖的 SLF4J 模块。
- 具体实现绑定 :将 SLF4J API 绑定到具体日志实现的模块,例如:
- slf4j-log4j12:绑定到 Log4j 1.2.x
- slf4j-jdk14:绑定到 JDK 1.4+ 的 java.util.logging
- slf4j-simple:SLF4J 自带的简单实现(主要用于测试)
- 桥接器 :将其他日志框架的调用重定向到 SLF4J,例如:
- jcl-over-slf4j:将 Jakarta Commons Logging 调用重定向到 SLF4J
- log4j-over-slf4j:将 Log4j 调用重定向到 SLF4J
- jul-to-slf4j:将 java.util.logging 调用重定向到 SLF4J
典型依赖配置示例(Maven):
xml
<!-- SLF4J API -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.9</version>
</dependency>
<!-- 绑定到 Logback(推荐组合) -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.4.11</version>
</dependency>
为什么选择 SLF4J + Logback 组合?
在 Spring Boot 的默认配置中,SLF4J 与 Logback 是黄金搭档,原因如下:
- 原生集成:Logback 是 SLF4J 的原生实现,两者由同一作者开发,配合最为紧密,性能最优。
- 零配置启动:Spring Boot 提供了合理的默认配置,开箱即用。
- 丰富的特性:Logback 支持异步日志、条件化配置、自动重载配置文件、多种输出格式等高级功能。
- 向后兼容:通过桥接器,可以统一项目中可能存在的其他日志框架调用。
2.3 日志格式说明

2.4 日志级别分类
日志级别(Log Level)用于标识日志信息的重要性或严重程度。通过为日志消息分配不同的级别,我们可以控制哪些信息需要被记录、输出到何处(如控制台、文件),以及在不同环境(开发、测试、生产)下输出哪些级别的日志,从而实现灵活的日志管理和问题排查。
SLF4J 定义了以下几种常见的日志级别,从低到高依次为:
- TRACE:最详细的跟踪信息,通常用于记录程序运行过程中的每一步细节,如方法的进入退出、变量的值等。在开发阶段用于深度调试,生产环境通常关闭。
- DEBUG:调试信息,用于记录程序运行的关键节点、变量状态等,帮助开发者理解程序流程和定位问题。开发环境常用,生产环境可根据需要开启。
- INFO:信息性消息,用于记录程序正常的运行状态、重要的业务事件或系统启动信息等。这是默认的、推荐在生产环境使用的级别,用于监控系统健康状态。
- WARN:警告信息,表明可能有问题发生,但程序仍能继续运行。例如,磁盘空间不足、使用了过时的 API、非关键的第三方服务调用超时等。需要关注但非紧急。
- ERROR:错误信息,表明发生了错误事件,影响了某个操作的正常完成,但应用程序可能仍能继续运行。例如,数据库连接失败、文件读取异常、业务逻辑错误等。需要立即调查处理。
- FATAL (在某些框架中):致命错误,表明发生了非常严重的错误事件,可能导致应用程序崩溃或中止。SLF4J 规范本身未定义此级别,但一些底层实现(如 Logback)支持。
级别规则 :日志框架通常遵循"级别阈值"规则。例如,如果将日志级别设置为 INFO,那么只有级别等于或高于 INFO(即 INFO、WARN、ERROR)的日志才会被输出,而 TRACE 和 DEBUG 级别的日志将被忽略。级别越高收到的信息越少。
2.5 日志级别的使用
日志的级别是开发人员自己设置的,开发人员根据自己的理解来判断信息的重要程度
所以针对这些级别,Logger对象分别提供了对应的方法来输出日志
代码实现:
java
@RestController
@RequestMapping("/logger")
public class LoggerController {
private static final Logger logger = LoggerFactory.getLogger(LoggerController.class);
@RequestMapping("/logger1")
public String printLog() {
logger.trace("====trace====");
logger.debug("====debug====");
logger.info("====info====");
logger.warn("====warn====");
logger.error("====error====");
return "打印不同的级别";
}
}
运行结果:

我们发现,结果只打印了info,warn和error级别的日志
这与日志级别的配置有关,日志的输出级别默认是info级别 ,所以只会打印大于等于此级别的日志,也就是info、warn、error
2.6 日志配置
上面分享的都是日志地使用,日志框架支持我们更灵活的输出日志,包括内容,格式等
2.6.1 配置日志级别
日志级别配置只需要在配置文件中设置"logging.level"配置项即可
properties配置:
java
logging.level.root=debuge
yml配置:
java
logging:
level:
root: debug
重新运行代码,观察结果:

2.6.2 日志持久化
日志持久化有两种方式:
- 配置日志文件名
- 配置日志的存储目录
配置日志文件的路径和文件名:
java
//properties配置:
logging.file.name=logger/springboot.log
//yml配置:
logging:
file:
name: logger/springboot.log
运行展示:

配置日志文件的保存路径:
java
//properties配置
logging.file.path=D:/temp
//yml配置
logging:
file:
path: D:/temp
运行展示:

当logging.file.name和logging.file.path两种配置都存在时,以logging.file.name为准
2.6.3 配置日志文件分割
随着项目的运行,日志数量会不断增加,这时就需要对日志进行分割
|-------------------------------------------------|-------------------|--------------------------------------------------|
| 配置项 | 说明 | 默认值 |
| logging.logback.rollingpolicy.file-name-pattern | 日志分割后的文件名格式 | ${LOG_FILE}.%d{yyyy-MM-dd}.%i(日志名.日期.索引) |
| logging.logback.rollingpolicy.max-file-size | 日志文件超过这个大小就自动进行分割 | 10MB |
代码实现:
java
//properties配置:
logging.logback.rollingpolicy.file-name-pattern=${LOG_FILE}.%d{yyyy-MM-dd}.%i
logging.logback.rollingpolicy.max-file-size=2KB
//yml配置:
logging:
logback:
rollingpolicy:file-name-pattern: ${LOG_FILE}.%d{yyyy-MM-dd}.%i
max-file-size: 2KB
2.6.4 配置日志格式
日志格式决定了每条日志输出的样式和包含的信息。Spring Boot 默认使用 Logback 作为日志实现,我们可以通过配置文件自定义日志的输出格式。
常见的日志格式占位符:
%d{yyyy-MM-dd HH:mm:ss}:日期时间%-5level:日志级别(左对齐,宽度5)%logger{36}:日志记录器名称(最大长度36)%msg:日志消息%n:换行符%thread:线程名%M:方法名%L:行号
配置示例(application.properties):
properties
# 控制台日志格式
logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n
文件日志格式
logging.pattern.file=%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n
配置示例(application.yml):
yaml
logging:
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
file: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
格式说明:
%d{yyyy-MM-dd HH:mm:ss}:显示完整日期时间[%thread]:显示线程名,便于多线程调试%-5level:日志级别,左对齐占5个字符宽度%logger{36}:记录器名称,最多显示36个字符%msg:实际的日志消息内容%n:换行符,确保每条日志单独一行
通过合理配置日志格式,可以让日志信息更加清晰、规范,便于后续的日志分析和问题排查。
三、更简单的日志输出
每次使用都调用LoggerFactory.getLogger(xxx.class)很繁琐,所以lombok给我们提供了更为简单的方式:
- 添加lombok框架支持
- 使用@slf4j注解输出日志
3.1 添加lombok依赖
java
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
3.2 输出日志
代码编写:
java
@Slf4j
@RestController
public class Logger {
@RequestMapping("/1")
public void log(){
log.info("==输出日志内容==");
return ;
}
}
运行展示:

四、小结
好饿好饿好饿,感觉每次写博客都要花挺久的,有点点不想写了,想赶紧往后赶进度。因为今天突然发现很多算法题都没掌握,愁哇愁哇。现在才理解那句出来混早晚要还的,今天晚上在更一期牛客算法,其实已经写了很多了,就是一直没往博客写,哈哈哈。接下来一个星期我将更新最少五篇,请格外观众老爷给我点点赞,支持下我吧。