1. 日志框架
Java常见的日志框架:
- SLF4J:日志门面,不直接实现日志,推荐业务代码使用。
- Logback:Spring Boot 默认日志实现,配置简单。
- Log4j2:高性能,支持异步日志、结构化日志。
- JUL / JCL:较老,通常不直接用于新项目。

SLF4J不同于其他日志框架, 它不是⼀个真正的日志实现, 而是⼀个抽象层, 对日志框架制定的⼀种规范, 标准, 接口. 所有SLF4J并不能独立使用, 需要和具体的日志框架配合使用。
SLF4J 就像通用遥控器,而 log4j2 、 Logback 和 JUL 就像不同品牌的电视机,通过 通用的遥控器 可以操作不同品牌的电视。
门面模式(外观模式)
SLF4J是门面模式的典型应用(但不仅仅使用了门面模式).
门面模式(Facade Pattern)又称为外观模式, 提供了⼀个统⼀的接⼝, 用来访问子系统中的⼀群接⼝。 其主要特征是定义了⼀个高层接口, 让子系统更容易使用。

门面模式主要包含2种角色:
外观角色(Facade):也称门面角色,系统对外的统一接口。
子系统角色 (SubSystem):可以同时有一个或多个SubSystem.每个SubSytem都不是一个单独的类,
而是一个类的集合.SubSystem并不知道Facade的存在,对于SubSystem而言,Facade只是另一个客户端而已(即 Facade 对 SubSystem透明)
门面模式的实现
场景: 回家, 我们会开各个屋的灯. 离开家时, 会关闭各个屋的灯
如果家里设置⼀个总开关来控制整个屋的灯就会很方便
我们使用门面模式的实现:






门面模式的优点:
- 减少了系统的相互依赖,实现了客户端与子系统的耦合关系,这使得子系统的变化不会影响到调用它的客户端;
- 提高了灵活性,简化了客户端对子系统的使用难度,客户端无需关心子系统的具体实现方式,而只需要和门面对象交互即可.
- 提高了安全性,可以灵活设定访问权限,不在门面对象中开通方法,就无法访问
SLF4J 框架介绍
SLF4J就是其他日志框架的门面.SLF4J可以理解为是提供日志服务的统一API接口,并不涉及到具体的日志逻辑实现.
不引入日志门面
常见的日志框架有log4J,logback等.如果一个项目已经使用了log4j,而你依赖的另一个类库,假如是Apache Active MQ,它依赖于另外一个日志框架logback,那么你就需要把logback也加载进去.

存在问题:
- 1.不同日志框架的API接口和配置文件不同,如果多个日志框架共存,那么不得不维护多套配置文件(这个配置文件是指用户自定义的配置文件).
- 2.如果要更换日志框架,应用程序将不得不修改代码,并且修改过程中可能会存在一些代码冲突。
- 3.如果引入的第三方框架,使用了多套,那就不得不维护多套配置。
引入日志门面
引入门面日志框架之后,应用程序和日志框架(框架的具体实现)之间有了统一的API接口(门面日志框架实现),此时应用程序只需要维护一套日志文件配置,且当底层实现框架改变时,也不需要更改应用程序代码.

SLF4J就是这个日志门面。
总的来说,SLF4J使你的代码独立于任意一个特定的日志API,这是一个对于开发API的开发者很好的思想。
2. 日志的使用
Spring Boot 项目在启动的时候默认就有日志输出,如下图所示:

它打印的日志和 System.out.print 有什么不同呢:

访问 http://127.0.0.1:8080/logger/print,观察日志输出:

可以看到, 我们通过 System.out.print 打印的日志, 比SpringBoot 打印的日志缺少了很多信息。
① 日志格式说明
SpringBoot 日志输出内容元素具体如下:

从上图可以看到,日志输出内容元素具体如下:
- 时间日期:精确到毫秒
- 日志级别:ERROR, WARN, INFO, DEBUG 或 TRACE
- 进程ID
- 线程名
- Logger名(通常使用源代码的类名)
- 日志内容
其中,应用名称是根据 配置文件 中是否有配置而定的:

如果未配置,则不显示:

② 打印日志
SpringBoot 内置了日志框架 Slf4j , 我们可以直接在程序中调用 Slf4j 来输出日志。
打印日志的步骤:
- 在程序中得到日志对象.
- 使用日志对象输出要打印的内容
使用 org.slf4j 包下的 Logger 类,在程序中获取日志对象需要使用日志工厂LoggerFactory 的 getLogger 方法,如下代码所示:


使用上述的日志对象打印日志:

打印日志效果展示:

③ 日志的级别
日志的级别从高到低依次为: FATAL、ERROR、WARN、INFO、DEBUG、TRACE
- FATAL: 致命信息,表示需要立即被处理的系统级错误
- ERROR: 错误信息, 级别较高的错误日志信息, 但仍然不影响系统的继续运行.
- WARN: 警告信息, 不影响使用, 但需要注意的问题
- INFO: 普通信息, 用于记录应⽤程序正常运行时的⼀些信息, 例如系统启动完成、请求处理完成等.
- DEBUG: 调试信息, 需要调试时候的关键信息打印.
- TRACE: 追踪信息, 比DEBUG更细粒度的信息事件(除非有特殊用意,否则请使用DEBUG级别替代)
级别越高,收到的消息越少。
日志级别的使用
日志级别是开发人员自己设置的,开发人员根据自己的理解来判断该信息的重要程度
针对这些级别, Logger 对象分别提供了对应的方法, 来输出日志:

