logback介绍
Logback继承自log4j。Logback的架构非常的通用,适用于不同的使用场景。
通过上图可以看到logback和Log4j都是slf4j规范的具体实现,我们在程序中直接调用的API其实都是slf4j的api,底层则是真正的日志实现组件—logback或者log4j。
Logback 构建在三个主要的类上:Logger,Appender 和 Layout。这三个不同类型的组件一起作用能够让开发者根据消息的类型以及日志的级别来打印日志。
Logger作为日志的记录器,把它关联到应用的对应的context后,主要用于存放日志对象,也可以定义日志类型、级别。各个logger 都被关联到一个 LoggerContext,LoggerContext负责制造logger,也负责以树结构排列各 logger。
Appender主要用于指定日志输出的目的地,目的地可以是控制台、文件、 数据库等。
Layout 负责把事件转换成字符串,输出格式化的日志信息。
logback的maven坐标:
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.3</version>
</dependency>
logback层级
在 logback中每一个 logger 都依附在 LoggerContext 上,它负责产生 logger,并且通过一个树状的层级结构来进行管理。
一个 Logger 被当作为一个实体,它们的命名是大小写敏感的,并且遵循以下规则:
如果一个logger的名字加上一个.作为另一个logger名字的前缀,那么该logger就是另一个logger的祖先。如果一个logger与另一个logger之间没有其它的logger,则该logger就是另一个logger的父级。
举例:
名为cn.itcast的logger是名为cn.itcast.service的logger的父级
名为cn的logger是名为cn.itcast的logger的父级,是名为cn.itcast.service的logger的祖先
在logback中有一个root logger,它是logger层次结构的最高层,它是一个特殊的logger,因为它是每一个层次结构的一部分。
logback日志输出等级
logback的日志输出等级分为:TRACE, DEBUG, INFO, WARN, ERROR。
如果一个给定的logger没有指定一个日志输出等级,那么它就会继承离它最近的一个祖先的层级。
为了确保所有的logger都有一个日志输出等级,root logger会有一个默认输出等级 — DEBUG。
logback初始化步骤
- logback会在类路径下寻找名为logback-test.xml的文件
- 如果没有找到,logback会继续寻找名为logback.groovy的文件
- 如果没有找到,logback会继续寻找名为logback.xml的文件
- 如果没有找到,将会在类路径下寻找文件META-INFO/services/ch.qos.logback.classic.spi.Configurator,该文件的内容为实现了Configurator接口的实现类的全限定类名
- 如果以上都没有成功,logback会通过BasicConfigurator为自己进行配置,并且日志将会全部在控制台打印出来
最后一步的目的是为了保证在所有的配置文件都没有被找到的情况下,提供一个默认的配置。
logback入门案例
案例一
本案例是一个logback简单应用,并且不提供配置文件而使用其提供的默认配置。
第一步:创建maven工程logback_demo并配置pom.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<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
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.itcast</groupId>
<artifactId>logback_demo</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
</project>
第二步:编写单元测试
package cn.itcast.logback;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.core.util.StatusPrinter;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* logback使用方法
*/
public class LogbackTest {
//简单使用
@Test
public void test1(){
Logger logger = LoggerFactory.getLogger("cn.itcast.logback.HelloWorld");
logger.debug("debug ...");
}
//打印日志内部状态
@Test
public void test2(){
Logger logger = LoggerFactory.getLogger("cn.itcast.logback.HelloWorld");
logger.debug("debug ...");
// 打印内部的状态
LoggerContext lc = (LoggerContext)LoggerFactory.getILoggerFactory();
StatusPrinter.print(lc);
}
/*
* 日志输出级别:ERROR > WARN > INFO > DEBUG > TRACE
* */
//测试默认的日志输出级别
@Test
public void test3(){
Logger logger = LoggerFactory.getLogger("cn.itcast.logback.HelloWorld");
logger.error("error ...");
logger.warn("warn ...");
logger.info("info ...");
logger.debug("debug ...");
//因为默认的输出级别为debug,所以这一条日志不会输出
logger.trace("trace ...");
}
//设置日志输出级别
@Test
public void test4(){
ch.qos.logback.classic.Logger logger = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger("cn.itcast.logback.HelloWorld");
logger.setLevel(Level.WARN);
logger.error("error ...");
logger.warn("warn ...");
logger.info("info ...");
logger.debug("debug ...");
logger.trace("trace ...");
}
//测试Logger的继承
@Test
public void test5(){
ch.qos.logback.classic.Logger logger =
(ch.qos.logback.classic.Logger) LoggerFactory.getLogger("cn.itcast");
logger.setLevel(Level.INFO);
logger.error("error ...");
logger.warn("warn ...");
logger.info("info ...");
logger.debug("debug ...");
logger.trace("trace ...");
// "cn.itcast.logback" 会继承 "cn.itcast" 的有效级别
Logger barLogger = LoggerFactory.getLogger("cn.itcast.logback");
// 这条日志会打印,因为 INFO >= INFO
barLogger.info("子级信息");
// 这条日志不会打印,因为 DEBUG < INFO
barLogger.debug("子级调试信息");
}
//Logger获取,根据同一个名称获得的logger都是同一个实例
@Test
public void test6(){
Logger logger1 = LoggerFactory.getLogger("cn.itcast");
Logger logger2 = LoggerFactory.getLogger("cn.itcast");
System.out.println(logger1 == logger2);
}
//参数化日志
@Test
public void test7(){
Logger logger = LoggerFactory.getLogger("cn.itcast");
logger.debug("hello {}","world");
}
}
案例二
本案例是logback中Spring Boot项目中的应用。
第一步:创建maven工程springboot_logback_demo并配置pom文件
<?xml version="1.0" encoding="UTF-8"?>
<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
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath/>
</parent>
<groupId>cn.itcast</groupId>
<artifactId>springboot_logback_demo</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
</project>
第二步:在resources下编写logback配置文件logback-base.xml和logback-spring.xml
logback-base.xml
<?xml version="1.0" encoding="UTF-8"?>
<included>
<contextName>logback</contextName>
<!--
name的值是变量的名称,value的值时变量定义的值
定义变量后,可以使“${}”来使用变量
-->
<property name="log.path" value="d:\\logs" />
<!-- 彩色日志 -->
<!-- 彩色日志依赖的渲染类 -->
<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" />
<!-- 彩色日志格式 -->
<property name="CONSOLE_LOG_PATTERN" 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}}"/>
<!--输出到控制台-->
<appender name="LOG_CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
<!-- 设置字符集 -->
<charset>UTF-8</charset>
</encoder>
</appender>
<!--输出到文件-->
<appender name="LOG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文件的路径及文件名 -->
<file>${log.path}/logback.log</file>
<!--日志文件输出格式-->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 每天日志归档路径以及格式 -->
<fileNamePattern>${log.path}/info/log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!--日志文件保留天数-->
<maxHistory>15</maxHistory>
</rollingPolicy>
</appender>
</included>
logback-spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!--引入其他配置文件-->
<include resource="logback-base.xml" />
<!--
<logger>用来设置某一个包或者具体的某一个类的日志打印级别、
以及指定<appender>。<logger>仅有一个name属性,
一个可选的level和一个可选的addtivity属性。
name:用来指定受此logger约束的某一个包或者具体的某一个类。
level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,
如果未设置此属性,那么当前logger将会继承上级的级别。
addtivity:是否向上级logger传递打印信息。默认是true。
-->
<!--开发环境-->
<springProfile name="dev">
<logger name="cn.itcast.controller" additivity="false" level="debug">
<appender-ref ref="LOG_CONSOLE"/>
</logger>
</springProfile>
<!--生产环境-->
<springProfile name="pro">
<logger name="cn.itcast.controller" additivity="false" level="info">
<appender-ref ref="LOG_FILE"/>
</logger>
</springProfile>
<!--
root节点是必选节点,用来指定最基础的日志输出级别,只有一个level属性
level:设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF 默认是DEBUG
可以包含零个或多个元素,标识这个appender将会添加到这个logger。
-->
<root level="info">
<appender-ref ref="LOG_CONSOLE" />
<appender-ref ref="LOG_FILE" />
</root>
</configuration>
第三步:编写application.yml
server:
port: 9000
logging:
#在Spring Boot项目中默认加载类路径下的logback-spring.xml文件
config: classpath:logback-spring.xml
spring:
profiles:
active: dev
第四步:创建UserController
package cn.itcast.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/user")
public class UserController {
Logger logger = LoggerFactory.getLogger(UserController.class);
@GetMapping("/get")
public String get(){
logger.trace("trace...");
logger.debug("debug...");
logger.info("info...");
logger.warn("warn...");
logger.error("error...");
return "OK";
}
}
第五步:创建启动类
package cn.itcast;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class LogbackApp {
public static void main(String[] args) {
SpringApplication.run(LogbackApp.class,args);
}
}
启动项目,访问地址:http://localhost:9000/user/get
可以看到控制台已经开始输出日志信息。
修改application.yml文件中的开发模式为pro,重启项目这日志输出到了文件中。
项目实战
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<!--appender=日志打印器,name=指定一个名称,logger通过这个name来引用它,class=实现类,ConsoleAppender=打印输出到java控制台-->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>
%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{100} - %msg%n
</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!--RollingFileAppender=可压缩归档的文件日志打印器-->
<appender name="info" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!--
file标签=当天的日志文件路径和文件名设置,${user.dir}为System.getProperty("user.dir"),
如果是java -jar xxx.jar运行的话,则user.dir=运行java -jar命令当时所在的目录,
如果是tomcat等容器中运行,则为运行容器命令所在的目录
-->
<file>${user.dir}/logs/sso/info/info.log</file>
<!--rollingPolicy=压缩和归档策略设置,SizeAndTimeBasedRollingPolicy=根据文件大小和时间进行归档-->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!--
fileNamePattern=归档文件路径和文件名设置,%d{yyyy-MM-dd}=输出日志时的年月日,
%i日志文件超出最大大小时,拆分的归档文件自增的文件索引,
.log.gz=后缀为.gz表示需要压缩归档,后缀为.log表示不需要压缩归档
-->
<fileNamePattern>
${user.dir}/logs/sso/info/info.%d{yyyy-MM-dd}.log
</fileNamePattern>
<!-- 单个日志文件大小,超出会拆分成多个文件,用自增索引区分-->
<maxFileSize>200MB</maxFileSize>
<!--日志保存天数,超出该日期的日志文件会被删除-->
<maxHistory>120</maxHistory>
<!--所有的日志文件总和最大大小,超出该大小后,会从最老的日志开始删除,一直删到符合该日志大小-->
<totalSizeCap>10GB</totalSizeCap>
</rollingPolicy>
<!--filter=过滤器,LevelFilter=级别过滤器,这里只打印输出INFO级别的日志-->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!--level=要匹配的级别,INFO,只有log.info(...)的日志会被该appender打印-->
<level>INFO</level>
<!--onMatch和onMismatch是用来配置这是正向过滤还是反向过滤的,只有两个值ACCEPT(承认)和DENY(拒绝)-->
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<!--encoder=打印编码器,用于设置打印的式和字符编码-->
<encoder>
<!--
pattern=打印的日志格式,%d{yyyy-MM-dd HH:mm:ss.SSS}=输出毫秒级时间,[%thread]输出线程ID,
%logger=日志类名,也就是Logger.getLogger(XXX.class)里的XXX,{100}=日志类名最长显示长度,
%msg=日志信息本身,%n=换行
-->
<pattern>
%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{100} - %msg%n
</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<appender name="debug" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${user.dir}/logs/sso/debug/debug.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>
${user.dir}/logs/sso/debug/debug.%d{yyyy-MM-dd}-%i.log.gz
</fileNamePattern>
<maxFileSize>200MB</maxFileSize>
<maxHistory>120</maxHistory>
<totalSizeCap>10GB</totalSizeCap>
</rollingPolicy>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>DEBUG</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<encoder>
<pattern>
%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{100} - %msg%n
</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<appender name="error" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${user.dir}/logs/sso/error/error.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>
${user.dir}/logs/sso/error/error.%d{yyyy-MM-dd}.log
</fileNamePattern>
<maxFileSize>200MB</maxFileSize>
<maxHistory>120</maxHistory>
<totalSizeCap>10GB</totalSizeCap>
</rollingPolicy>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<encoder>
<pattern>
%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{100} - %msg%n
</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<appender name="mybatis" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${user.dir}/logs/sso/sql/sql.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>
${user.dir}/logs/sso/sql/sql.%d{yyyy-MM-dd}.log
</fileNamePattern>
<maxFileSize>200MB</maxFileSize>
<maxHistory>120</maxHistory>
<totalSizeCap>10GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>
%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{100} - %msg%n
</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!--
logger=日志管理器,里面存放者平时log.debug(...)、log.info(...)、log.warn(...)、log.error(...)存放的日志信息,
name=要过滤的包名,additivity=是否传递给父级logger进行打印,false相当于阻断了继承关系。
logger有继承机制,根据name里的包名进行继承,aaa.bbb.ccc继承aaa.bbb,aaa.bbb继承aaa,aaa继承root。
-->
<logger name="com.xxxx.base.db.dao" additivity="false">
<!--level=日志级别,日志级别从低到高一共有:debug,info,warn,error-->
<level value="debug"/>
<appender-ref ref="mybatis"/>
</logger>
<logger name="com.xxxx.attachment.db.dao" additivity="false">
<level value="debug"/>
<appender-ref ref="mybatis"/>
</logger>
<logger name="com.xxxx.sso.db.dao" additivity="false">
<level value="debug"/>
<appender-ref ref="mybatis"/>
</logger>
<logger name="com.xxxx" additivity="false">
<level value="debug"/>
<appender-ref ref="debug"></appender-ref>
<appender-ref ref="info"></appender-ref>
<appender-ref ref="error"></appender-ref>
</logger>
<logger name="org.springframework.scheduling.concurrent" level="ERROR" additivity="false">
<appender-ref ref="error"></appender-ref>
</logger>
<logger name="org.springframework.context.support" level="ERROR" additivity="false">
<appender-ref ref="error"></appender-ref>
</logger>
<logger name="org.springframework" level="INFO" additivity="false">
<appender-ref ref="debug"></appender-ref>
<appender-ref ref="info"></appender-ref>
<appender-ref ref="error"></appender-ref>
</logger>
<logger name="com.alibaba.nacos" level="DEBUG" additivity="false">
<appender-ref ref="debug"></appender-ref>
<appender-ref ref="info"></appender-ref>
<appender-ref ref="error"></appender-ref>
</logger>
<logger name="org.mybatis.spring" additivity="false">
<level value="error"/>
<appender-ref ref="mybatis"/>
</logger>
<logger name="org.mybatis" additivity="false">
<level value="debug"/>
<appender-ref ref="mybatis"/>
</logger>
<logger name="com.baomidou" additivity="false">
<level value="debug"/>
<appender-ref ref="mybatis"/>
</logger>
<logger name="org.apache.ibatis" additivity="false">
<level value="debug"/>
<appender-ref ref="mybatis"/>
</logger>
<logger name="java.sql" additivity="false">
<level value="debug"/>
<appender-ref ref="mybatis"/>
</logger>
<!--root=根日志管理器,所有logger都直接或间接的继承root-->
<root level="INFO">
<appender-ref ref="STDOUT"/>
</root>
</configuration>