清晰梳理最全日志框架关系与日志配置-SpringBoot 2.7.2 实战基础 ...

打印 上一主题 下一主题

主题 946|帖子 946|积分 2840

优雅哥 SpringBoot 2.7.2 实战基础 - 07 - 日志配置

Java 中日志相关的 jar 包非常多,log4j、log4j2、commons-logging、logback、slf4j 等,本文首先梳理这些包之间关系,然后介绍在 spring boot 中日志的配置,最后介绍多环境的配置。
1 日志框架历史


1.1 log4j

很多年前,一个叫 Ceki Gülcü 的大佬在一个项目中开发跟踪 API,这套跟踪 API 逐步演变成 log4j, 大概1999年,log4j 成为 Apache 的一员。
1.2 JUL

JUL:java.util.logging.
Apache 觉得 log4j 很有价值,就推荐给 SUN 公司(Java 语言是由 SUN 公司的 James Gosling 发明的),希望 SUN 公司在 JDK 中加入 log4j,SUN 公司觉得加入日志 API 很有必要,但是又看不上 log4j,于是便自己搞了一套官方的,于 2002 年  JDK 1.4 中实现了 JUL。
此时,市面上就有两套日志 API:来自 Apache 的 log4j 和来自官方的 JUL。
1.3 JCL(Jakarta Commons Logging)和 Simple Log

