maven依赖
<!-- https://mvnrepository.com/artifact/ch.qos.logback/logback-classic -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
1 logback介绍
Logback 同第一个广泛应用的java日志框架log4j 都来自于Ceki Gülcü大神的手笔,logback继承于log4j,基于SLF4J提供的外观接口, 是两大java日志外观框架阵营 slf4j(也是来自Ceki Gülcü大神) 和common logging之中slf4j的官方实现。刚面世的时候由于性能上较log4j 和 jul的巨大提升受到java工程师的热捧,现在被log4j2爆了。
下面这段内容 摘自 http://www.logback.cn/
Logback 的架构非常的通用,适用不同的使用场景。Logback 被分成三个不同的模块:logback-core,logback-classic,logback-access。
logback-core 是其它两个模块的基础。logback-classic 模块可以看作是 log4j 的一个优化版本,它天然的支持 SLF4J,所以你可以随意的从其它日志框架(例如:log4j 或者 java.util.logging)切回到 logack。
logback-access 可以与 Servlet 容器进行整合,例如:Tomcat、Jetty。它提供了 http 访问日志的功能。
2 logback架构介绍
logback的架构主要基于三个对象进行配置
Bean | 描述 |
---|---|
Logger | 日志 |
Appender | 日志输出者 |
Encoder | 编码者 |
2.1 Logger 日志
logger对象是日志框架的核心对象,主要属性是name,用于产生日志事件,并绑定了一个或多个appender用于输出到指定的目的地。
如何获取一个logger实例?
example
Logger logger1 = LoggerFactory.getLogger(Dog.class);
Logger logger2 = LoggerFactory.getLogger("demo");
其中logger2 得到的实例及xml配置文件中声明的logger实例。
<configuration>
<logger name="demo" />
</configuration>
2.1.1 LoggerContext 上下文
Logback通过LoggerContext来管理所有的logger,每个logger都继承于ROOT根日志,这个我们可以从代码中看到,在初始化LoggerContext实例的时候会初始化一个名为ROOT的logger。
public LoggerContext() {
this.root.setLevel(Level.DEBUG);
this.loggerCache.put("ROOT", this.root);
this.initEvaluatorMap();
this.size = 1;
this.frameworkPackages = new ArrayList();
}
每一个 logger 都依附在 LoggerContext 上,它负责产生 logger,并且通过一个树状的层级结构来进行管理。
2.1.2 Level 有效层级
Logger 能够被分成不同的等级。不同的等级(TRACE, DEBUG, INFO, WARN, ERROR)定义在 ch.qos.logback.classic.Level 类中。
对于一个给定的名为 L 的 logger,它的有效层级为从自身一直回溯到 root logger,直到找到第一个不为空的层级作为自己的层级。
为了确保所有的 logger 都有一个层级,root logger 会有一个默认层级 — DEBUG
//只展示了部分接口
package org.slf4j;
public interface Logger {
void trace(String var1);
void trace(Marker var1, String var2);
void debug(String var1);
void debug(Marker var1, String var2);
void info(String var1);
void info(Marker var1, String var2);
void warn(String var1);
void warn(Marker var1, String var2);
void error(String var1);
void error(Marker var1, String var2);
}
层级有效力比较:TRACE < DEBUG < INFO < WARN < ERROR。
如果一条的日志的打印级别大于 logger 的有效级别,该条日志才可以被打印出来。
public final class Level implements Serializable {
public static final int ERROR_INT = 40000;
public static final int WARN_INT = 30000;
public static final int INFO_INT = 20000;
public static final int DEBUG_INT = 10000;
public static final int TRACE_INT = 5000;
}
有效力的大小设置,logback是通过给定一个int整数来比较的。
此外logback还支持使用marker标志来设定自定义的级别
Logger logger = LoggerFactory.getLogger(Dog.class);
Marker marker = MarkerFactory.getMarker("HAPPY");
logger.info(marker,"The litte dog felt very happy");
带有marker的输出语句的有效力等同于调用的输出方法的有效力,上例中marker的有效力即INFO的有效力。如果使用TRACE控制台就看不到这个日志消息了。
2.1.3 层级结构与配置传递
官方是这样解释的,如果一个 logger 的名字加上一个 . 作为另一个 logger 名字的前缀,那么该 logger 就是另一个 logger 的父级。
按照我们通常的做法,会使用类的完全限定名作为logger的名称,这样就可以避免名称的重复而获取到其他类中的重名的logger。
private static Logger logger = LoggerFactory.getLogger(Dog.class);
假设我们现在有一个类,creature.animal.Dog, 我们在代码中初始化一个logger实例,结果就会得到包含ROOT的四个logger实例对象,logback将根据完全限定名以 点 ( . )分割并依次初始化,以此来构建上下级的树形结构。
/**
* @author Kern
* @date 2020/3/26 17:04
* @description TODO
*/
public class Dog {
public static void main(String[] args) {
Logger logger = LoggerFactory.getLogger(Dog.class);
((LoggerContext)LoggerFactory.getILoggerFactory()).getLoggerList().forEach(e -> {
System.out.println(e.getName());
});
}
}
那么这个结构的意义在哪里? 配置的传递!
以level的配置为例,我们都明白初始化一个日志的时候我们是不需要设定Level的
一般情况下都是在初始化前进行配置。如果不配置level的话,本文第一段代码中也可以看到,ROOT的默认的level是DEBUG。
当子类的level配置为null时,logback将向上查找,直到找到一个level层级不为null的父类,并沿用他的level配置。如果子类的level配置不为null,则直接使用子类的level。 不仅如此,包括appender置也沿用这一规则。这样的设计帮助实现了一次配置,多处使用的功能,同时也是非常灵活的。
其次可以通过设定additivity的属性为false来阻断配置的传递(Why would you do that?)。
//java
((ch.qos.logback.classic.Logger)logger).setAdditive(false);
<!-- xml -->
<configuration>
<logger name="creature.animal.Dog" additivity="false"/>
</configuration>
由于上下文初始化时的默认行为,一个什么也不配置的logback 默认的Level为DEBUG,同时持有一个ConsoleAppender,也就是说默认情况下,将至少具备在控制台打印日志有效层级为DEBUG的消息。
public class LoggerContext extends ContextBase implements ILoggerFactory, LifeCycle {
final Logger root = new Logger("ROOT", (Logger)null, this);
public LoggerContext() {
//默认加载ROOT
this.root.setLevel(Level.DEBUG);
this.loggerCache.put("ROOT", this.root);
this.initEvaluatorMap();
this.size = 1;
this.frameworkPackages = new ArrayList();
}
}
-------------------------------分割线----------------------------
//在读取不到配置文件时,默认的配置类。
public class BasicConfigurator extends ContextAwareBase implements Configurator {
public BasicConfigurator() {
}
public void configure(LoggerContext lc) {
this.addInfo("Setting up default configuration.");
//控制台appender
ConsoleAppender<ILoggingEvent> ca = new ConsoleAppender();
ca.setContext(lc);
ca.setName("console");
LayoutWrappingEncoder<ILoggingEvent> encoder = new LayoutWrappingEncoder();
encoder.setContext(lc);
TTLLLayout layout = new TTLLLayout();
layout.setContext(lc);
layout.start();
encoder.setLayout(layout);
ca.setEncoder(encoder);
ca.start();
//下面两句加载到ROOT中
Logger rootLogger = lc.getLogger("ROOT");
rootLogger.addAppender(ca);
}
}
2.2 Appender 日志输出者
appender的任务是写入日志事件到目的地(文件/控制台/远程服务器)
一个 logger 可以有多个 appender。也就是说一个日志实例可以将其产生的日志事件写入到多个目的地。
站在 logback 的角度来说,输出目的地叫做 appender。appender 包括console、file、remote socket server、MySQL、PostgreSQL、Oracle 或者其它的数据库、JMS、remote UNIX Syslog daemons 中。
2.2.1 Appender接口结构
我们项目中常用的ConsoleAppender / FileAppender / RollingFileAppender 如上图,继承于 UnsynchronizedAppenderBase,其中的doAppend 方法与 AppenderBase 的 doAppend方法的主要差异在于方法上的同步锁,这也是类命名的由来。
AppenderBase 的 doAppend() 的实现是 synchronized 的。 不同的线程通过同一个 appender 打印日志是线程安全的。当一个线程 T 正在执行 doAppend() 方法,接下来其它的线程调用将会被阻塞直到线程 T 离开 doAppend() 方法,这样可以确保 T 对 appender 的访问具有独占性。
因为这种同步并不总是适合的,所以 logback 提供了 ch.qos.logback.core.UnsynchronizedAppenderBase 类,跟 AppenderBase 类十分的相似。
public abstract class UnsynchronizedAppenderBase<E> extends ContextAwareBase implements Appender<E> {
public void doAppend(E eventObject) {
//省略。。。
}
}
-------------------分割线-------------------------------
public abstract class AppenderBase<E> extends ContextAwareBase implements Appender<E> {
public synchronized void doAppend(E eventObject) {
//省略。。。
}
}
2.2.2 常用的appender简单介绍
具体的配置就不多赘述了。简单介绍一下功能及关键的属性
2.2.2.1 ConsoleAppender 输出到控制台
属性 | 值 | 描述 | 默认值 |
---|---|---|---|
encoder | 通过pattern配置输出格式 | ||
target | System.out/System.err | 控制台输出调用 | System.out |
配置示例
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<!-- <encoder 元素强制一个 class 属性去指定一个类的全限定名,用于实例化。
如果 encoder 的 class 是 PatternLayoutEncoder,那么基于默认类映射,class 属性可以被隐藏。-->
<encoder>
<pattern>[%t] %level %d{yyyy-MM-dd'T'HH:mm:ss} %class.%method[%line] %msg%n</pattern>
</encoder>
<!-- 默认为System.out System.out / System.err-->
<target>System.out</target>
</appender>
2.2.2.2 FileAppender 输出到文件
属性 | 值 | 描述 | 默认值 |
---|---|---|---|
append | boolean | 是否在文件中追加内容,false的话就覆盖log文件 | true |
encoder | 通过pattern配置输出格式 | ||
file | String | 文件名 | |
prudent | boolean | 严谨模式,文件加排他锁,影响性能 | false |
配置示例
<appender name="localFile" class="ch.qos.logback.core.FileAppender">
<!-- 文件名-->
<file>本地路径/log.log</file>
<!-- 只输出INFO以上的日志-->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
<!-- 是否追加,false覆盖,默认true-->
<append>true</append>
<!-- 将 immediateFlush 设置为 false 可以获得更高的日志吞吐量,默认方法更加的安全。 -->
<immediateFlush>true</immediateFlush>
<!-- encoder即该appender的输出格式配置-->
<encoder>
<pattern>%level %d{yyyy-MM-dd'T'HH:mm:ss} %class.%method[%line] %msg%n</pattern>
</encoder>
</appender>
2.2.2.3 RollingFileAppender 滚动文件输出
这部分内容文首中内容介绍的十分详细,就不复制黏贴了。还是需要仔细看看。为了文章的完整性,还是简单贴一些关键概念。
RollingFileAppender 继承自FileAppender,具有滚动输出日志文件的功能。
例如,RollingFileAppender 将日志输出到 log.txt 文件,在满足了特定的条件之后,将日志输出到另外一个文件。
属性 | 值 | 描述 | 默认值 |
---|---|---|---|
rollingPolicy | RollingPolicy | 负责日志轮转的功能。what to do? | |
triggeringPolicy | TriggeringPolicy | 它负责日志轮转的时机,通知 RollingFileAppender 何时轮转。。 when to do? |
示例配置
<appender name="warnRolling" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 滚动规则-->
<!-- 只输出WARN以上的日志-->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>WARN</level>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--这里为了实验,时间格式配置到了分秒,实际生产中是不可能这么小的时间粒度的 -->
<fileNamePattern>本地路径/%d{yyyy-MM-dd'T'HHmm}/rollingLog.%d{yyyy-MM-dd'T'HHmmss,aux}.log</fileNamePattern>
<maxHistory>5</maxHistory>
<totalSizeCap>1MB</totalSizeCap>
</rollingPolicy>
<!-- encoder即该appender的输出格式配置-->
<encoder>
<pattern>%date %level [%thread] %logger{10} [%file:%line] %msg%n</pattern>
</encoder>
</appender>
2.2.3 Encoder
从上面的例子中可以看到,每个appender中都包含了一个encoder元素,我们从常用的三个Appender的父类OutputStreamAppender也可以看到Encoder属性
public class OutputStreamAppender<E> extends UnsynchronizedAppenderBase<E> {
protected Encoder<E> encoder;
}
如果把appender解释为日志的输出者,那么encoder就是日志消息的解释器,encoder接口负责把日志消息转换为字节数组,其实现之一PatternLayoutEncoder是我们在xml配置文件中常用到的格式化的定制日志消息的解释器。
2.2.3.1 LayoutWrappingEncoder
直到 logback 的 0.9.19 版本,许多 appender 依赖 layout 实例去控制日志的格式化输出。因为基于 layout 接口存在了大量的代码,所以我们需要一种方式容 encoder 与 layout 进行交互。LayoutWrappingEncoder 就是 encoder 与 layout 之间的桥梁。它实现了 encoder 接口并且包裹了一个 layout,通过委托该 layout 将日志事件转换为字符串。
2.2.3.2 PatternLayoutEncoder
由于 PatternLayout 是最常用的 layout,logback 使用 PatternLayoutEncoder 来满足这种用法。它扩展了 LayoutWrappingEncoder,被限制用来包裹 PatternLayout 实例。
在 logback 0.9.19 版本,无论 FileAppender 还是其子类通过 PatternLayout 来进行配置,都必须使用 PatternLayoutEncoder 来代替。
outputPatternAsHeader
为了帮助解析日志文件,logback 可以将格式化字符串插入到日志文件的顶部。这个功能默认是关闭的。可以为相关的 PatternLayoutEncoder 设置 outputPatternAsHeader 属性的值为 true 来开启这个功能。
2.2.3.3 Layout
layout 是 logback 的组件,负责将日志事件转换为字符串。Layout 接口中的 format() 方法接受一个表示日志事件的对象 (任何类型) 并返回一个字符串。
常用的 PatternLayout 格式字符串解析
编码 | 释义 |
---|---|
%c{length} / %lo{length} / %logger{length} | 日志的名称 |
%C{length} / %class{length} | 输出发出日志请求的全限定名称 |
%contextName / %cn | logger上下文名称 |
%d{pattern} / %date{pattern} / %d{pattern, timezone} / %date{pattern,timezone} | 时间{格式,时区} |
%L / %line | 所在行号,生成行号不是特别快,因此不建议使用。 |
%m / %msg / %message | 输出日志消息 |
%M / %method | 输出发出日志请求的方法名 |
%n | 换行 |
%p / %le / %level | 输出日志时间的级别 |
%r / %relative | 输出应用程序启动到创建日志事件所花费的毫秒数 |
%t / %thread | 输出生成日志事件的线程名 |
3 完整配置及DEMO测试
logback 环境的配置会在应用初始化的时候完成。最优的方式是通过读取配置文件。当然也可以利用ServiceLoader机制加载继承了BasicConfigurator的配置类来完成。这里采用常用的xml配置。
3.1 配置文件查找规则
这里简单介绍一个logback加载配置文件的规则:
- 1.logback 会在类路径下寻找名为 logback-test.xml 的文件。
- 2.如果没有找到,logback 会继续寻找名为 logback.groovy 的文件。
- 3.如果没有找到,logback 会继续寻找名为 logback.xml 的文件。
- 4.如果没有找到,将会通过 JDK 提供的 ServiceLoader 工具在类路径下寻找文件 META-INFO/services/ch.qos.logback.classic.spi.Configurator,该文件的内容为实现了 Configurator 接口的实现类的全限定类名。
- 5.如果以上都没有成功,logback 会通过 BasicConfigurator 为自己进行配置,并且日志将会全部在控制台打印出来。
3.2 配置文件内容简要
每一个xml配置文件用 configuration 标签包围,不需要xml文件头。
其中包括 :
- appender
- logger
- root
<configuration>
<!-- 0-N个-->
<appender name="" class="">
<!-- 1个-->
<encoder>
<!-- 1个-->
<pattern></pattern>
</encoder>
</appender>
<!-- 0-N个-->
<logger name="">
</logger>
<!-- 1个-->
<root level="">
<!-- 0-N个-->
<appender-ref ref="" />
</root>
</configuration>
3.3 测试Demo
新建springboot项目
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 https://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.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>cn.kerninventory</groupId>
<artifactId>demo-slf4j-logback</artifactId>
<version>1.0-SNAPSHOT</version>
<name>demo-slf4j-logback</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- 移除spingboot中旧版本的logback-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
<exclusion>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- https://mvnrepository.com/artifact/ch.qos.logback/logback-classic -->
<!--logback中有slf4j的依赖,不用重复引,不然还可能不兼容 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
package cn.kerninventory.demoslf4jlogback;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class DemoSlf4jLogbackApplication {
public static void main(String[] args) {
SpringApplication.run(DemoSlf4jLogbackApplication.class, args);
}
}
新建logback-test.xml 文件
<!--配置文件,只要符合logback的解析规则就可以-->
<!--在 logback 版本 0.9.17 之后,显示规定的标签名不区分大小写。-->
<!--appender 输出目的地-->
<!--layout 输出的格式-->
<!--root 根日志-->
<!--scan=true, 配置文件修改时自动扫描,默认一分钟扫描一次, 该文件中设置30S扫描一次-->
<!--当设置了 scan="true",会新建一个 ReconfigureOnChangeTask 任务用于监视配置文件是否变化。ReconfigureOnChangeTask 也会自动监视外部文件的变化。-->
<!--如果更改后的配置文件有语法错误,则会回退到之前的配置文件。-->
<!-- packagingData="true"启用展示包数据,开销比较大-->
<configuration
scan="true" scanPeriod="30 seconds"
packagingData="true"
>
<!-- 设置日志上下文的名称-->
<contextName>LOGBACK</contextName>
<!-- 设置属性-->
<property name="basicDir" value="/logs" />
<!-- 设置属性,引入其他配置文件,
这个配置文件配置了一个dir.logs的路径属性,
下文中看到的话,可以替换为自己需要的路径
-->
<property resource="filedir.properties" />
<!-- 配置该属性则将打印配置文件的状态, 这将与设置root.level=debug 取得一样的效果-->
<statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" />
<!-- appender 通过 <appender> 元素进行配置,
需要两个强制的属性 name 与 class。
name 属性用来指定 appender 的名字,
class 属性需要指定类的全限定名用于实例化。
<appender> 元素可以包含
0 或一个 <layout> 元素,
0 或多个 <encoder> 元素,
0 或多个 <filter> 元素。
除了这些公共的元素之外,<appender> 元素可以包含任意与 appender 类的 JavaBean 属性相一致的元素。-->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<!-- <encoder 元素强制一个 class 属性去指定一个类的全限定名,用于实例化。
如果 encoder 的 class 是 PatternLayoutEncoder,那么基于默认类映射,class 属性可以被隐藏。-->
<encoder>
<pattern>[%t] %level %d{yyyy-MM-dd'T'HH:mm:ss} %class.%method[%line] %msg%n</pattern>
</encoder>
<!-- 默认为System.out System.out / System.err-->
<target>System.out</target>
</appender>
<!-- 通过 "bySecond" 将时间格式化成 "yyyyMMdd'T'HHmmss" 的形式插入到 logger 的上下文中,这个值对后续的配置也适用-->
<property name="filename" value="${dir.logs}/demolog.log"/>
<appender name="localFile" class="ch.qos.logback.core.FileAppender">
<!-- 文件名-->
<file>${filename}</file>
<!-- 只输出INFO以上的日志-->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
<!-- 是否追加,false覆盖,默认true-->
<append>true</append>
<!-- 将 immediateFlush 设置为 false 可以获得更高的日志吞吐量,默认方法更加的安全。 -->
<immediateFlush>true</immediateFlush>
<!-- encoder即该appender的输出格式配置-->
<encoder>
<pattern>%level %d{yyyy-MM-dd'T'HH:mm:ss} %class.%method[%line] %msg%n</pattern>
</encoder>
</appender>
<appender name="warnRolling" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 滚动规则-->
<!-- 只输出WARN以上的日志-->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>WARN</level>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${dir.logs}/%d{yyyy-MM-dd'T'HHmm}/rollingLog.%d{yyyy-MM-dd'T'HHmmss,aux}.log</fileNamePattern>
<maxHistory>5</maxHistory>
<totalSizeCap>1MB</totalSizeCap>
</rollingPolicy>
<!-- encoder即该appender的输出格式配置-->
<encoder>
<pattern>%date %level [%thread] %logger{10} [%file:%line] %msg%n</pattern>
</encoder>
</appender>
<!-- 手动添加的logger 除name之外其余都是可选的 -->
<logger name="default" level="debug" additivity="true">
<appender-ref ref="console" />
<appender-ref ref="localFile" />
</logger>
<!-- 最多只能有一个root元素存在-->
<!-- root的level和appender-ref是唯二可以配置的属性-->
<root level="DEBUG">
<!-- root 与 输出目的地的映射-->
<appender-ref ref="console" />
<appender-ref ref="localFile" />
<appender-ref ref="warnRolling"/>
</root>
</configuration>
测试接口
package cn.kerninventory.demoslf4jlogback.web;
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;
/**
* @author Kern
* @date 2020/3/26 15:36
* @description TODO
*/
@RestController
@RequestMapping("/demo")
public class DemoController {
private static Logger logger = LoggerFactory.getLogger(DemoController.class);
@GetMapping("")
public String toLogging() throws InterruptedException {
logger.info("logging start!");
for (int i = 0 ; i < 500; i++) {
logger.info("PC {}", i);
if ((i & 3) == 0) {
logger.warn("i={},i & 3 == 0", i);
}
if ((i & 5) == 0) {
logger.error("i={},i & 5 == 0", i);
}
Thread.currentThread().sleep(300);
}
logger.info("logging end!");
return "Hello logback!";
}
}
运行结果
可以看到rollingLog根据时间区间生成了多个文件夹和多个文件。然后这里的文件生成不完全根据你定义的时间,我定义的每一秒都不一样的文件,但是结果是没有的。其中应该有算法根据文件数量和时间区间进行控制,
控制台,配置了DEBUG,所以几种级别的日志都有。
demolog.log, 用Filter过滤了INFO以下的日志消息。
rollingLog,用Filter过滤了WARN以下的日志消息。
本文就到这里了。算是比较系统的过了一遍logback,应对日常的开发足够了。有兴趣的小伙伴可以再深入研究一下。
参考资源: http://www.logback.cn/