项目中日志系统是必不可少的,目前比较流行的日志框架有 log4j
、logback
等,这两个框架的作者是同一个
人,Logback
旨在作为流行的 log4j
项目的后续版本,从而恢复 log4j
离开的位置。
另外 slf4j(Simple Logging Facade for Java)
则是一个日志门面框架,提供了日志系统中常用的接口,
logback
和 log4j
则对 slf4j
进行了实现。
官网:https://logging.apache.org/log4j/1.x/
SpringBoot 默认就是使用 SLF4J
作为日志门面,logback
作为日志实现来记录日志。
Spring Boot在所有内部日志中使用Commons Logging
,但也保留默认配置对常用日志的支持,如:
Java Util Logging, Log4J, Log4J2, SLF4J, Logback
每种 Logger 都可以通过配置使用控制台或者文件输出日志内容。
默认情况下,如果您使用 Starters
,会使用 Logback
来实现日志管理。
我们没必要纠结使用默认的 Logback
还是 Log4j
,直接用 Slf4j
即可。至于这个日志具体是如何写到控制台或
者文件的,则由Spring Boot项目中引入了什么具体的日志框架决定,默认情况下就是 Logback
。
我们本文将讲述如何在spring boot 中应用 logback+slf4j
实现日志的记录。
<dependency>
<artifactId>spring-boot-starter-loggingartifactId>
<groupId>org.springframework.bootgroupId>
dependency>
org.springframework.boot
的依赖:
<dependencies>
<dependency>
<groupId>ch.qos.logbackgroupId>
<artifactId>logback-classicartifactId>
<version>1.2.6version>
<scope>compilescope>
dependency>
<dependency>
<groupId>org.apache.logging.log4jgroupId>
<artifactId>log4j-to-slf4jartifactId>
<version>2.14.1version>
<scope>compilescope>
dependency>
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>jul-to-slf4jartifactId>
<version>1.7.32version>
<scope>compilescope>
dependency>
dependencies>
boot-starter-web 启动器当中,会依赖中我们所使用环境的一些 jar包的信息,里面就包含了slf4j
日志门面和
logback
的日志实现。
1、SpringBoot底层默认使用 logback 作为日志实现
2、使用了 SLF4J 作为日志门面
3、将JUL也转换成 slf4j
4、在使用Springboot框架之后,其内部所有的日志实现都通过桥接器转换成slf4j日志门面进行统一的管理,最终
交给logback日志实现框架进行日志输出。
Logback 是log4j 框架的作者开发的新一代日志框架,它效率更高、能够适应诸多的运行环境,同时天然支持
SLF4J。
Logback的定制性更加灵活,同时也是spring boot的内置日志框架。
maven依赖中添加了spring-boot-starter-logging
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-loggingartifactId>
dependency>
但是呢,实际开发中我们不需要直接添加该依赖,你会发现spring-boot-starter
其中包含了spring-boot-
starter-logging
,该依赖内容就是 Spring Boot 默认的日志框架Logback+SLF4J
。而spring-boot-starter-
web
包含了spring-boot-starte
,所以我们只需要引入web组件即可:
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
完整 pom.xml 文件:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.5.6version>
<relativePath/>
parent>
<groupId>com.loggroupId>
<artifactId>spring-boot-logartifactId>
<version>0.0.1-SNAPSHOTversion>
<name>spring-boot-logname>
<description>spring-boot-logdescription>
<properties>
<java.version>1.8java.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
project>
package com.log.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author zhangshixing
* @date 2021年11月04日 17:42
*/
// 也可以使用@Slf4j注解输出日志(建议)
@RestController
public class DemoController {
/**
* 声明日志记录器对象(slf4j包)
*/
public static final Logger logger = LoggerFactory.getLogger(DemoController.class);
@GetMapping("get")
public void test() {
logger.error("error");
logger.warn("warn");
logger.info("info");
logger.debug("debug");
logger.trace("trace");
}
}
package com.log;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class SpringBootLogApplicationTests {
// 声明日志记录器对象(slf4j包)
public static final Logger logger = LoggerFactory.getLogger(SpringBootLogApplicationTests.class);
@Test
void contextLoads() {
logger.error("error");
logger.warn("warn");
logger.info("info");
logger.debug("debug");
logger.trace("trace");
}
}
输出:
2023-11-18 10:17:19.458 ERROR 1596 --- [ main] com.log.SpringBootLogApplicationTests : error
2023-11-18 10:17:19.458 WARN 1596 --- [ main] com.log.SpringBootLogApplicationTests : warn
2023-11-18 10:17:19.458 INFO 1596 --- [ main] com.log.SpringBootLogApplicationTests : info
因为我们没有提供任何配置文件,因此默认的日志级别就是info,所以 debug 和 trace 不会输出。
Springboot 支持对日志进行具体的配置,可以直接在 application.properties
配置文件中简单定义,也可以
导入具体日志实现的配置文件。
默认情况下Spring Boot将日志输出到控制台,不会写到日志文件。
如果要编写除控制台输出之外的日志文件,则需在
application.properties
中设置logging.file.name
或logging.file.path
属性。
注:二者不能同时使用,如若同时使用,则只有 logging.file.name 生效
logging.file.name
=文件名
logging.file.path
=日志文件路径
logging.level.包名
=指定包下的日志级别
logging.pattern.console
=日志打印规则
logging.file.name
设置文件,可以是绝对路径,也可以是相对路径。如:logging.file.name=my.log
logging.file.path
设置目录,会在该目录下创建spring.log
文件,并写入日志内容,如:
logging.file.path=/var/log
注:二者不能同时使用,如若同时使用,则只有
logging.file.name
生效,可以看到这种方式配置简单,但是能实现的功能也非常有限,如果想要更复杂的需求,就需要下面的定制化配置了。
我们设置:
logging.file.path=D:
会生成:
我们设置:
logging.file.path=D:
logging.file.name=D:\\my_log.txt
# 自定义logger对象的日志级别,com.log.controller是自定义logger对象的名称,也就是包名
logging.level.com.log.controller = trace
# 指定控制台输出消息格式
# 格式配置请参考:https://logging.apache.org/log4j/2.x/manual/layouts.html
logging.pattern.console = [%-5level] %d{yyyy-MM-dd HH:mm:ss} %c [%thread] %m%n
发送请求:http://127.0.0.1:8080/get
输出:
[ERROR] 2023-11-18 21:24:04 com.log.controller.DemoController [http-nio-8080-exec-1] error
[WARN ] 2023-11-18 21:24:04 com.log.controller.DemoController [http-nio-8080-exec-1] warn
[INFO ] 2023-11-18 21:24:04 com.log.controller.DemoController [http-nio-8080-exec-1] info
[DEBUG] 2023-11-18 21:24:04 com.log.controller.DemoController [http-nio-8080-exec-1] debug
[TRACE] 2023-11-18 21:24:04 com.log.controller.DemoController [http-nio-8080-exec-1] trace
# 指定日志文件消息格式
logging.pattern.file=[%-5level] %d{yyyy-MM-dd HH:mm:ss} %c [%thread] %m%n
输出:
SpringBoot提供的默认配置文件无法进行复杂的配置,比如所有日志都放到一个文件当中,不易于后期维护和管
理,希望是按照一定的规则进行滚动拆分,比如文件大小,时间等。通过简单的SpringBoot提供的配置文件还不
够,通常在企业开发中会导入具体某个日志实现相应的配置文件。
给类路径下放上每个日志框架自己的配置文件,SpringBoot就不使用默认配置的了:
日志框架 | 配置文件 |
---|---|
Logback | logback-spring.xml、logback.xml |
Log4j2 | log4j2-spring.xml、log4j2.xml |
JUL | logging.properties |
Spring Boot 官方推荐优先使用带有 -spring
的文件名作为你的日志配置(如使用logback-spring.xml
,而不
是 logback.xml
),命名为logback-spring.xml
的日志配置文件,将xml放至src/main/resource
下面。
也可以使用自定义的名称,比如logback-config.xml
,只需要在application.properties
文件中使用
logging.config=classpath:logback-config.xml
指定即可。
对于 logback 配置文件,logback-spring.xml
和 logback.xml
都能够被加载识别,增加了 -spring
的配置
文件会被 SpringBoot 框架解析,而不是由 logback 解析。
被 SpringBoot 框架解析的文件,只需修改 SpringBoot 的全局参数,就能对配置文件进行灵活调整,而不需要再
修改 logback 提供的核心配置文件了。
在讲解logback-spring.xml
之前我们先来了解三个单词:Logger
(记录器),Appenders
(附加器) 和
Layouts
(布局)。
Logback 的架构主要由三个核心组件组成:Logger、Appender 和 Layout。其中,Logger 用于记录 Log,
Appender 用于将 Log 输出到控制台或文件,Layout 用于定义 Log 输出格式。
这三种类型的组件协同工作,使开发人员能够根据消息类型和级别记录消息,并在运行时控制这些消息的格式以及
报告的位置。下面简要介绍一下这三个概念。
Logger 相当于一个记录器,用于产生 Log。Logger 有一个与之关联的 Log Level,表示记录 Log 要记录的最小等
级。Logger 可以选择是否记录它接收到的 Log,如果记录的 Log Level 低于 Logger 的 Log Level 则不记录。多个
Logger 可以形成一颗 Logger 的层次结构。
Appender 用于配置 Log 输出的目的地。Log 可以输出到控制台,文件,远程服务器等等。每个 Appender 都有
一个 Layout,用于格式化 Log 输出。一个 Logger 可以由多个 Appender 处理 Log 事件,例如,一个 Logger 可
以将所有 Error Level 的 Log 信息输出到一个文件中,同时将其他 Level 的 Log 输出到控制台。
Layout 用于格式化 Log 的输出方式。Layout 的作用是对 Log 进行格式化,然后由 Appender 输出。在 Layout
中可以定义 Log 的输出格式,比如时间,线程名称,Log Level 和 Log 的正文内容等。
Logback 的配置文件使用 XML 格式编写,一般命名为 logback.xml
。配置文件中包括了 Logger、Appender 和
Encoder 等标签。下面是一个基本的 xml 配置文件的示例:
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%npattern>
encoder>
appender>
<logger name="chapters.configuration" level="INFO"/>
<root level="DEBUG">
<appender-ref ref="STDOUT" />
root>
configuration>
上面示例中,定义了一个名为 STDOUT
的 Appender,它是 ConsoleAppender 类型,用于将日志信息输出到控制
台上。同时定义了一个根 Logger,日志级别为 DEBUG,并将日志信息输出到 STDOUT
Appender 中。Encoder
标签用于定义日志信息的输出格式。
<configuration>
<property name="pattern" value="[%-5level] %d{yyyy-MM-dd HH:mm:ss} %c %M %L %thread %m %n"/>
<appender name="myConsole" class="ch.qos.logback.core.ConsoleAppender">
<target>System.errtarget>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${pattern}pattern>
encoder>
appender>
<logger name="com.ahead" level="info" additivity="false">
<appender-ref ref="myConsole" />
logger>
configuration>
上传了具体日志实现的配置文件后,默认的SpringBoot配置信息就会作废。
logback.xml
配置文件的基本结构可以描述为
元素,包含零个或多个
元素,后跟
零个或多个
元素,后跟最多一个
元素(也可以没有)。
下图说明了这种基本结构:
元素只接受一个必需的name属性,一个可选的level属性和一个可选的additivity
属性,允许值为
true或false。level属性的值允许一个不区分大小写的字符串值TRACE,DEBUG,INFO,WARN,ERROR,ALL或OFF
。
特殊于大小写不敏感的值INHERITED
或其同义词NULL将强制记录器的级别从层次结构中的较高级别继承。
元素可以包含零个或多个
元素,这样引用的每个appender都被添加到指定的logger
中,logger元素级别具有继承性。
例1:示例中,仅为根记录器分配了级别。 此级别值DEBUG由其他记录器X,X.Y和X.Y.Z继承
Logger name | Assigned level | Effective level |
---|---|---|
root | DEBUG | DEBUG |
X | none | DEBUG |
X.Y | none | DEBUG |
X.Y.Z | none | DEBUG |
例2:所有记录器都有一个指定的级别值。 级别继承不起作用
Logger name | Assigned level | Effective level |
---|---|---|
root | ERROR | ERROR |
X | INFO | INFO |
X.Y | DEBUG | DEBUG |
X.Y.Z | WARN | WARN |
例3:记录器root,X和X.Y.Z分别被分配了DEBUG,INFO和ERROR级别。 Logger X.Y从其父X继承其级别值。
Logger name | Assigned level | Effective level |
---|---|---|
root | DEBUG | DEBUG |
X | INFO | INFO |
X.Y | none | INFO |
X.Y.Z | ERROR | ERROR |
例4:在示例4中,记录器root和X分别被分配了DEBUG和INFO级别。 记录器X.Y和X.Y.Z从其最近的父X继承其级别
值,该父级具有指定的级别。
Logger name | Assigned level | Effective level |
---|---|---|
root | DEBUG | DEBUG |
X | INFO | INFO |
X.Y | none | INFO |
X.Y.Z | none | INFO |
元素配置根记录器,它支持单个属性,即 level 属性。它不允许任何其他属性,因为 additivity 标志不适
用于根记录器。此外,由于根记录器已被命名为 ROOT,因此它也不允许使用 name 属性。
level 属性的值可以是不区分大小写的字符串 TRACE, DEBUG, INFO, WARN, ERROR, ALL, OFF
之一。
元素可以包含零个或多个
元素,这样引用的每个 appender 都被添加到根记录器中。
appender使用
元素配置,该元素采用两个必需属性 name 和 class。name 属性指定 appender 的名
称,而 class 属性指定要实例化的 appender 类的完全限定名称。
元素可以包含零个或一个
元素,零个或多个
元素以及零个或多个
元
素,下图说明了常见的结构:
重要:在logback中,输出目标称为appender
,addAppender
方法将appender
添加到给定的记录器logger。给
定记录器的每个启用的日志记录请求都将转发到该记录器中的所有appender
以及层次结构中较高的appender
。
换句话说,appender
是从记录器层次结构中附加地继承的。
例如,如果将控制台appender
添加到根记录器,则所有启用的日志记录请求将至少在控制台上打印。如果另外将
文件追加器添加到记录器(例如L),则对L和L的子项启用的记录请求将打印在文件和控制台上。通过将记录器的
additivity
标志设置为false,可以覆盖此默认行为,以便不再添加appender
累积。
Appender
是一个接口,它有许多子接口和实现类,具体如下图所示:
其中最重要的两个Appender为:ConsoleAppender
、RollingFileAppender
。
ConsoleAppender
,如名称所示,将日志输出到控制台上。
RollingFileAppender
是FileAppender
的一个子类,扩展了FileAppender
,具有翻转日志文件的功能。例
如RollingFileAppender
可以记录到名为log.txt
文件的文件,并且一旦满足某个条件,就将其日志记录目标
更改为另一个文件。
有两个与RollingFileAppender
交互的重要子组件。第一个RollingFileAppender
子组件,即RollingPolicy
负责执行翻转所需的操作。RollingFileAppender
的第二个子组件,即TriggeringPolicy
将确定是否以及何时
发生翻转。因此,RollingPolicy
负责什么和TriggeringPolicy
负责什么时候。
作为任何用途,RollingFileAppender
必须同时设置RollingPolicy
和TriggeringPolicy
。但是,如果其
RollingPolicy
也实现了TriggeringPolicy
接口,则只需要显式指定前者。
滚动策略:
TimeBasedRollingPolicy
:可能是最受欢迎的滚动策略。它根据时间定义翻转策略,例如按天或按月。
TimeBasedRollingPolicy
承担滚动和触发所述翻转的责任。实际上,TimeBasedTriggeringPolicy
实
现了RollingPolicy
和TriggeringPolicy
接口。
SizeAndTimeBasedRollingPolicy
:有时您可能希望按日期归档文件,但同时限制每个日志文件的大小,特
别是如果后处理工具对日志文件施加大小限制。为了满足此要求,logback 提供了
SizeAndTimeBasedRollingPolicy
,它是TimeBasedRollingPolicy
的一个子类,实现了基于时间和日志
文件大小的翻滚策略。
encoder中最重要就是pattern属性,它负责控制输出日志的格式,这里给出一个我自己写的示例:
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %highlight(%-5level) --- [%15.15(%thread)] %cyan(%-40.40(%logger{40})) : %msg%npattern>
其中:
# 日期
%d{yyyy-MM-dd HH:mm:ss.SSS}
# 日志级别
%-5level
# 颜色,info为蓝色,warn为浅红,error为加粗红,debug为黑色
%highlight()
# 打印日志的线程
%thread
# 如果记录的线程字符长度小于15(第一个)则用空格在左侧补齐,如果字符长度大于15(第二个),则从开头开始截断多余的字符
%15.15()
# 颜色
%cyan
# 日志输出的类名
# 表示logger名字最长40个字符,否则按照句点分割。
%logger{40}
# 如果记录的logger字符长度小于40(第一个)则用空格在右侧补齐,如果字符长度大于40(第二个),则从开头开始截断多余的字符
%-40.40()
# 日志输出内容
%msg
# 换行符
%n
格式配置请参考:https://logging.apache.org/log4j/2.x/manual/layouts.html
filter中最重要的两个过滤器为:LevelFilter
、ThresholdFilter
。
LevelFilter
根据精确的级别匹配过滤事件。如果事件的级别等于配置的级别,则筛选器接受或拒绝该事件,具
体取决于onMatch
和onMismatch
属性的配置。例如下面配置将只打印INFO级别的日志,其余的全部禁止打印输
出:
<configuration>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFOlevel>
<onMatch>ACCEPTonMatch>
<onMismatch>DENYonMismatch>
filter>
<encoder>
<pattern>
%-4relative [%thread] %-5level %logger{30} - %msg%n
pattern>
encoder>
appender>
<root level="DEBUG">
<appender-ref ref="CONSOLE" />
root>
configuration>
ThresholdFilter
过滤低于指定阈值的事件。对于等于或高于阈值的事件,ThresholdFilter
将在调用其
decision()
方法时响应NEUTRAL
。
但是,将拒绝级别低于阈值的事件,例如下面的配置将拒绝所有低于INFO级别的日志,只输出INFO以及以上级别
的日志:
<configuration>
<appender name="CONSOLE"
class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFOlevel>
filter>
<encoder>
<pattern>
%-4relative [%thread] %-5level %logger{30} - %msg%n
pattern>
encoder>
appender>
<root level="DEBUG">
<appender-ref ref="CONSOLE" />
root>
configuration>
<configuration scan="true" scanPeriod="10 seconds">
<contextName>spring-boot-logcontextName>
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
<conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
<conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />
<springProperty scope="context" name="APP_NAME" source="spring.application.name"/>
<property name="FILE_DIR" value="D:\\logs\\${APP_NAME}" />
<property name="ROLL_FILE_DIR" value="D:\\logs\\${APP_NAME}" />
<property name="CONSOLE_LOG_FORMAT" value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){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}}"/>
<property name="LOG_FORMAT" value="%date{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n" />
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>DEBUGlevel>
filter>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<springProfile name="dev">
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%-5level] %m%npattern>
springProfile>
<springProfile name="pro">
<pattern>${CONSOLE_LOG_FORMAT}pattern>
<charset>UTF-8charset>
springProfile>
encoder>
appender>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>${FILE_DIR}\\spring-log.logfile>
<append>trueappend>
<encoder>
<pattern>${LOG_FORMAT}pattern>
encoder>
appender>
<appender name="FILE_ROLLING" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${ROLL_FILE_DIR}\\spring-log-rolling.logfile>
<encoder>
<pattern>${LOG_FORMAT}pattern>
<charset>UTF-8charset>
encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${ROLL_FILE_DIR}\\spring-log-rolling.%d{yyyy-MM-dd}.%i.logfileNamePattern>
<maxHistory>30maxHistory>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MBmaxFileSize>
timeBasedFileNamingAndTriggeringPolicy>
<totalSizeCap>500MBtotalSizeCap>
<cleanHistoryOnStart>truecleanHistoryOnStart>
rollingPolicy>
appender>
<root level="info">
<appender-ref ref="CONSOLE" />
<appender-ref ref="FILE" />
<appender-ref ref="FILE_ROLLING" />
root>
configuration>
使用是需要设置:
spring.profiles.active = dev/pro
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<property name="log_dir" value="/customize/logs" />
<property name="maxHistory" value="30"/>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger -%msg%npattern>
encoder>
appender>
<appender name="ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERRORlevel>
<onMatch>ACCEPTonMatch>
<onMismatch>DENYonMismatch>
filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log_dir}/%d{yyyy-MM-dd}/error-log.logfileNamePattern>
<maxHistory>${maxHistory}maxHistory>
rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%npattern>
encoder>
appender>
<appender name="WARN" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>WARNlevel>
<onMatch>ACCEPTonMatch>
<onMismatch>DENYonMismatch>
filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log_dir}/%d{yyyy-MM-dd}/warn-log.log
fileNamePattern>
<maxHistory>${maxHistory}maxHistory>
rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%npattern>
encoder>
appender>
<appender name="INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFOlevel>
<onMatch>ACCEPTonMatch>
<onMismatch>DENYonMismatch>
filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log_dir}/%d{yyyy-MM-dd}/info-log.log
fileNamePattern>
<maxHistory>${maxHistory}maxHistory>
rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%npattern>
encoder>
appender>
<appender name="DEBUG" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>DEBUGlevel>
<onMatch>ACCEPTonMatch>
<onMismatch>DENYonMismatch>
filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log_dir}/%d{yyyy-MM-dd}/debug-log.log
fileNamePattern>
<maxHistory>${maxHistory}maxHistory>
rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%npattern>
encoder>
appender>
<appender name="TRACE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>TRACElevel>
<onMatch>ACCEPTonMatch>
<onMismatch>DENYonMismatch>
filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log_dir}/%d{yyyy-MM-dd}/trace-log.log
fileNamePattern>
<maxHistory>${maxHistory}maxHistory>
rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%npattern>
encoder>
appender>
<logger name="java.sql.PreparedStatement" value="DEBUG" />
<logger name="java.sql.Connection" value="DEBUG" />
<logger name="java.sql.Statement" value="DEBUG" />
<logger name="com.ibatis" value="DEBUG" />
<logger name="com.ibatis.common.jdbc.SimpleDataSource" value="DEBUG" />
<logger name="com.ibatis.common.jdbc.ScriptRunner" level="DEBUG"/>
<logger name="com.ibatis.sqlmap.engine.impl.SqlMapClientDelegate" value="DEBUG" />
<root level="debug">
<appender-ref ref="STDOUT" />
<appender-ref ref="ERROR" />
<appender-ref ref="INFO" />
<appender-ref ref="WARN" />
<appender-ref ref="DEBUG" />
<appender-ref ref="TRACE" />
root>
configuration>
<configuration debug="true">
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %highlight(%-5level) --- [%15.15(%thread)] %cyan(%-40.40(%logger{40})) : %msg%npattern>
<charset>UTF-8charset>
encoder>
appender>
<appender name="info_log" class="ch.qos.logback.core.rolling.RollingFileAppender">
<File>logs/project_info.logFile>
<append>trueappend>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERRORlevel>
<onMatch>DENYonMatch>
<onMismatch>ACCEPTonMismatch>
filter>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>logs/project_info.%d.%i.logfileNamePattern>
<maxHistory>30maxHistory>
<totalSizeCap>20GBtotalSizeCap>
<maxFileSize>10MBmaxFileSize>
rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level --- [%15.15(%thread)] %-40.40(%logger{40}) : %msg%npattern>
<charset>UTF-8charset>
encoder>
appender>
<appender name="error_log" class="ch.qos.logback.core.rolling.RollingFileAppender">
<File>logs/project_error.logFile>
<append>trueappend>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERRORlevel>
filter>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>logs/project_error.%d.%i.logfileNamePattern>
<maxHistory>30maxHistory>
<totalSizeCap>20GBtotalSizeCap>
<maxFileSize>10MBmaxFileSize>
rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level --- [%15.15(%thread)] %-40.40(%logger{40}) : %msg%npattern>
<charset>UTF-8charset>
encoder>
appender>
<root level="INFO">
<appender-ref ref="STDOUT" />
root>
<logger name="com.sailing.springbootmybatis" level="INFO">
<appender-ref ref="info_log" />
<appender-ref ref="error_log" />
logger>
<logger name="com.sailing.springbootmybatis.mapper" level="DEBUG" additivity="false">
<appender-ref ref="info_log" />
<appender-ref ref="error_log" />
logger>
<logger name="com.atomikos" level="INFO" additivity="false">
<appender-ref ref="info_log" />
<appender-ref ref="error_log" />
logger>
configuration>
这里再说下 log 日志输出代码,一般有人可能在代码中使用如下方式输出:
Object entry = new SomeObject();
logger.debug("The entry is " + entry);
上面看起来没什么问题,但是会存在构造消息参数的成本,即将entry转换成字符串相加。
并且无论是否记录消息,都是如此,即:哪怕日志级别为 INFO,也会执行括号里面的操作,但是日志不会输出,
下面是优化后的写法:
if(logger.isDebugEnabled()) {
Object entry = new SomeObject();
logger.debug("The entry is " + entry);
}
上面的写法,首先对设置的日志级别进行了判断,如果为debug模式,才进行参数的构造,对第一种写法进行
了改善。
不过还有最好的写法,使用占位符:
Object entry = new SomeObject();
logger.debug("The entry is {}.", entry);
只有在评估是否记录之后,并且只有在决策是肯定的情况下,记录器实现才会格式化消息并将{}
对替换为条目
的字符串值。换句话说,当禁用日志语句时,此表单不会产生参数构造的成本。
logback 作者进行测试得出:第一种和第三种写法将产生完全相同的输出。但是,在禁用日志记录语句的情况下,
第三个变体将比第一个变体优于至少30倍。
如果有多个参数,写法如下:
logger.debug("The new entry is {}. It replaces {}.", entry, oldEntry);
如果需要传递三个或更多参数,则还可以使用Object []
变体:
Object[] paramArray = {newVal, below, above};
logger.debug("Value {} was inserted between {} and {}.", paramArray);
记录日志的时候我们可能需要在文件中记录下异常的堆栈信息,经过测试,logger.error(e)
不会打印出堆
栈信息,正确的写法是:
logger.error("程序异常, 详细信息:{}", e.getLocalizedMessage() , e);
我们可以看到 info 的多个重载函数: