1 简介
logback官方文档
在实际项目开发中,日志框架是不可避免的。目前常用的日志框架包括log4j和logback等。
该文章将选取logback作为日志框架讲解。logback提供的日志功能主要有两个方面:
- 通过logback框架可以在控制台或者日志文件记录日志信息
- 拦截用户请求,将操作日志保存到数据库 使用logback框架
Logback 构建在三个主要的类上:Logger,Appender 和 Layout。这三个不同类型的组件一起作用能够让开发者根据消息的类型以及日志的级别来打印日志。
Logger作为日志的记录器,把它关联到应用的对应的context后,主要用于存放日志对象,也可以定义日志类型、级别。各个logger 都被关联到一个 LoggerContext,LoggerContext负责制造logger,也负责以树结构排列各 logger。
Appender主要用于指定日志输出的目的地,,目的地可以是控制台、文件、远程套接字服务器、 MySQL、PostreSQL、 Oracle和其他数据库、 JMS和远程UNIX Syslog守护进程等。
Layout 负责把事件转换成字符串,输出格式化的日志信息。
2 日志树形结构
/**
* 日志logger以树状形式来组织
* 以.作为前缀分隔符,
*/
public static void testLoggerTree(){
java.util.logging.Logger logger1= java.util.logging.Logger.getLogger("x");
java.util.logging.Logger logger2= java.util.logging.Logger.getLogger("x.y");
java.util.logging.Logger logger3= java.util.logging.Logger.getLogger("x.y.z");
//查询日志1,2,3的父级日志
//x.y
System.out.println(logger3.getParent().getName());
//x
System.out.println(logger2.getParent().getName());
//"",其父级日志是java.util.logging.LogManager$RootLogger
//根据层级关系,所有的日志的顶层parent均是java.util.logging.LogManager$RootLogger
System.out.println(logger1.getParent().getName());
}
3 日志级别
Logback框架分为5个日志级别,TRACE < DEBUG < INFO < WARN < ERROR。框架会根据设定的不同日志级别打印出不同的日志信息。
在未设置日志级别的情况下,会继承日志层级关系最靠近的那个日志级别。
日志名称 | 设置的日志级别 | 生效的日志级别 |
---|---|---|
root | DEBUG | DEBUG |
X | none | DEBUG |
X.Y | none | DEBUG |
X.Y.Z | none | DEBUG |
root日志下的子日志包括X,X.Y,X.Y.Z,在所有的子日志均未设置日志级别情形下,将继承顶级root的日志级别DEBUG
日志打印生效的核心原则是 日志请求级别大于等于设定的日志logger的级别
public class Mylogger {
public static void main(String[] args) {
//logger类型为ch.qos.logback.classic.Logger;
Logger logger= (Logger) LoggerFactory.getLogger("com.logger");
//TRACE < DEBUG < INFO < WARN < ERROR
//设置日志级别 类型为ch.qos.logback.classic.Logger
logger.setLevel(Level.INFO);
//DEBUG<INFO 日志打印被禁用,无输出
logger.debug("debug ing");
//等于 14:34:36.561 [main] INFO com.logger - info ing
logger.info("info ing");
//大于 14:34:36.564 [main] WARN com.logger - warn ing
logger.warn("warn ing");
}
4日志初始化流程
- logback会在类路径下寻找名为logback-test.xml的文件
- 如果没有找到,logback会继续寻找名为logback.groovy的文件
- 如果没有找到,logback会继续寻找名为logback.xml的文件
- 如果没有找到,将会在类路径下寻找文件META-INFO/services/ch.qos.logback.classic.spi.Configurator,该文件的内容为实现了Configurator接口的实现类的全限定类名
- 如果以上都没有成功,logback会通过BasicConfigurator为自己进行配置,并且日志将会全部在控制台打印出来
最后一步的目的是为了保证在所有的配置文件都没有被找到的情况下,提供一个默认的配置。
/**
* Logger的初始化过程
*/
public static void testLogInit(){
//获得日志的上线文
LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
//打印内部状态
StatusPrinter.print(context);
}
结果与日志初始化描述一致
5Logback自定义配置
【整合SpringBoot项目进行说明】
1 相关依赖
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<!--web starter底层依赖了logging框架-->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
2 logback-base.xml 【用于配置logger基础信息】
<?xml version="1.0" encoding="UTF-8"?>
<included>
<!--log-->
<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>
3 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="com.controller" additivity="false" level="debug">
<appender-ref ref="LOG_CONSOLE"/>
</logger>
</springProfile>
<!--生产环境-->
<springProfile name="pro">
<logger name="com.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>
4 测试类
@RestController
@RequestMapping("/user")
public class UserController {
//类路径为com.controller.UserController
//继承com.controller下的logger属性 所以Logger的级别为DEBUG
Logger logger = LoggerFactory.getLogger(UserController.class);
@GetMapping("/get")
public String get(){
//TRACE<DEBUG 忽略
logger.trace("trace...");
//打印输出
logger.debug("debug...");
//打印输出
logger.info("info...");
//打印输出
logger.warn("warn...");
//打印输出
logger.error("error...");
return "OK";
}
}