SpringBoot中的日志库分为两种:
关于外观库的出现,可设想一下:现在有多种日志库,每一种接口都不同。于是我们在不同项目中往往需要调用不同的API。于是希望,无论哪一种日志库,我们都在外面套一层壳,使用时调用这层壳的接口,这样就统一了。这层壳就是外观库。
对于这两种库,常见的有:
Log4j
、Log4j2
、Logback
Slf4j
、Apache Commons Logging
通常只需要引入Slf4j
依赖,然后通过Slf4j
来调用Logback
或Log4j
。
对应地,配置文件除了对Slf4j
进行配置,也可以对Logback
或Log4j
进行配置。
Logback相比于Log4j,性能提高了10倍以上的性能,占用的内存也变小了,并且文档十分详细。推荐使用Slf4j
+Logback
。
官网:
https://logback.qos.ch/
官方文档:
https://logback.qos.ch/documentation.html
Slf4j
、Logback
和Log4j
是同一个作者,使用了相同的设计,因此Slf4j
直接可调用Logback
和Log4j
。而对于其他日志实现库,例如java.util.logging
等,需要使用一个适配器模块来将Apache Commons Logging
的接口转换为Slf4j
的可调用接口。
Spring Boot
默认使用Logback
。只需要引入spring-boot-starter
或spring-boot-starter-web
就会默认包含,不需要再单独引入。
lombok
中默认包含了Slf4j
,因此只要引入了lombok
就无需再单独引入Slf4j
:
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.18.24version>
dependency>
要打印,就要先获取日志对象。
通常地,使用Slf4j
包中的LoggerFactory
来得到日志对象,然后打印:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class LoggerController {
// 得到日志对象
private Logger logger = LoggerFactory.getLogger(LoggerController.class);
@RequestMapping("/logger")
public String logger() {
// 日志打印
logger.trace("日志级别: trace");
logger.debug("日志级别: debug");
logger.info("日志级别: info");
logger.warn("日志级别: warn");
logger.error("日志级别: error");
return "logger";
}
}
如果项目中引入了lombok
组件,则可在类前添加@Slf4j
注解,这样就可在类中直接使用log
日志对象,而无需通过LoggerFactory
来获取。
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@Slf4j // 通过lombok的`@Slf4j`得到日志实例
public class LoggerController {
@RequestMapping("/logger")
public String logger() {
// 日志打印
log.trace("日志级别: trace");
log.debug("日志级别: debug");
log.info("日志级别: info");
log.warn("日志级别: warn");
log.error("日志级别: error");
return "logger";
}
}
日志有8个级别:
对于这8个级别,级别依次递增。级别越高,打印的日志越少。
通常地,只使用TRACE
、DEBUG
、INFO
、WARN
、ERROR
这5个等级。
Spring Boot会打印指定级别及更高级的日志。
例如日志设置为INFO
级,那么Spring Boot会打印INFO
、WARN
、ERROR
。
logback的配置可在application.yml和xml中配置,其中xml配置更加灵活。
若多个配置同时存在,则其加载顺序为:
logback.xml → application.yml → logback-spring.xml
后加载的会覆盖先加载的。因此application.yml会覆盖logback.xml中的相同配置。即后加载的优先级更高。
同理,若要使用 application.yml 中定义的变量,应使用 logback-spring.xml 。官方推荐使用 logback-spring.xml 。
加载logback.xml时,默认会在classpath
查找以下文件: 用户自定义xml > logback-test.xml > logback.groovy > logback.xml 。
如果上述4个配置文件都不存在,那么logback会调用BasicConfigurator
来创建一个最小化配置将日志输出到控制台。最小化配置会构造一个父为
的
,PatternLayoutEncoder
为%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
。
其中用户自定义xml
是在application.yml中通过logging.config=classpath:logging-config.xml
配置的。
logging:
config: classpath:logback.xml # 自定义配置文件
pattern:
file: "%d{HH:mm:ss.SSS} [%thread] %highlight(%-5level) %cyan(%logger{15} - %msg %n)" # 文件输出
console: "%d{HH:mm:ss.SSS} [%thread] %highlight(%-5level) %cyan(%logger{15} - %msg %n)" # 控制台输出
file:
name: E:\logs\test.log # 日志文件输出路径
level:
root: INFO # root输出级别
com.spring: WARN # 特定包的输出级别
<configuration>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
appender>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
appender>
<appender name="ROLLING" class="ch.qos.logback.core.RollingFileAppender">
appender>
<root>root>
<logger>logger>
configuration>
注意区分属性和配置的区别:
,
name就是
`标签的属性。testFile.log
,
就是
标签的配置。
是最外层的标签,所有其他标签都要定义在该标签内。
可设置多个属性:
例如:
<configuration scan="false" scanPeriod="60 seconds" debug="false">
上下文名称为了避免同一服务器上多个web应用的logger产生上下文冲突,可使用
标签设置上下文的名称,确保每个xml的
唯一即可。
例如:
<contextName>TestcontextName>
标签
标签负责写日志。定义了日志的输出位置、触发策略、格式等。注意只负责写,不负责日志等级。
一个
标签负责一种日志。由于一个系统中往往包含多种日志,因此通常同时使用多个
标签。
包含2个属性:
name
: 指定
的名称,可自定义。需在root
标签中引用。class
: 指定所使用实现库中Appender
类的含路径名称。关于class
,有以下几个常用取值:
ch.qos.logback.core.ConsoleAppender
: 输出到控制台。ch.qos.logback.core.FileAppender
: 输出到静态记录文件。ch.qos.logback.core.rolling.RollingFileAppender
: 输出到滚动日志文件。ConsoleAppender
ConsoleAppender
最为简单,直接输出到控制台。通常用于开发调试。
ConsoleAppender
常用的设置为:
: 对记录事件进行格式化。例如:
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>console %d %p - %m%npattern>
encoder>
appender>
FileAppender
FileAppender
会将所有的日志信息都写入到一个文件中。因此若append
属性为true
,则日志文件会越来越大。
FileAppender
常用的设置为:
: 文件名。无默认值,必须设置。其路径可以为绝对或相对。若上级目录不存在则会自动创建。
: 是否开启追加。默认为true。若为true
,则新的日志会追加到文件末尾;若为false
,则写新日志前会清空日志文件。
: 对记录事件进行格式化。
: 是否安全写入文件。默认为false。效率低。例如:
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>testFile.logfile>
<append>trueappend>
<encoder>
<pattern>%d %p - %m%npattern>
encoder>
RollingFileAppender
RollingFileAppender
会将日志滚动记录,即写入一个文件,当满足条件时,再写入下一个文件。例如设置文件大小为30MB,则当写入的日志文件达到30MB时会再新建一个日志文件进行写入。达到条件这个行为称为滚动。
RollingFileAppender
常用的设置为:
: 文件名。无默认值,必须设置。其路径可以为绝对或相对。若上级目录不存在则会自动创建。
: 是否开启追加。默认为true。若为true
,则新的日志会追加到文件末尾;若为false
,则写新日志前会清空日志文件。
: 滚动策略。通常包含:滚动条件(例如最大日志文件大小),文件命名格式,日志文件保存期限,等等。
: 日志输出格式。例如:
<appender name="ROLLING" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>testFile.logfile>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/${app-name}-%d{yyyy-MM-dd}-%i.logfileNamePattern>
<MaxHistory>30MaxHistory>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>30MBmaxFileSize>
timeBasedFileNamingAndTriggeringPolicy>
rollingPolicy>
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [ %-5level ] [ %thread ] - [ %logger{50} : %line ] - %msg%npattern>
layout>
滚动策略
使用class
属性指定滚动策略,不同的策略有不同的配置。
ch.qos.logback.core.rolling.TimeBasedRollingPolicy
。
: 文件名格式,必须。可以使用%d
转换符来接收一个java.text.SimpleDateFormat
对象,例如%d{yyyy-MM-dd HH:mm:ss.SSS}
。
: 日志文件保留天数。ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy
。
: 日志文件大小,默认为10MB。
: 是否安全写入文件。若为true
,不支持FixedWindowRollingPolicy
。支持TimeBasedRollingPolicy
,但此时必须满足:1不支持也不允许文件压缩;2不能设置file属性。
: 告知 RollingFileAppender 何时激活滚动。
标签
标签有2个作用:
启用。
其name
属性固定为ROOT
,且没有上级,故而
只有一个属性:
level
: 最重要,指定日志的打印等级,默认为DEBUG
。其值可以为:ALL
,TRACE
,DEBUG
,INFO
,WARN
,ERROR,
FATAL,
OFF。不可以设置为
INHERITED或
NULL`。可以在
下配置多个
,标识对应的
被添加到
下,从而可以继承
的属性和内容。
例如:
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE" />
<appender-ref ref="ROLLING" />
root>
其中
的ref
属性即为
或
的name
属性。
也是一个特殊的
,且为所有
的最上级父。
的parent
属性为null
。
标签
标签非必须,用于对某些日志等级进行单独定义。
例如设置
,但希望springframework
的等级为WARN
,此时就可添加一个
标签:
常用属性为:
name
: 指定需单独设置的组件类名。level
: 设置组件的日志等级。其值可以为:ALL
,TRACE
,DEBUG
,INFO
,WARN
,ERROR,
FATAL,
OFF。可设置为
INHERITED或
NULL`。若不设置,则默认继承上级的level。additivity
: 是否向上级
传递打印信息。默认为true
。若为true
,则本
打印一次,然后传递给上级
,上级
会再打印一次,这样就会造成重复打印。因此通常都设置为false
。例如:
<logger name="TEST_PARENT" level="INFO" additivity="false">
<appender-ref ref="TEST_CHILD"/>
logger>
这样就形成了父子关系,TEST_CHILD
的上级为TEST_PARENT
。
有一个parent
属性,指向其父
。每个
都有parent
,只有
除外,
是根节点。因此
和所有的
会形成一棵树。注意父子关系是由parent
属性形成的,而非类继承这样的关系。
变量有两种:本地变量和配置文件变量。
在xml中定义一个变量,然后在整个xml文件中引用。格式为:
通过${变量名}
格式使用变量。例如:
<property name="LOG_HOME" value="E:/logs/" />
<appender>
<file>${LOG_HOME}/${app-name}.logfile>
appender>
若希望从yml或.properties中读取变量值在xml中使用,则需要使用
。
例如,在application.yml中定义了一个logback_app_property.level
变量:
logback_app_property:
level: INFO
现在希望引用这个level
变量,则可在xml中添加
标签,并令其source
属性指向logback_app_property.level
变量。
<springProperty name="YML_LEVEL" source="logback_app_property.level" defaultValue="INFO"/>
同样是通过${变量名}
格式使用变量:
<root level="${YML_LEVEL}">
root>
首先在yml中定义
logging:
# 指定 logback-app.xml 作为logback的配置文件
config: classpath:logback-app.xml
# 用于 logback-app.xml 文件配置的参数值
logback_app_property:
# TRACE < DEBUG < INFO < WARN < ERROR
level: INFO
然后在logback-app.xml中定义:
<configuration>
<contextName>ApplicationcontextName>
<jmxConfigurator/>
<property name="LOG_HOME" value="E:/logs/" />
<property name="app-name" value="test-admin"/>
<property name="filename" value="server"/>
<property name="ENCODER_PATTERN" value="%red(%date{ISO8601}]) %highlight(%-5level) %green([%10.10thread]) [%X{X-B3-TraceId}] %boldMagenta(%logger{20}) - %cyan(%msg%n)" />
<appender name="consoleAppender" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${ENCODER_PATTERN}pattern>
<charset>UTF-8charset>
encoder>
appender>
<appender name="appLogAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/${app-name}.logfile>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/${app-name}-%d{yyyy-MM-dd}-%i.logfileNamePattern>
<MaxHistory>30MaxHistory>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>30MBmaxFileSize>
timeBasedFileNamingAndTriggeringPolicy>
rollingPolicy>
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [ %-5level ] [ %thread ] - [ %logger{50} : %line ] - %msg%npattern>
layout>
appender>
<appender name="errorLogAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/${app-name}-error.logfile>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/${app-name}-error-%d{yyyy-MM-dd}.logfileNamePattern>
<maxHistory>90maxHistory>
rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [ %-5level ] [ %thread ] - [ %logger{50} : %line ] - %msg%npattern>
encoder>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>WARNlevel>
filter>
appender>
<logger name="org.springframework" level="WARN"/>
<logger name="org.spring" level="WARN"/>
<logger name="org.hibernate" level="WARN"/>
<logger name="io.grpc.netty" level="OFF"/>
<logger name="org.eclipse.jetty" level="WARN"/>
<logger name="jndi" level="WARN"/>
<logger name="redis.clients" level="WARN"/>
<logger name="application" level="WARN"/>
<logger name="springfox.documentation" level="WARN"/>
<logger name="com.netflix" level="WARN"/>
<logger name="org.reflections" level="WARN"/>
<logger name="org.apache" level="WARN"/>
<logger name="io.grpc.internal.ClientCallImpl" level="OFF"/>
<logger name="org.springframework.amqp.rabbit" level="ERROR"/>
<logger name="com.baomidou.dynamic.datasource.DynamicRoutingDataSource" level="WARN"/>
<logger name="com.zaxxer.hikari.pool.HikariPool" level="WARN"/>
<logger name="org.quartz.core.QuartzSchedulerThread" level="ERROR"/>
<logger name="io.lettuce.core.protocol.RedisStateMachine" level="INFO"/>
<logger name="io.lettuce.core.RedisChannelHandler" level="INFO"/>
<logger name="io.lettuce.core.protocol.CommandHandler" level="INFO"/>
<logger name="io.lettuce.core.protocol.CommandEncoder" level="INFO"/>
<logger name="io.lettuce.core.protocol.DefaultEndpoint" level="INFO"/>
<logger name="io.lettuce.core.protocol.ConnectionWatchdog" level="INFO"/>
<logger name="io.lettuce.core.RedisClient" level="INFO"/>
<logger name="org.mybatis.spring.mapper.ClassPathMapperScanner" level="INFO"/>
<logger name="com.baomidou.mybatisplus.core.MybatisConfiguration" level="INFO"/>
<springProperty name="LEVEL" source="logback_app_property.level" defaultValue="INFO"/>
<root level="${LEVEL}">
<appender-ref ref="consoleAppender"/>
<appender-ref ref="appLogAppender" />
<appender-ref ref="errorLogAppender" />
root>
configuration>
在程序中使用代码来动态修改日志级别,首先导入库:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.LoggerContext;
然后获取
并修改level
:
String loggerName = "ROOT";
String loggerLevel = "DEBUG";
// 获取日志上下文
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
// 获取ROOT
ch.qos.logback.classic.Logger logger = loggerContext.getLogger(loggerName);
// 修改日志等级
logger.setLevel(Level.valueOf(loggerLevel));
注意这里的loggerName
与JMX不同,是大小写不敏感的,例如ROOT
可以写作root
。
JMX是一个带窗口的应用程序,为运行中的应用提供管理功能。JMX是跨平台的,与具体应用无关。配置logback开启JMX后即可通过JMX查看logback信息。
配置logback开启JMX,需在xml中添加一行标签:
当应用运行后,打开系统的CMD窗口,在其中输入命令:
jconsole
然后就会打开JMX的应用窗口,并提示新建连接。设应用程序运行在本地,则在本地进程的列表中选择对应的应用程序,然后双击或点连接。
之后可能弹出安全连接失败。是否以不安全的方式重试?,选择不安全的连接即可。
在上方的标签列表中点击MBean标签,然后在列表中找到ch.qos.logbak.classic
,依次点击进入default
→ch.qos.logback.classic.jmx.JMXConfigurator
,其下会列出属性和操作两个列表。在其下分别包含不同的功能。
对于属性,包含LoggerList
和Statuses
。其中LoggerList
列出了所有的Logger。特殊地,
标签其对应名称为ROOT
,会列在第一行。
对于操作,可进行:
null
。null
。点击属性下的LoggerList
,即可看到一个Logger数组。第一个就是ROOT
。
点击操作下的getLoggerLevel
,需输入一个参数p1
,该参数就是LoggerList
中的一个Logger。输入ROOT
,然后点击按钮getLoggerLevel,即可看到ROOT
的等级。注意这里的p1
是大小写敏感的,例如ROOT
不可以写作root
。
点击操作下的setLoggerLevel
,需输入两个参数p1
和p2
,p1
为Logger名称,p2
为level值。例如p1
输入ROOT
,p2
输入DEBUG
,然后点击按钮setLoggerLevel,即可将ROOT
变更为DEBUG
等级。
同理,更改应用的配置文件 logback.xml ,然后点击 操作 下的reloadDefaultConfiguration
,并点击右侧的reloadDefaultConfiguration
按钮,即可重新加载配置文件。
但在实际测试中,若配置文件为 logback.xml ,则表现相对正常;而若配置文件为其他名称,例如 logback-spring.xml ,则重载配置文件后原控制台将不再进行打印,且对配置文件的修改也不会生效。似乎reloadDefaultConfiguration
只识别 logback.xml 。
另外,修改配置文件只能是更改 logback.xml 本身。如果修改的是application.yml中的logback_app_property.level
变量,reloadDefaultConfiguration
时并不会重新读取该变量并应用到 logback.xml 中。
一般来说,通过JMX来修改的只有ROOT
,以及logback.xml中配置的多个
。