访问http://127.0.0.1:8080/logger/printLog,观察日志信息:

结果发现, 只打印了info, warn和error级别的日志
这与日志级别的配置有关,日志的输出级别默认是 info级别 , 所以只会打印大于等于此级别的日志, 也就是info, warn和error。
④ 日志配置
上述是日志的使用, 日志框架支持我们更灵活的输出日志, 包括内容, 格式等
配置日志级别
日志级别配置只需要在配置文件中设置 "logging.level" 配置项即可,如下所示:

重新运行上述代码:

⑤ 日志持久化
以上的日志都是输出在控制台上的, 下一次运行时,之前的日志都会消失,然而在线上环境中, 我们需要把日志保存下来, 以便出现问题之后追溯问题. 把日志保存下来就叫持久化。
日志持久化有两种方式:
- 配置日志文件名
- 配置日志的存储目录

logging.file.name 控制完整的文件名(可包含路径),即指定日志文件的完整名称或完整路径
- 用法:
- 只写文件名(如 myapp.log):日志会生成在当前项目的当前工作目录下,文件名为 myapp.log。
- 写绝对路径(如 /var/log/myapp.log 或 D:/logs/myapp.log):日志会精确生成在指定的位置,并使用指定的文件名。
- 特点:灵活性最高,你可以完全自定义日志叫什么名字,放在哪里。
而 logging.file.path 只控制存储目录,文件名由框架默认生成(spring.log)。
- 默认行为:如果你配置了 logging.file.path=/var/log,但没有配置 logging.file.name,Spring Boot 会在这个目录下自动创建一个默认文件名的日志文件,默认名字通常是 spring.log。
- 特点:你只关心日志放在哪个文件夹,不关心它叫什么名字。
配置日志文件的路径和文件名:

重新运行,右击项目点击刷新:

运行结果显示, 日志内容保存在了对应的目录下:

该日志中也保存了刚才运行后的结果日志:

再次运行,除了保存之前的日志,还新添加了刚才运行的:

这样就实现了日志的持久化。
配置日志文件的保存路径:
此时我们设置和 file.name 一样的路径:

看看运行后 log.log 是不是日志文件:

如上图所示,只是一个单纯的路径而已,file.path 文件名是默认的 spring.log。
注意 : logging.file.name 和 logging.file.path 两个都配置的情况下, 只生效其⼀,以 logging.file.name 为准。
例如,此时配置了两个日志文件:

运行刷新后,确实是以 file.name 为准:

⑥ 配置日志文件分割
如果我们的日志都放在⼀个文件中, 随着项目的运行, 日志文件会越来越大,需要对日志文件进行分割。
|-------------------------------------------------|-----------------|----------------------------------|
| 配置项 | 说明 | 默认值 |
| logging.logback.rollingpolicy.file-name-pattern | 日志分割后的文件格式 | ${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz |
| logging.logback.rollingpolicy.max-file-size | 日志文件超过这个大小就自动分割 | 10MB |
配置日志文件分割:

日志分割的结果:

可以看出这些日志文件是压缩文件。
设置一下文件格式,注意到 默认值是 ${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz,我们可以把 .gz 的后缀去掉,这样就是一个可以随时打开的普通文件了。
再次运行,观察日志文件分割:

此时都是一些普通文件,一个文件大小是 1KB,也就是 1KB 就分割。
分割后的日志文件名为:日志名( ${LOG_FILE}).日期( %d{yyyy-MM-dd}).索引( %i.)
⑦ 配置日志格式
目前日志打印的格式是默认的

打印日志格式, 也是支持配置的,支持控制台和日志文件分别设置
|-------------------------|-----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 配置项 | 说明 | 默认值 |
| logging.pattern.console | 控制台日志格式 | %clr(%d{{LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd'T'HH:mm:ss.SSSXXX}}){faint} %clr({LOG_LEVEL_PATTERN:-%5p}) %clr({PID:- }){magenta} %clr(---){faint} %clr(\[%15.15t\]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n{LOG_EXCEPTION_CONVERSION_WORD:-%wEx} |
| logging.pattern.file | 日志文件的日志格式 | %d{{LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd'T'HH:mm:ss.SSSXXX}} {LOG_LEVEL_PATTERN:-%5p} {PID:- } --- \[%t\] %-40.40logger{39} : %m%n{LOG_EXCEPTION_CONVERSION_WORD:-%wEx} |
设置控制台日志格式:

观察日志变化:

3. 简单日志输出
每次都使用 LoggerFactory.getLogger(xxx.class) 很繁琐, 且每个类都添加⼀遍, lombok给我们提供了一种更简单的方式。
- 添加 lombok 框架支持
- 使用 @slf4j 注解输出日志
添加Lombok依赖:

输出日志:
lombok提供的 @Slf4j 会帮我们提供⼀个日志对象 log, 我们直接使用就可以.
也就是说
@Slf4j 等于
private static Logger logger = LoggerFactory.getLogger(LoggerController.class);
