https://mp.weixin.qq.com/s?__biz=MzI1NDY0MTkzNQ==&mid=2247497654&idx=2&sn=56f90509f698b90dfa23c6d616c330d6&chksm=e9c0a5d6deb72cc0c7168f51a99560658ac7fe4ee9394fed2b41892ad742b7b226b7820e129d&scene=178&cur_album_id=2369296394722934787#rd
1. Java 日志概览
说到 Java 许多初学者可能对日志感到困惑,因为这里涉及的东西太多了:Apache Commons Logging
、Slf4j
、Log4j
、Log4j2
、Logback
、Java Util Logging
等等,这些框架各有什么作用?它们有什么区别?
1.1 总体概览
下图显示得很好。 Java 中的日志体系:
[外链图片存储失败,源站可能有防盗链机制,建议保存图片直接上传(img-QOk18Wkl-1654582740414)(img/640-20220607141705518.png)]
可以看到,Java 日志框架主要分为两类:和。
日志立面定义了一组日志的接口规范,它不提供具体的底层实现逻辑。Apache Commons Logging
和 Slf4j
属于这一类。
日志实现是日志的具体实现,包括日志级控制、日志打印格式、日志输出形式(输出到数据库、输出到文件、输出到控制台等)。Log4j
、Log4j2
、Logback
以及 Java Util Logging
属于这一类。
将日志立面与日志分离实际上是一种典型的立面模式。这样,具体业务可以在不同的日志框架之间自由切换,而无需更改任何代码。开发人员只需掌握日志立面 API 即可。
日志门面不能单独使用,必须与具体的日志实现框架相结合。
那么日志框架可以单独使用吗?
当然,技术上没有问题,但我们通常不这样做,因为它的可维护性很差,以后不容易扩展。例如 A 开发工具包使用 Log4j 打印日志,B 引用这个工具包,但是 B 喜欢使用 Logback 打印日志,一个业务使用两个甚至多个日志框架,开发人员还需要维护多个日志的配置文件。所以我们都用日志门面打印日志。
1.2 日志级别
使用日志级别的好处是,调整级别可以屏蔽许多与调试相关的日志输出。不同的日志定义不同的日志级别,但它们是相似的。
Java Util Logging
定义了 7 从严重到普通的日志级别是:
- SEVERE
- WARNING
- INFO
- CONFIG
- FINE
- FINER
- FINEST
因为默认水平是 INFO,因此 INFO 等级以下的日志不会打印出来。
Log4j
定义了 8 日志级别(去掉 OFF 和 ALL,可以说分为 6 一级),从严重到普通,依次为:
- OFF:最高等级的,用于关闭所有日志记录。
- FATAL:这个级别的重大错误可以直接停止程序。
- ERROR:如果不想输出太多的日志,可以打印错误和异常信息。
- WARN:警告提示。
- INFO:用于生产环境中输出程序运行的一些重要信息,不能滥用。
- DEBUG:在开发过程中打印一些操作信息。
- TRACE
- ALL 打开所有日志记录的最低级别。
Logback
从严重到普通,日志级别相对简单:
- ERROR
- WARN
- INFO
- DEBUG
- TRACE
1.3 综合对比
Java Util Logging
系统在 JVM
一旦应用程序开始运行,就成初始化。一旦应用程序开始运行,配置就无法修改。此外,该日志的配置也不太方便 JVM
如下:
-Djava.util.logging.config.file=<config-file-name>。
由于这些限制, Java Util Logging
不广泛使用。
Log4j
虽然配置繁琐,但一旦配置完成,使用起来非常方便。只需将相关配置文件放入 classpath
很多情况下,Log4j
我们可以在不同的项目中重复使用配置文件。
Log4j
可以和 Apache Commons Logging
搭配使用,Apache Commons Logging
自动搜索和使用 Log4j
,若未找到 Log4j
,再使用 Java Util Logging
。
比 Log4j
Apache Commons Logging
组合更受欢迎 Slf4j
Logback
组合。
Logback
是 Slf4j
它生实现框架也来自 Log4j
作者(Ceki Gülcü)但是相比之下 Log4j
,它具有更多的优点、特点和更强的性能。
1.4 最佳实践
- 如果不想添加任何依赖,请使用它
Java Util Logging
或者框架容器提供的日志接口。 - 若更在意性能,建议:
Slf4j
Logback
。 - 如果已经在项目中使用过,
Log4j
推荐组合为:Slf4j
Log4j2
。
2. Spring Boot 日志实现
Spring Boot 使用 Apache Commons Logging
作为内部日志框架的立面,它只是一个日志接口,需要在实际应用中指定相应的日志。
Spring Boot 实现默认日志是 Logback
。这个很好看:随便启动一个 Spring Boot 从控制台上找一行日志,如下:
[外链图片存储失败,源站可能有防盗链机制,建议保存图片直接上传(img-4ceBatpz-1654582740416)(img/640-20220607141705700.png)]
考虑到最后 prod 我们在项目中全局搜索可变字符:The following profiles are active
,结果如下:
[外链图片存储失败,源站可能有防盗链机制,建议保存图片直接上传(img-EVGbDw7k-1654582740416)(img/640-20220607141705624.png)]
在日志输出行 debug。再次启动项目,如下图所示:
[外链图片存储失败,源站可能有防盗链机制,建议保存图片直接上传(img-bbpd7VxH-1654582740417)(img/640-20220607141705608.png)]
这时,我们可以看到真正的日志实现了 Logback
。
其他的诸如 Java Util Logging
、Log4j
等框架,Spring Boot 也有很好的支持。
在 Spring Boot 只要在项目中添加以下项目, web 自动添加日志依赖:
<dependncy>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
2.1 Spring Boot 日志配置
Spring Boot 的日志系统会自动根据 classpath 下的内容选择合适的日志配置,在这个过程中首选 Logback。
如果开发者需要修改日志级别,只需要在 application.properties 文件中通过 logging.level 前缀+包名
的形式进行配置即可,例如下面这样:
logging.level.org.springframework.web=debug
logging.level.org.hibernate=error
如果你想将日志输出到文件,可以通过如下配置指定日志文件名:
logging.file.name=javaboy.log
logging.file.name 可以只指定日志文件名,也可以指定日志文件全路径,例如下面这样:
logging.file.name=/Users/sang/Documents/javaboy/javaboy.log
如果你只是想重新定义输出日志文件的路径,也可以使用 logging.file.path
属性,如下:
logging.file.path=/Users/sang/Documents/javaboy
如果想对输出到文件中的日志进行精细化管理,还有如下一些属性可以配置:
- logging.logback.rollingpolicy.file-name-pattern:日志归档的文件名,日志文件达到一定大小之后,自动进行压缩归档。
- logging.logback.rollingpolicy.clean-history-on-start:是否在应用启动时进行归档管理。
- logging.logback.rollingpolicy.max-file-size:日志文件大小上限,达到该上限后,会自动压缩。
- logging.logback.rollingpolicy.total-size-cap:日志文件被删除之前,可以容纳的最大大小。
- logging.logback.rollingpolicy.max-history:日志文件保存的天数。
日志文件归档这块,小伙伴们感兴趣可以自己试下,可以首先将 max-file-size 属性调小,这样方便看到效果:
logging.logback.rollingpolicy.max-file-size=1MB
然后添加如下接口:
@RestController
public class HelloController {
private static final Logger logger = getLogger(HelloController.class);
@GetMapping("/hello")
public void hello() {
for (int i = 0; i < 100000; i++) {
logger.info("hello javaboy");
}
}
}
访问该接口,可以看到最终生成的日志文件被自动压缩了:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BncN4kft-1654582740418)(img/640-20220607141705563.png)]
application.properties 中还可以配置日志分组。
日志分组能够把相关的 logger 放到一个组统一管理。
例如我们可以定义一个 tomcat 组:
logging.group.tomcat=org.apache.catalina,org.apache.coyote, org.apache.tomcat
然后统一管理 tomcat 组中的所有 logger:
logging.level.tomcat=TRACE
Spring Boot 中还预定义了两个日志分组 web 和 sql,如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dPRGHjJR-1654582740418)(img/640-20220607141705596.png)]
不过在 application.properties 中只能实现对日志一些非常简单的配置,如果想实现更加细粒度的日志配置,那就需要使用日志实现的原生配置,例如 Logback
的 classpath:logback.xml
,Log4j
的 classpath:log4j.xml
等。如果这些日志配置文件存在于 classpath 下,那么默认情况下,Spring Boot 就会自动加载这些配置文件。
2.2 Logback 配置
2.2.1 基本配置
默认的 Logback
配置文件名有两种:
logback.xml
:这种配置文件会直接被日志框架加载。logback-spring.xml
:这种配置文件不会被日志框架直接加载,而是由 Spring Boot 去解析日志配置,可以使用 Spring Boot 的高级 Profile 功能。
Spring Boot 中为 Logback
提供了四个默认的配置文件,位置在 org/springframework/boot/logging/logback/
,分别是:
- defaults.xml:提供了公共的日志配置,日志输出规则等。
- console-appender.xml:使用 CONSOLE_LOG_PATTERN 添加一个ConsoleAppender。
- file-appender.xml:添加一个 RollingFileAppender。
- base.xml:为了兼容旧版 Spring Boot 而提供的。
如果需要自定义 logback.xml
文件,可以在自定义时使用这些默认的配置文件,也可以不使用。一个典型的 logback.xml
文件如下(resources/logback.xml):
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<include resource="org/springframework/boot/logging/logback/console-appender.xml" />
<root level="INFO">
<appender-ref ref="CONSOLE" />
</root>
<logger name="org.springframework.web" level="DEBUG"/>
</configuration>
可以通过 include 引入 Spring Boot 已经提供的配置文件,也可以自定义。
2.2.2 输出到文件
如果想禁止控制台的日志输出,转而将日志内容输出到一个文件,我们可以自定义一个 logback-spring.xml
文件,并引入前面所说的 file-appender.xml
文件。
像下面这样(resources/logback-spring.xml
):
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include resource="org/springframework/boot/logging/logback/defaults.xml" />
<property name="LOG_FILE" value="${LOG_FILE:-${LOG_PATH:-${LOG_TEMP:-${java.io.tmpdir:-/tmp}}/}spring.log}"/>
<include resource="org/springframework/boot/logging/logback/file-appender.xml" />
<root level="INFO">
<appender-ref ref="FILE" />
</root>
</configuration>
2.3 Log4j 配置
如果 classpath 下存在 Log4j2
的依赖,Spring Boot 会自动进行配置。
默认情况下 classpath 下当然不存在 Log4j2
的依赖,如果想使用 Log4j2
,可以排除已有的 Logback
,然后再引入 Log4j2
,如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
Log4j2
的配置就比较容易了,在 reources 目录下新建 log4j2.xml 文件,内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<configuration status="warn">
<properties>
<Property name="app_name">logging</Property>
<Property name="log_path">logs/${app_name}</Property>
</properties>
<appenders>
<console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="[%d][%t][%p][%l] %m%n" />
</console>
<RollingFile name="RollingFileInfo" fileName="${log_path}/info.log" filePattern="${log_path}/$${date:yyyy-MM}/info-%d{yyyy-MM-dd}-%i.log.gz">
<Filters>
<ThresholdFilter level="INFO" />
<ThresholdFilter level="WARN" onMatch="DENY" onMismatch="NEUTRAL" />
</Filters>
<PatternLayout pattern="[%d][%t][%p][%c:%L] %m%n" />
<Policies>
<TimeBasedTriggeringPolicy interval="1" modulate="true" />
<SizeBasedTriggeringPolicy size="2 MB" />
</Policies>
<DefaultRolloverStrategy compressionLevel="0" max="10"/>
</RollingFile>
<RollingFile name="RollingFileWarn" fileName="${log_path}/warn.log" filePattern="${log_path}/$${date:yyyy-MM}/warn-%d{yyyy-MM-dd}-%i.log.gz">
<Filters>
<ThresholdFilter level="WARN" />
<ThresholdFilter level="ERROR" onMatch="DENY" onMismatch="NEUTRAL" />
</Filters>
<PatternLayout pattern="[%d][%t][%p][%c:%L] %m%n" />
<Policies>
<TimeBasedTriggeringPolicy interval="1" modulate="true" />
<SizeBasedTriggeringPolicy size="2 MB" />
</Policies>
<DefaultRolloverStrategy compressionLevel="0" max="10"/>
</RollingFile>
<RollingFile name="RollingFileError" fileName="${log_path}/error.log" filePattern="${log_path}/$${date:yyyy-MM}/error-%d{yyyy-MM-dd}-%i.log.gz">
<ThresholdFilter level="ERROR" />
<PatternLayout pattern="[%d][%t][%p][%c:%L] %m%n" />
<Policies>
<TimeBasedTriggeringPolicy interval="1" modulate="true" />
<SizeBasedTriggeringPolicy size="2 MB" />
</Policies>
<DefaultRolloverStrategy compressionLevel="0" max="10"/>
</RollingFile>
</appenders>
<loggers>
<root level="info">
<appender-ref ref="Console" />
<appender-ref ref="RollingFileInfo" />
<appender-ref ref="RollingFileWarn" />
<appender-ref ref="RollingFileError" />
</root>
</loggers>
</configuration>
首先在 properties 节点中指定了应用名称以及日志文件位置。
然后通过几个不同的 RollingFile 对不同级别的日志分别处理,不同级别的日志将输出到不同的文件,并按照各自的命名方式进行压缩。
这段配置比较程式化,小伙伴们可以保存下来做成 IntelliJ IDEA 模版以便日常使用。
3.小结
好啦,这就是松哥和小伙伴们分享的 Spring Boot 日志了,整体来说并不难,小伙伴们可以仔细品一品。