日志级别
几种常见的日志级别由低到高分为: TRACE < DEBUG < INFO < WARN < ERROR <FATAL 。
如何理解这个日志级别呢?很简单,如果项目中的日志级别设置为 INFO ,那么比它更低级别的日志信息就看不到了,即是 TRACE 、 DEBUG 日志将会不显示。
日志框架有哪些?
常见的日志框架有 log4j 、 logback 、 log4j2 。
log4j 这个日志框架显示是耳熟能详了,在 Spring 开发中是经常使用,但是据说log4j官方已经不再更新了,而且在性能上比logback 、 log4j2 差了很多。
logback 是由 log4j 创始人设计的另外一个开源日志框架,logback相比之于log4j性能提升了10%以上,初始化内存加载也更小了。作为的Spring Boot默认的日志框架肯定是有着不小的优势。
log4j2 晚于 logback 推出,官网介绍性能比 logback 高,但谁知道是不是王婆卖瓜自卖自夸,坊间流传,log4j2在很多思想理念上都是照抄logback,因此即便log4j2是Apache官方项目,Spring等许多框架项目没有将它纳入主流。
日志框架很多,究竟如何选择能够适应现在的项目开发,当然不是普通程序员考虑的,但是为了更高的追求,至少应该了解一下,哈哈。
Spring Boot 日志框架
Spring Boot默认的日志框架是 logback ,既然Spring Boot能够将其纳入的默认的日志系统,肯定是有一定的考量的,因此实际开发过程中还是不要更换。
原则上需要使用logback,需要添加以下依赖,但是既然是默认的日志框架,当然不用重新引入依赖了。
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
Spring Boot中默认的日志级别是 INFO ,启动项目日志打印如下:
从上图可以看出,输出的日志的默认元素如下:
1. 时间日期:精确到毫秒
2. 日志级别:ERROR, WARN, INFO, DEBUG , TRACE
3. 进程ID
4. 分隔符:— 标识实际日志的开始
5. 线程名:方括号括起来(可能会截断控制台输出)
6. Logger名:通常使用源代码的类名
7. 日志内容
代码中如何使用日志?
在业务中肯定需要追溯日志,那么如何在自己的业务中输出日志呢?其实常用的有两种方式,下面一一介绍。
第一种其实也是很早之前常用的一种方式,只需要在代码添加如下:
private final Logger logger=
LoggerFactory.getLogger(DemoApplicationTests.class);
这种方式显然比较鸡肋,如果每个类中都添加一下岂不是很low。别着急,lombok为我们解决了这个难题。第二种:要想使用lombok,需要添加如下依赖:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
使用也是很简单,只需要在类上标注一个注解 @Slf4j 即可,如下:
@Slf4j
class DemoApplicationTests {
@Test
public void test(){
log.debug("输出DEBUG日志.......");
}
}
如何定制日志级别?
Spring Boot中默认的日志级别是INFO,但是可以自己定制日志级别,如下:
logging.level.root=DEBUG
上面是将所有的日志的级别都改成了 DEBUG ,Spring Boot还支持 package 级别的日志级别调整,格式为: logging.level.xxx=xxx ,如下:
logging.level.com.example.demo=INFO
那么完整的配置如下:
logging.level.root=DEBUG
logging.level.com.example.demo=INFO
日志如何输出到文件中?
Spring Boot中日志默认是输出到控制台的,但是在生产环境中显示不可行的,因此需要配置日志输出到日志文件中。
其中有两个重要配置如下:
1. logging.file.path :指定日志文件的路径
2. logging.file.name :日志的文件名,默认为 spring.log
注意:官方文档说这两个属性不能同时配置,否则不生效,因此只需要配置一个即可。
指定输出的文件为当前项目路径的 logs 文件下,默认生成的日志文件为spring.log ,如下:
logging.file.path=./logs
日志文件中还有一些其他的属性,比如日志文件的最大size,保留几天的日志等等,下面会介绍到。
如何定制日志格式?
默认的日志格式在第一张图已经看到了,有时我们需要定制自己需要的日志输出格式,这样在排查日志的时候能够一目了然。
定制日志格式有两个配置,分别是控制台的输出格式和文件中的日志输出格式,如下:
1. logging.pattern.console :控制台的输出格式
2. logging.pattern.file :日志文件的输出格式
例如配置如下:
logging.pattern.console=%d{yyyy/MM/dd-HH:mm:ss} [%thread] %-5level %logger- %msg%n
logging.pattern.file=%d{yyyy/MM/dd-HH:mm} [%thread] %-5level %logger- %msg%n
上面的配置编码的含义如下:
%d{HH:mm:ss.SSS}——日志输出时间
%thread——输出日志的进程名字,这在Web应用以及异步任务处理中很有用
%-5level——日志级别,并且使用5个字符靠左对齐
%logger- ——日志输出者的名字
%msg——日志消息
%n——平台的换行符
如何自定义日志配置文件?
Spring Boot官方文档指出,根据不同的日志系统,可以按照如下的日志配置文件名就能够被正确加载,如下:
- Logback :logback-spring.xml, logback-spring.groovy, logback.xml, logback.groovy
- Log4j :log4j-spring.properties, log4j-spring.xml, log4j.properties, log4j.xml
- Log4j2 :log4j2-spring.xml, log4j2.xml
- JDK (Java Util Logging) :logging.properties
Spring Boot官方推荐优先使用带有-spring的文件名作为你的日志配置。因此只需要在 src/resources 文件夹下创建 logback-spring.xml 即可,配置文件内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<property name="logPath" value="spring-boot-03-loging/logs"/><!-- 定义日志存放目录 -->
<property name="PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t-
%L] %-5level %logger{36} %L %M - %msg%xEx%n"/><!-- 日志输出的格式-->
<contextName>logback</contextName>
<!--输出到控制台 ConsoleAppender-->
<appender name="consoleLog"
class="ch.qos.logback.core.ConsoleAppender">
<!--展示格式 layout-->
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>${PATTERN}</pattern>
</layout>
<!--过滤器,只有过滤到指定级别的日志信息才会输出,如果level为ERROR,那么控制台只会输出ERROR日志-->
<!--
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
-->
</appender>
<!--正常的日志文件,输出到文件中-->
<appender name="fileDEBUGLog"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<!--如果只是想要 Info 级别的日志,只是过滤 info 还是会输出 Error 日志,因为 Error 的级别高,
所以我们使用下面的策略,可以避免输出 Error 的日志-->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>Error</level><!--过滤 Error-->
<onMatch>DENY</onMatch><!--匹配到就禁止-->
<onMismatch>ACCEPT</onMismatch><!--没有匹配到就允许-->
</filter>
<!--日志名称,如果没有File 属性,那么只会使用FileNamePattern的文件
路径规则如果同时有<File>和<FileNamePattern>,那么当天日志是<File>,明天
会自动把今天的日志改名为今天的日期。即,<File> 的日志都是当天的。
-->
<File>${logPath}/log_demo.log</File>
<!--滚动策略,按照时间滚动 TimeBasedRollingPolicy-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--文件路径,定义了日志的切分方式——把每一天的日志归档到一个文件中,以防止日志填满整个磁盘空间-->
<FileNamePattern>${logPath}/log_demo_%d{yyyy-MM- dd}.log</FileNamePattern>
<!--只保留最近90天的日志-->
<maxHistory>90</maxHistory>
<!--用来指定日志文件的上限大小,那么到了这个值,就会删除旧的日志-->
<!--<totalSizeCap>1GB</totalSizeCap>-->
</rollingPolicy>
<!--日志输出编码格式化-->
<encoder>
<charset>UTF-8</charset>
<pattern>${PATTERN}</pattern>
</encoder>
</appender>
<!--指定最基础的日志输出级别-->
<root level="DEBUG">
<!--appender将会添加到这个loger-->
<appender-ref ref="consoleLog"/>
<appender-ref ref="fileDEBUGLog"/>
<appender-ref ref="fileErrorLog"/>
</root>
<!-- 定义指定package的日志级别-->
<logger name="org.springframework" level="DEBUG"></logger>
<logger name="org.mybatis" level="DEBUG"></logger>
<logger name="java.sql.Connection" level="DEBUG"></logger>
<logger name="java.sql.Statement" level="DEBUG"></logger>
<logger name="java.sql.PreparedStatement" level="DEBUG"></logger>
<logger name="io.lettuce.*" level="INFO"></logger>
<logger name="io.netty.*" level="ERROR"></logger>
<logger name="com.rabbitmq.*" level="DEBUG"></logger>
<logger name="org.springframework.amqp.*" level="DEBUG"></logger>
<logger name="org.springframework.scheduling.*" level="DEBUG"></logger>
<!--定义com.xxx..xx..xx包下的日志信息不上传,直接输出到fileDEBUGLog和fileErrorLog这个两个appender中,
日志级别为DEBUG-->
<logger name="com.xxx.xxx.xx" additivity="false" level="DEBUG">
<appender-ref ref="fileDEBUGLog"/>
<appender-ref ref="fileErrorLog"/>
</logger>
</configuration>
当然,如果就不想用SpringBoot推荐的名字,想自己定制也行,只需要在配置文件中指定配置文件名即可,如下:
logging.config=classpath:logging-config.xml
懵逼了,一堆配置什么意思?别着急,下面一一介绍。
日志配置文件节点属性介绍
configuration节点
这是一个根节点,其中的各个属性如下:
1. scan :当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。
2. scanPeriod :设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。
3. debug :当此属性设置为true时,将打印出logback内部日志信息,
实时查看logback运行状态。默认值为false。
root节点
这是一个必须节点,用来指定基础的日志级别,只有一个 level 属性,默认值是 DEBUG 该节点可以包含零个或者多个元素,子节点是 appender-ref ,标记这个 appender 将会添加到这个 logger 中。
contextName节点
标识一个上下文名称,默认为 default,一般用不到
property节点
标记一个上下文变量,属性有name和value,定义变量之后可以使用 ${} 来获取。
appender节点
用来格式化日志输出节点,有两个属性 name 和 class ,class用来指定哪种输出策略,常用就是控制台输出策略和文件输出策略。
这个节点很重要,通常的日志文件需要定义三个appender,分别是控制台输出,常规日志文件输出,异常日志文件输出。
该节点有几个重要的子节点,如下:
1. filter :日志输出拦截器,没有特殊定制一般使用系统自带的即可,但是如果要将日志分开,比如将ERROR级别的日志输出到一个文件中,将除了 ERROR 级别的日志输出到另外一个文件中,此时就要拦截 ERROR 级别的日志了。
2. encoder : 和pattern节点组合用于具体输出的日志格式和编码方式。
3. file : 节点用来指明日志文件的输出位置,可以是绝对路径也可以是相对路径
4. rollingPolicy : 日志回滚策略,在这里我们用了TimeBasedRollingPolicy,基于时间的回滚策略,有以下子节点fileNamePattern,必要节点,可以用来设置指定时间的日志归档。
5. maxHistory : 可选节点,控制保留的归档文件的最大数量,超出数量就删除旧文件,,例如设置为30的话,则30天之后,旧的日志就会被删除
6. totalSizeCap : 可选节点,用来指定日志文件的上限大小,例如设置为3GB的话,那么到了这个值,就会删除旧的日志
logger节点
可选节点,用来具体指明包的日志输出级别,它将会覆盖root的输出级别。 该节点有几个重要的属性如下:
1. name :指定的包名
2. level :可选,日志的级别
3. addtivity :可选,默认为true,将此logger的信息向上级传递,将有root节点定义日志打印。如果设置为false,将不会上传,此时需要定义一个 appender-ref 节点才会输出。
日志框架切换
什么是日志门面?
前面介绍的日志框架都是基于日志门面 SLF4j 即简单日志门面(Simple Logging Facade for Java),SLF4j 并不是一个真正的日志实现,而是一个抽象层,它允许你在后台使用任意一个日志实现。
使用了 slf4j 后,对于应用程序来说,无论底层的日志框架如何变,应用程序不需要修改任意一行代码,就可以直接上线了。如果对 SLF4j 比较感兴趣的可以去官网看看:SLF4j 官网
如何做到无感知切换?
SLF4j 是日志门面,无论什么日志框架都是基于 SLF4j 的 API 实现,因此无论是代码打印日志还是 Lombok 注解形式打印日志,都要使用的 SLF4j 的 API,而 API ,这样才能解耦,做到无感知。因为最终切换的框架只是对 SLF4j的实现,并不是切换SLF4j。其实这一条在阿里开发手册中也是明确指出了,如下:
Spring Boot默认是Logback日志框架,如果需要切换到其他的日志框架应该如何做?
首先我们先看官网的一张图,一切都在图中,如下:
SLF4j 只是一个门面,共有两大特性。一是静态绑定、二是桥接。
什么是静态绑定?:我们以 log4j 为例。首先我们的 application 中会使用 slf4j 的 api 进行日志记录。我们引入适配层的 jar 包 slf4j-log412.jar 及底层日志框架实现 log4j.jar 。简单的说适配层做的事情就是把 slf4j 的 api 转化成 log4j 的 api。通过这样的方式来屏蔽底层框架实现细节。
什么是桥接?:比如你的 application 中使用了 slf4j ,并绑定了 logback 。但是项目中引入了一个 A.jar , A.jar 使用的日志框架是 log4j 。那么有没有方法让 slf4j 来接管这个 A.jar 包中使用 log4j 输出的日志呢?这就用到了桥接包。你只需要引入 log4j-over-slf4j.jar 并删除 log4j.jar 就可以实现 slf4j 对 A.jar 中 log4j 的接管.听起来有些不可思议。你可能会想如果删除 log4j.jar 那 A.jar 不会报编译错误嘛?答案是不会。因为 log4j-over-slf4j.jar 实现了 log4j 几乎所有 public 的 API 。但关键方法都被改写了。不再是简单的输出日志,而是将日志输出指令委托给 slf4j 。
下面就以 log4j2 为例,切换 Spring Boot 的日志框架为 log4j2 ,替换默认日志框架 Logback 。
引入依赖
Spring Boot 默认是 Logback 日志框架,如果想要切换 log4j2 肯定是要将 Logback 的依赖移除,只需要排除 web 模块中的日志场景启动器即可,如下:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!-- 去掉springboot默认日志框架logback -->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
排除默认的 logback 依赖
肯定是需要引入 log4j2 的依赖,其实 log4j2 为了与 Spring Boot 适配也做了个启动器,不需要在引入其他的jar包了,只需要添加如下依赖即可:
<!-- 引入log4j2依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
指定配置文件
Spring Boot 官方文档已经给出了默认两个 log4j2 的配置的名称,分别为:log4j2-spring.xml , log4j2.xml ,但是建议使用 log4j2-spring.xml ,因为 Spring Boot 会做一些扩展,行吧,就整这个放在 src/resources 文件夹下即可。
另外如果不使用默认的配置名称,则需要在 application.properties 指定配置文件,如下:
logging.config=classpath:logging-config.xml
日志配置文件如何配置?
其实 log4j2 的一些配置和 logback 很相似,这里就不再一一介绍,有兴趣的可以去官网查查,直接贴出一些即用的配置,如下:
<?xml version="1.0" encoding="UTF-8"?>
<!--Configuration 后面的 status,这个用于设置 log4j2 自身内部的信息输出,可以不设置,当设置成 trace 时,你会看到 log4j2 内部各种详细输出-->
<!--monitorInterval:Log4j 能够自动检测修改配置 文件和重新配置本身,设置间隔秒数-->
<configuration monitorInterval="5">
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!--变量配置-->
<Properties>
<!-- 格式化输出:%date 表示日期,%thread 表示线程名,%-5level:级别从左显示5个字符宽度 %msg:日志消息,%n是换行符-->
<!-- %logger{36} 表示 Logger 名字最长36个字符 -->
<property name="LOG_PATTERN" value="%date{HH:mm:ss.SSS}[%thread] %-5level %logger{36} - %msg%n" />
<!-- 定义日志存储的路径 -->
<property name="FILE_PATH" value="更换为你的日志路径" />
<property name="FILE_NAME" value="更换为你的项目名" />
</Properties>
<appenders>
<console name="Console" target="SYSTEM_OUT">
<!--输出日志的格式-->
<PatternLayout pattern="${LOG_PATTERN}"/>
<!--控制台只输出level及其以上级别的信息(onMatch),其他的直接
拒绝(onMismatch)-->
<ThresholdFilter level="info" onMatch="ACCEPT"
onMismatch="DENY"/>
</console>
<!--文件会打印出所有信息,这个log每次运行程序会自动清空,由
append属性决定,适合临时测试用-->
<File name="Filelog" fileName="${FILE_PATH}/test.log" append="false">
<PatternLayout pattern="${LOG_PATTERN}"/>
</File>
<!-- 这个会打印出所有的 info 及以下级别的信息,每次大小超过 size,则这 size 大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档-->
<RollingFile name="RollingFileInfo" fileName="${FILE_PATH}/info.log"
filePattern="${FILE_PATH}/${FILE_NAME}-INFO-%d{yyyy-MM-
dd}_%i.log.gz">
<!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒
绝(onMismatch)-->
<ThresholdFilter level="info" onMatch="ACCEPT"
onMismatch="DENY"/>
<PatternLayout pattern="${LOG_PATTERN}"/>
<Policies>
<!--interval属性用来指定多久滚动一次,默认是1 hour-->
<TimeBasedTriggeringPolicy interval="1"/>
<SizeBasedTriggeringPolicy size="10MB"/>
</Policies>
<!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹
下7个文件开始覆盖-->
<DefaultRolloverStrategy max="15"/>
</RollingFile>
<!-- 这个会打印出所有的warn及以下级别的信息,每次大小超过size,则
这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为
存档-->
<RollingFile name="RollingFileWarn"
fileName="${FILE_PATH}/warn.log"
filePattern="${FILE_PATH}/${FILE_NAME}-WARN-%d{yyyy-MM-
dd}_%i.log.gz">
<!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒
绝(onMismatch)-->
<ThresholdFilter level="warn" onMatch="ACCEPT"
onMismatch="DENY"/>
<PatternLayout pattern="${LOG_PATTERN}"/>
<Policies>
<!--interval属性用来指定多久滚动一次,默认是1 hour-->
<TimeBasedTriggeringPolicy interval="1"/>
<SizeBasedTriggeringPolicy size="10MB"/>
</Policies>
<!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹
下7个文件开始覆盖-->
<DefaultRolloverStrategy max="15"/>
</RollingFile>
<!-- 这个会打印出所有的error及以下级别的信息,每次大小超过size,则
这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为
存档-->
<RollingFile name="RollingFileError"
fileName="${FILE_PATH}/error.log"
filePattern="${FILE_PATH}/${FILE_NAME}-ERROR-%d{yyyy-MM-
dd}_%i.log.gz">
<!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒
绝(onMismatch)-->
<ThresholdFilter level="error" onMatch="ACCEPT"
onMismatch="DENY"/>
<PatternLayout pattern="${LOG_PATTERN}"/>
<Policies>
<!--interval属性用来指定多久滚动一次,默认是1 hour-->
<TimeBasedTriggeringPolicy interval="1"/>
<SizeBasedTriggeringPolicy size="10MB"/>
</Policies>
<!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹
下7个文件开始覆盖-->
<DefaultRolloverStrategy max="15"/>
</RollingFile>
</appenders>
<!--Logger节点用来单独指定日志的形式,比如要为指定包下的class指定不
同的日志级别等。-->
<!--然后定义loggers,只有定义了logger并引入的appender,appender才会
生效-->
<loggers>
<!--过滤掉spring和mybatis的一些无用的DEBUG信息-->
<logger name="org.mybatis" level="info" additivity="false">
<AppenderRef ref="Console"/>
</logger>
<!--监控系统信息-->
<!--若是additivity设为false,则 子Logger 只会在自己的appender里输
出,而不会在 父Logger 的appender里输出。-->
<Logger name="org.springframework" level="info" additivity="false">
<AppenderRef ref="Console"/>
</Logger>
<root level="info">
<appender-ref ref="Console"/>
<appender-ref ref="Filelog"/>
<appender-ref ref="RollingFileInfo"/>
<appender-ref ref="RollingFileWarn"/>
<appender-ref ref="RollingFileError"/>
</root>
</loggers>
</configuration>
上面的配置中如果需要使用的话,需要改掉全局变量中的日志路径和项目名
称,如下部分:
<property name="FILE_PATH" value="更换为你的日志路径" />
<property name="FILE_NAME" value="更换为你的项目名" />