JCL:Jakarta Commons Logging.
log4j 和 JUL 是两套不同的 API。一个出现较早、一个是官方的,两个用户群体都较大。如果在项目中想要切换日志框架就需要改动大量代码,同时这也不符合”面向接口编程“的设计原则。Apache 就推出了 JCL 项目,名字看着高大上,但这玩意儿就是在 SSH、SSM 时代到处都能看见,那就是搭建框架时经常会看到的 commons-logging 包,该项目是一套日志的抽象层(后来大神们针对这种日志的抽象层一个高端的名字——日志门面)。所谓”抽象层“,本质上就是一堆接口,有接口就需要有实现,没有实现那就是自娱自乐,没有鸟用。所以 Apache 针对 JCL 提供了一个默认实现,那就是 Simple Log。
JCL 基于动态绑定来实现日志的记录:开发过程中使用 JCL 定义的接口,程序运行的时候使用类路径 classpath 中的具体实现(Simple Log、log4j、JUL)。
可以和 JDBC 类比,Java 官方制定了数据库访问层持久化操作的标准 JDBC,各个数据库厂商(Oracle、MySQL等)实现这套标准。JCL 也是想成为规范制定者,统一日志操作的规范。
JCL 的出现,日志体系显得比较优雅,面向 JCL 的接口编程,可以很方便的切换日志框架。在这个时候, log4j他爹 Ceki`因为一些未知的原因离开了 Apache。
1.4 slf4j

slf4j:Simple Logging Facade for Java.
写代码的人大多有个共性:别人写的都是垃圾、自己昨天写的代码也是垃圾,只有自己现在写的才是最好的。Ceki  这哥们同样觉得 JCL 不是特别完美,于是自己又搞了一套新版本的日志接口(高端的名字是:日志门面):slf4j。
问题来了,slf4j 只有接口没有实现,难不成要让 log4j 和 JUL 都来实现 slf4j 吗?肯定是不可能的。
JCL 是采用动态绑定机制,而 slf4j 采用”桥接包“,也就是分别开发 log4j 和 JUL 的桥接包,通过桥接包来适配两者。大牛就是大牛,Ceki提供了这些桥接包 slf4j-log4j、slf4j-jdk14等。由于 JCL 出现比 slf4j 早,很多项目使用了 JCL,所以这大佬也提供了 slf4j-jcl。
还有一种常见:在某个项目中使用了一个第三方框架,这个项目使用了 slf4j 和 log4j,而依赖的第三方框架使用了 JCL 和 JUL,这时候系统就会有两种日志配置文件和两种打印方式,乱七八糟的。 Ceki Gülcü 也考虑到这种场景,没有他的桥接包搞不定的场景,于是就弄了个 jcl-over-slf4j 的桥接包...
1.5 logback

Ceki Gülcü 弄了 slf4j 和一堆桥接包,2006 年为 slf4j 提供了一个很厉害的实现:logback。与此同时他还特意写了一篇文章《Reasons to prefer logback over log4j》。毕竟 log4j 也出自于他的手,里面存在什么问题他最清楚。事实上,logback 的性能和设计确实比 log4j 更厉害,与时俱进嘛。
1.6 log4j2

logback 的出现让 Apache 坐不住了,2012年推出了新项目 log4j2。看名字像是 log4j的升级版,实际上是一个全新的玩意,它不兼容log4j。log4j2 几乎包括了 logback的特性。竞争是残酷的,与slf4j类似,log4j2也想统一日志的天下,也弄了一堆桥接包,通过桥接包 log4j-xxx 去兼容上面各种各样的日志框架...
乱七八糟扯了一堆,核心就三个概念:

  • 日志门面:JCL、slf4j
  • 日志产品:log4j、JUL、logback、log4j2
  • 桥接包:slf4j-xxx、log4j-xxx
2 Spring Boot 日志配置

Spring Boot 底层默认使用 slf4j 和 logback 的方式记录日志。咱们 demo 工程中依赖了 spring-boot-starter-web,它又依赖了 spring-boot-starter-logging,所以不需要再手动添加该依赖。
在 Spring Boot 中,application.yml 支持部分 logback 的日志配置,但一些高级配置只能通过独立的 xml 配置文件实现,经过 Spring Boot 的整合后,可支持多环境配置,但 logback 配置文件需要命名为 logback-spring.xml。如果使用了自定义日志配置文件,application.yml中 logging 有关配置就会失效。
2.1 springboot 默认的 logback 配置

SpringBoot 默认提供了一套 logback 的配置文件,位于 spring-boot依赖中的 org/springframework/boot/logging/logback/base.xml。

该文件引入了三个 xml 文件,并设置了root的日志级别为 info。console-appender.xml 和 file-appender.xml 中定义了日志的追加器,分别是名为 CONSOLE 的控制台追加器 和 名为 FILE 的文件追加器。
org/springframework/boot/logging/logback/defaults.xml 定义了 logback 的转换器、一些包的日志级别、日志显示格式。

默认在控制台中显示彩色日志,就是因为使用了转换器 ColorConverter,显示的格式为 CONSOLE_LOG_PATTERN 中使用了该转换器。
在我们的自定义配置中可以复用这个 default.xml 和 console-appender.xml。
2.2 自定义配置

在 src/main/resources/下创建配置文件 logback-spring.xml。
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <configuration>
  3.     <include resource="org/springframework/boot/logging/logback/defaults.xml" />
  4.     <include resource="org/springframework/boot/logging/logback/console-appender.xml" />
  5.     <root level="INFO">
  6. <root level="INFO">
  7.     <appender-ref ref="CONSOLE" />
  8.     <appender-ref ref="FILE" />
  9. </root><appender-ref ref="CONSOLE" />
  10.     </root>
  11. </configuration>
复制代码
上面的配置引入了 spring boot 中 logback 的默认配置和 CONSOLE 追加器,并定义了 root 的日志级别为 info。
2.3 日志级别

日志有五个级别:trace、debug、info、warn、error,级别依次较高,配置了某个级别,就会输出该级别及其以上的级别。如,配置日志级别为 warn,则日志会输出 warn、error;如果配置日志级别为 debug,则会输出 debug、info、warn、error。
在 DemoController 的 hello 方法中添加日志输出,测试日志级别:
  1. @RestController
  2. @RequestMapping("demo")
  3. public class DemoController {
  4.     private Logger logger = LoggerFactory.getLogger(DemoController.class);
  5.     @GetMapping("hello")
  6.     public String hello(String msg) {
  7. <root level="INFO">
  8.     <appender-ref ref="CONSOLE" />
  9.     <appender-ref ref="FILE" />
  10. </root>String result = "Hello Spring Boot ! " + msg;
  11. <root level="INFO">
  12.     <appender-ref ref="CONSOLE" />
  13.     <appender-ref ref="FILE" />
  14. </root>System.out.println(result);
  15. <root level="INFO">
  16.     <appender-ref ref="CONSOLE" />
  17.     <appender-ref ref="FILE" />
  18. </root>logger.error("error log");
  19. <root level="INFO">
  20.     <appender-ref ref="CONSOLE" />
  21.     <appender-ref ref="FILE" />
  22. </root>logger.warn("warn log");
  23. <root level="INFO">
  24.     <appender-ref ref="CONSOLE" />
  25.     <appender-ref ref="FILE" />
  26. </root>logger.info("info log");
  27. <root level="INFO">
  28.     <appender-ref ref="CONSOLE" />
  29.     <appender-ref ref="FILE" />
  30. </root>logger.debug("debug log");
  31. <root level="INFO">
  32.     <appender-ref ref="CONSOLE" />
  33.     <appender-ref ref="FILE" />
  34. </root>logger.trace("trace log");
  35. <root level="INFO">
  36.     <appender-ref ref="CONSOLE" />
  37.     <appender-ref ref="FILE" />
  38. </root>return result;
  39.     }
  40. }
复制代码
注意,引入的 Logger 和 LoggerFactory 两个类都是 slf4j 包下面的。上面的代码分别输出五个级别的日志。启动服务,访问 hello 接口,控制台输出:

控制台值输出 info、warn、error,可以看出 SpringBoot 默认输出级别为 info。可通过配置细粒度调整日志的级别:
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <configuration>
  3.     ...
  4.     <logger name="com.yygnb.demo" level="trace"/>
  5.           ...
  6. </configuration>
复制代码
上面按照包名更改了日志的显示级别,com.yygnb.demo 包下面的日志都是 trace 级别。重新访问 hello 接口,error、warn、info、debug、trace 都会全部打印出来。
如果使用了 lombok,可以不用手动创建 logger 对象,使用注解 @Slf4j 后,就能直接在代码中使用 log 对象:
  1. @Slf4j
  2. @RestController
  3. @RequestMapping("demo")
  4. public class DemoController {
  5.     @GetMapping("hello")
  6.     public String hello(String msg) {
  7. <root level="INFO">
  8.     <appender-ref ref="CONSOLE" />
  9.     <appender-ref ref="FILE" />
  10. </root>String result = "Hello Spring Boot ! " + msg;
  11. <root level="INFO">
  12.     <appender-ref ref="CONSOLE" />
  13.     <appender-ref ref="FILE" />
  14. </root>System.out.println(result);
  15. <root level="INFO">
  16.     <appender-ref ref="CONSOLE" />
  17.     <appender-ref ref="FILE" />
  18. </root>log.error("error log");
  19. <root level="INFO">
  20.     <appender-ref ref="CONSOLE" />
  21.     <appender-ref ref="FILE" />
  22. </root>log.warn("warn log");
  23. <root level="INFO">
  24.     <appender-ref ref="CONSOLE" />
  25.     <appender-ref ref="FILE" />
  26. </root>log.info("info log");
  27. <root level="INFO">
  28.     <appender-ref ref="CONSOLE" />
  29.     <appender-ref ref="FILE" />
  30. </root>log.debug("debug log");
  31. <root level="INFO">
  32.     <appender-ref ref="CONSOLE" />
  33.     <appender-ref ref="FILE" />
  34. </root>log.trace("trace log");
  35. <root level="INFO">
  36.     <appender-ref ref="CONSOLE" />
  37.     <appender-ref ref="FILE" />
  38. </root>return result;
  39.     }
  40. }
复制代码
关于是否应该使用 lombok,网上各种义正言辞、牵强附会的说辞都有,甚至有些标题党写着《我们公司的技术总监规定xxxx》,我只能说遵守公司或项目规定就行。
2.4 文件追加器

上面复用了 SpringBoot 自带的控制台追加器 CONSOLE,这里自定义文件追加器:
  1. ...
  2. <appender name="FILE" >
  3.     <filter >
  4. <root level="INFO">
  5.     <appender-ref ref="CONSOLE" />
  6.     <appender-ref ref="FILE" />
  7. </root><level>INFO</level>
  8.     </filter>
  9.     <encoder>
  10. <root level="INFO">
  11.     <appender-ref ref="CONSOLE" />
  12.     <appender-ref ref="FILE" />
  13. </root><pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{50} - %msg%n</pattern>
  14.     </encoder>
  15.     <rollingPolicy >
  16. <root level="INFO">
  17.     <appender-ref ref="CONSOLE" />
  18.     <appender-ref ref="FILE" />
  19. </root><fileNamePattern>logs/hero-springboot-demo.%d.log</fileNamePattern>
  20. <root level="INFO">
  21.     <appender-ref ref="CONSOLE" />
  22.     <appender-ref ref="FILE" />
  23. </root><MaxHistory>100</MaxHistory>
  24.     </rollingPolicy>
  25. </appender>
  26. ...
复制代码
并在 root 中添加这个自定义 FILE 追加器:
  1. <root level="INFO">
  2.     <appender-ref ref="CONSOLE" />
  3.     <appender-ref ref="FILE" />
  4. </root>
复制代码
上面的 FILE 追加器,日志路径为项目根路径下的 logs 目录,日志名称形如 hero-springboot-demo.2022-08-02.log。启动服务,访问 hello 接口,测试日志文件是否生成。
2.5 多环境日志

假设希望在 local 时,只输出控制台日志;在其他环境(dev、test等)输出控制台日志和文件日志。SpringBoot 提供了 springProfile 标签,通过该元素 name 属性指定环境。修改 root 元素:
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <configuration>
  3.     <include resource="org/springframework/boot/logging/logback/defaults.xml" />
  4.     <include resource="org/springframework/boot/logging/logback/console-appender.xml" />
  5.     <root level="INFO">
  6. <root level="INFO">
  7.     <appender-ref ref="CONSOLE" />
  8.     <appender-ref ref="FILE" />
  9. </root><appender-ref ref="CONSOLE" />
  10.     </root>
  11. </configuration><root level="INFO">
  12.     <appender-ref ref="CONSOLE" />
  13.     <appender-ref ref="FILE" />
  14. </root><root level="INFO">
  15.     <appender-ref ref="CONSOLE" />
  16.     <appender-ref ref="FILE" />
  17. </root>
复制代码
分别使用 local 和 dev 启动服务,测试多环境是否生效。
我们自定义的 logback-spring.xml 充分利用了 Spring Boot 官方提供的配置,最终完整配置如下:
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <configuration>
  3.     <include resource="org/springframework/boot/logging/logback/defaults.xml" />
  4.     <include resource="org/springframework/boot/logging/logback/console-appender.xml" />
  5.     <root level="INFO">
  6. <root level="INFO">
  7.     <appender-ref ref="CONSOLE" />
  8.     <appender-ref ref="FILE" />
  9. </root><appender-ref ref="CONSOLE" />
  10.     </root>
  11. </configuration><root level="INFO">
  12.     <appender-ref ref="CONSOLE" />
  13.     <appender-ref ref="FILE" />
  14. </root>    INFO<?xml version="1.0" encoding="UTF-8"?>
  15. <configuration>
  16.     <include resource="org/springframework/boot/logging/logback/defaults.xml" />
  17.     <include resource="org/springframework/boot/logging/logback/console-appender.xml" />
  18.     <root level="INFO">
  19. <root level="INFO">
  20.     <appender-ref ref="CONSOLE" />
  21.     <appender-ref ref="FILE" />
  22. </root><appender-ref ref="CONSOLE" />
  23.     </root>
  24. </configuration>    %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{50} - %msg%n<?xml version="1.0" encoding="UTF-8"?>
  25. <configuration>
  26.     <include resource="org/springframework/boot/logging/logback/defaults.xml" />
  27.     <include resource="org/springframework/boot/logging/logback/console-appender.xml" />
  28.     <root level="INFO">
  29. <root level="INFO">
  30.     <appender-ref ref="CONSOLE" />
  31.     <appender-ref ref="FILE" />
  32. </root><appender-ref ref="CONSOLE" />
  33.     </root>
  34. </configuration>    logs/hero-springboot-demo.%d.log<root level="INFO">
  35.     <appender-ref ref="CONSOLE" />
  36.     <appender-ref ref="FILE" />
  37. </root>    100<?xml version="1.0" encoding="UTF-8"?>
  38. <configuration>
  39.     <include resource="org/springframework/boot/logging/logback/defaults.xml" />
  40.     <include resource="org/springframework/boot/logging/logback/console-appender.xml" />
  41.     <root level="INFO">
  42. <root level="INFO">
  43.     <appender-ref ref="CONSOLE" />
  44.     <appender-ref ref="FILE" />
  45. </root><appender-ref ref="CONSOLE" />
  46.     </root>
  47. </configuration><?xml version="1.0" encoding="UTF-8"?>
  48. <configuration>
  49.     <include resource="org/springframework/boot/logging/logback/defaults.xml" />
  50.     <include resource="org/springframework/boot/logging/logback/console-appender.xml" />
  51.     <root level="INFO">
  52. <root level="INFO">
  53.     <appender-ref ref="CONSOLE" />
  54.     <appender-ref ref="FILE" />
  55. </root><appender-ref ref="CONSOLE" />
  56.     </root>
  57. </configuration><?xml version="1.0" encoding="UTF-8"?>
  58. <configuration>
  59.     <include resource="org/springframework/boot/logging/logback/defaults.xml" />
  60.     <include resource="org/springframework/boot/logging/logback/console-appender.xml" />
  61.     <root level="INFO">
  62. <root level="INFO">
  63.     <appender-ref ref="CONSOLE" />
  64.     <appender-ref ref="FILE" />
  65. </root><appender-ref ref="CONSOLE" />
  66.     </root>
  67. </configuration><root level="INFO">
  68.     <appender-ref ref="CONSOLE" />
  69.     <appender-ref ref="FILE" />
  70. </root><root level="INFO">
  71.     <appender-ref ref="CONSOLE" />
  72.     <appender-ref ref="FILE" />
  73. </root>
复制代码

/ 程序员优雅哥(youyacoder),今日学习到此结束~~~

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

祗疼妳一个

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表