日志logback详解

这篇博客是我参考了很多资料总结出来的,内容很多,希望能帮助到你^_^

可以先看下目录了解下大体内容: 

一、日志框架发展史

二、日志规范

三、日志级别

四、Logback

4.1 Maven引入

4.2 logback-spring.xml是在哪里加载的?

4.3 logback.xml和logback-spring.xml区别

五、logback-spring.xml格式详解

5.1 完整格式

5.2 configuration

5.2.1 定时器是如何生效的?

5.3 contextName

5.4 property和springProperty

5.5 timestamp

5.6.logger和root

5.6.1 说明

5.6.2.appender-ref

5.7 appender

5.7.1 ConsoleAppender

5.7.2 FileAppender

5.7.3 RollingFileAppender

5.7.3.1 TimeBasedRollingPolicy

5.7.3.2 SizeBasedTriggeringPolicy

5.7.3.3 FixedWindowRollingPolicy

5.7.3.4 SizeAndTimeBasedRollingPolicy

5.8 encoder说明

5.8.1 %n

5.8.2 %c %lo %logger

 5.8.3 %C %class

5.8.4.%d %date

5.8.5.%caller

5.8.6.%L %line

5.8.7 %m %msg %message

5.8.8.%M %method

5.8.9 %p %le %level

5.8.10 %r %relative

5.8.11.%t %thread

5.8.12 %replace

6.8.13 宽度设置

5.8.14.显示设置

5.8.14.1.%clr和defaults.xml

5.8.14.2.支持的颜色

 5.9 filter过滤规则

5.9.1 LevelFilter

5.9.2 ThresholdFilter

5.9.3 EvaluatorFilter

5.9.4 自定义过滤器

六、优化异常堆栈stack日志打印

6.1 未优化前

6.2.优化后


一、日志框架发展史

参考博客:

【精选】Java日志框架的发展历史,你不想了解一下吗_java日志历史-CSDN博客 [Java日志框架的发展历史,你不想了解一下吗]

第1阶段:只有System.out 与System.error

第2阶段:Apache大佬Ceki Gülcü 搞了个 Log4j,爆火

第3阶段:Sun眼红Log4j, 自己制定一套标准JUL  (Java Util Logging),没啥人用

第4阶段:Apache不服,也自己制定一套标准JCL (Jakarta Commons Logging), 可以在Log4j和JUL之间切换,但存在问题。

第5阶段,大佬Ceki Gülcü离开Apache公司,觉得JCL不好用,自己搞了个Slf4j (Simple Logging Facade For Java 简单日志门面)。但想要Apache和Sun来对接Slf4j太难,于是弄了桥接包,通过桥接包来帮助Slf4j接口与其他日志库建立关系,这种方式称桥接设计模式。

但仍然存在一个问题:日志配置文件未做统一,假如你的系统使用了Slf4j作为日志接口,使用Log4j作为日志产品,则配置文件需要配2份。

Ceki Gülcü发话: 没事,大家都选择用Slf4j统一吧,我来帮大家统一,没有事是桥接包解决不了的,有的话,那就再来个。

第6阶段:Ceki Gülcü 巨佬觉得市场上的日志标准库都是间接实现Slf4j接口,也就是说每次都需要配合桥接包,也就是之前的日志产品都不是正统的Slf4j的实现。

因此在2006年,Ceki Gülcü 基于Slf4j接口写出了Logback日志标准库

第7阶段:Slf4j+Logback的模式,显然很冲击JCL+Log4j,并且本身Logback确实比Log4j性能更优,设计更为合理,所以,老东家Apache可就坐不住了。

   在2012年,Apache直接推出新项目,不是Log4j1.x升级,而是新项目Log4j2,因为Log4j2是完全不兼容Log4j1.x的。

   并且很微妙的,Log4j2几乎涵盖Logback所有的特性(这不是对着干是啥~而且还有抄袭的嫌疑哈哈哈),更甚者的Log4j2也搞了分离的设计,分化成log4j-api和log4j-core这个log4j-api也是日志接口,log4j-core才是日志产品

二、日志规范

参考博客

https://www.cnblogs.com/yangyongjie/p/16230247.html [Java日志规范]

1、【强制】应用中不可直接使用日志系统(Log4j、Logback)中的 API,而应依赖使用日志框架 (SLF4J)中的 API,使用门面模式的日志框架。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Component
public class SpringUtil implements ApplicationContextAware {
    private static final Logger log = LoggerFactory.getLogger(SpringUtil.class);
}

或者使用lombok注解:

import lombok.extern.slf4j.Slf4j;
@Component
@Slf4j
public class SpringUtil implements ApplicationContextAware {
}

2、【强制】所有日志文件至少保存15天,因为有些异常具备以“周”为频次发生的特点。日志文件格式为: {logname}.log.{保存日期},日期格式:yyyy-MM-dd,例如info.log.2023-04-21

3、【强制】对 trace/debug/info 级别的日志输出,必须使用条件输出形式或者使用占位符的方式。

错误示例:(如果日志级别是 warn,该日志不会打印,但是会执行字符串拼接操作,如果 symbol 是对象,会执行 toString()方法,浪费了系统资源,但最终日志却没有打印)

log.debug("Processing trade with id: " + id + " and symbol: " + symbol);

解决方法一:打印日志前加上判断:

if (log.isInfoEnabled()) {
    log.info("Processing trade with id: " + id + " and symbol: " + symbol);
}
if (log.isWarnEnabled()) {
    log.warn("Processing trade with id: " + id + " and symbol: " + symbol);
}
if (log.isDebugEnabled()) {
    log.debug("Processing trade with id: " + id + " and symbol: " + symbol);
}

解决方法二(推荐使用)使用参数化形式{}占位,[] 进行参数隔离因为 String 字符串的拼接会使用 StringBuilder 的 append()方式,有一定的性能损耗。使用占位符仅是替换动作,可以有效提升性能。

log.debug("Processing trade with id: [{}] and symbol: [{}]", id, symbol);

4、【强制】生产环境禁止直接使用 System.out 或 System.err 输出日志或使用 e.printStackTrace()打印异常堆栈。

5、【强制】生产环境禁止打印debug日志

6、【建议】打印日志不要使用JSON.toJSONString()写法,而是通过重写对象的toString()方法(可以使用lombok的 @ToString(callSuper=true))。

错误示例:

log.info("基础数据拉取sc:{},mqJson:{}", dockDTO.getNeighNo(), JSON.toJSONString(amcBaseInfoPo));

正确示例:

log.info("基础数据拉取sc:{},mqJson:{}", dockDTO.getNeighNo(), amcBaseInfoPo);

三、日志级别

参考博客

日志打印的8种级别(很详细)_日志级别-CSDN博客[日志打印的8种级别(很详细)]

log4j定义了8个级别的log,优先级从高到低依次为:

OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL
如果日志设置为L,一个级别为P的输出日志只有当P >= L时日志才会输出。

Log4j建议只使用四个级别,优先级从高到低分别是ERROR、WARN、INFO、DEBUG

四、Logback

参考博客:

logback 日志 介绍和配置详解_logback日志配置-CSDN博客 [logback 日志 介绍和配置详解]

万字详解logback日志框架,再没这么全的了!-腾讯云开发者社区-腾讯云 [万字详解logback日志框架,再没这么全的了!]

logback详解 - 知乎[logback详解]

(三)Logback中的Appender - 程序员大本营[(三)Logback中的Appender]

Java日志框架:logback详解 - 知乎[Java日志框架:logback详解]

logback -- 配置详解 -- 三 -- <encoder>[logback -- 配置详解 -- 三 -- <encoder>]

logback 配置颜色高亮_黑帽子技术的博客-CSDN博客[logback 配置颜色高亮]

LogBack 基本使用_凯凯JAVA的博客-CSDN博客[LogBack 基本使用]

(六)Logback中的Filter_logback filter-CSDN博客[(六)Logback中的Filter]

logback logback.xml常用配置详解(三) <filter>-CSDN博客[logback logback.xml常用配置详解(三) <filter>]

RollingFileAppender详解[RollingFileAppender详解]

logback解析——Appender - 简书[logback解析——Appender]

4.1 Maven引入

Spring Boot 默认使用 SLF4J+Logback 记录日志。

1、springBoot已经帮我们引入了logback包,所以我们不需要再去引用。

2、在项目资源文件夹 resources 下 创建 logback-spring.xml 文件。logback 将会自动读取该配置文件。

logback是springboot自带的日志框架.该框架主要有3个模块:

logback-core:核心代码块

logback-classic:实现了slf4j的api,加入该依赖可以实现log4j的api。

logback-access:访问模块与servlet容器集成提供通过http来访问日志的功能(也就是说不需要访问服务器,直接在网页上就可以访问日志文件,实现HTTP访问日志的功能)。

maven已经帮我们集成好了,只需引用:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.5.RELEASE</version>
    <relativePath/>
</parent>
<dependencies>
<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

点击进入spring-boot-starter-web,可以看到:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter</artifactId>
  <version>2.2.5.RELEASE</version>
  <scope>compile</scope>
</dependency>

点击进入spring-boot-starter包,可以看到:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-logging</artifactId>
  <version>2.2.5.RELEASE</version>
  <scope>compile</scope>
</dependency>

点击进入spring-boot-starter-logging,可以看到:

<dependency>
  <groupId>ch.qos.logback</groupId>
  <artifactId>logback-classic</artifactId>
  <version>1.2.3</version>
  <scope>compile</scope>
</dependency>

4.2 logback-spring.xml是在哪里加载的?

参考博客

springboot 源码阅读之 logback-spring.xml 是在哪里加载的?_logback-spring加载流程-CSDN博客 [springboot 源码阅读之 logback-spring.xml 是在哪里加载的?]

随便在logback-spring.xml写个错误:

<configuration scan="true" scanPeriod="10 seconds" debug="false">
    <a></a>
</configuration>

启动报错:

Exception in thread "main" java.lang.IllegalStateException: Logback configuration error detected: 
ERROR in ch.qos.logback.core.joran.spi.Interpreter@2:8 - no applicable action for [a], current ElementPath  is [[configuration][a]]
	at org.springframework.boot.logging.logback.LogbackLoggingSystem.loadConfiguration(LogbackLoggingSystem.java:169)
	at org.springframework.boot.logging.AbstractLoggingSystem.initializeWithConventions(AbstractLoggingSystem.java:80)
	at org.springframework.boot.logging.AbstractLoggingSystem.initialize(AbstractLoggingSystem.java:60)
	at org.springframework.boot.logging.logback.LogbackLoggingSystem.initialize(LogbackLoggingSystem.java:118)
	at org.springframework.boot.context.logging.LoggingApplicationListener.initializeSystem(LoggingApplicationListener.java:313)
	at org.springframework.boot.context.logging.LoggingApplicationListener.initialize(LoggingApplicationListener.java:288)
	at org.springframework.boot.context.logging.LoggingApplicationListener.onApplicationEnvironmentPreparedEvent(LoggingApplicationListener.java:246)
	at org.springframework.boot.context.logging.LoggingApplicationListener.onApplicationEvent(LoggingApplicationListener.java:223)
	at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172)
	at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165)
	at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139)
	at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:127)
	at org.springframework.boot.context.event.EventPublishingRunListener.environmentPrepared(EventPublishingRunListener.java:76)
	at org.springframework.boot.SpringApplicationRunListeners.environmentPrepared(SpringApplicationRunListeners.java:53)
	at org.springframework.boot.SpringApplication.prepareEnvironment(SpringApplication.java:345)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:308)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1215)
	at com.top.whysu.web.system.SystemWebApplication.main(SystemWebApplication.java:17)

进入LogbackLoggingSystem.java第169行,不断debug发现

1、org.springframework.boot.logging.logback.LogbackLoggingSystem定了4个默认配置文件:

@Override
protected String[] getStandardConfigLocations() {
   return new String[] { "logback-test.groovy", "logback-test.xml", "logback.groovy", "logback.xml" };
}

从这里可以看出来读取顺序是:logback-test.groovy,  logback-test.xml, logback.groovy, logback.xml

2、org.springframework.boot.logging.AbstractLoggingSystem这边先判断4个默认配置文件是否存在,如果不存的话,则加上-spring后缀再查询。

4.3 logback.xml和logback-spring.xml区别

参考博客

logback.xml和logback-spring.xml的区别_logback-spring和logback.xml-CSDN博客 [logback.xml和logback-spring.xml的区别]

https://www.cnblogs.com/huangdh/p/16778065.html [logback.xml和logback-spring.xml的区别]

【精选】附加:logback日志组件中,logback.xml和logback-spring.xml的区别;(本篇博客并没有得出明确的结论……)_logback.xml和配置文件的区别’-CSDN博客 [附加:logback日志组件中,logback.xml和logback-spring.xml的区别;]

我们使用SLF4J框架记录日志时,会用到 logback.xml 和 logback-spring.xml 两个不同的配置文件。

1、logback-spring.xml 只有在Spring应用程序运行的时候才生效,即带有@SpringBootApplication注解的类启动的时候才会生效。

     如果不是Spring应用程序,而是一个main方法或者一个JUnit的测试方法,要用 logback.xml 来配置。

2、存放的位置不同:

    logback-spring.xml存放的位置是在SpringApplication主类所在的项目的resources目录,也就是application.yml或者application.properties所在的目录

     logback.xml存放的位置是在你启动的那个类所在的项目的resources目录。

3、二者的加载顺序是:logback.xml—>application.properties—>logback-spring.xml。如果你在logback.xml定义了变量,而恰好这个变量又被写在了application.properties中,那么就有可能会获取不到,因为application.properties在logback.xml的后面执行。这也是为什么springboot推荐使用logback-spring.xml来替代logback.xml来配置。

五、logback-spring.xml格式详解

5.1 完整格式

被这张图吓住了?其实很简单的。

<root>和<logger>可以看做一棵树,root就是根节点,logger就是子节点。

<appender>就是用来定义这些节点的规则。

另外<property>和<contextName>就更简单了。

property就是定义变量,

contextName就是你这棵树的名称。

logback的日志配置文件格式如下所示:

<configuration scan="true" scanPeriod="60 seconds" debug="false">
    <property name="glmapper-name" value="glmapper-demo" />
    <contextName>${glmapper-name}</contextName>
    <appender>
        //xxxx
    </appender>
    <logger>
        //xxxx
    </logger>
    <root>
        //xxxx
    </root>
</configuration> 

5.2 configuration

根节点<configuration>,包含下面三个属性:

scan: 默认值为true。当此属性设置为true时,配置文件如果发生改变,将会被重新加载。

scanPeriod: 设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。如可以设置为scanPeriod="30 seconds"表示每30秒检测一次。

debug: 默认值false。当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。

<configuration scan="true" scanPeriod="30 seconds" debug="false">
</configuration>

5.2.1 定时器是如何生效的?

把debug设置为true

<configuration scan="true" scanPeriod="30 seconds" debug="true">

发现启动的时候打印了一句:

16:09:09,385 |-INFO in ch.qos.logback.classic.joran.action.ConfigurationAction - Setting ReconfigureOnChangeTask scanning period to 30 seconds

打开类 ch.qos.logback.classic.joran.action.ConfigurationAction;

发现的确有个定时器:

5.3 contextName

<contextName>标签作用是用来设置日志上下文的名称,可以用来区分不用应用程序。

5.4 property和springProperty

1、通过property元素可定义变量。它有name和value两个属性。

可以通过“${name}”来使用变量。

<property name="LOG_HOME" value="/home/smart-community-docking/scd/logs"/>

如果你是在windows上启动的话,然后你的项目在D盘,则日志会生成在D:\home\smart-community-docking\scd\logs目录下。

2、使用springProperty的话,值是可以从配置文件application.properties里面取值的。

例如现在有application.propertis定义了值:

spring.application.name=smart-community-docking

则可以通过springProperty获取到该值:

<configuration scan="true" scanPeriod="30 seconds" debug="false">
    <springProperty scope="context" name="APP_NAME" source="spring.application.name" defaultValue="smart-community-docking"/>
    <contextName>${APP_NAME}</contextName>
</configuration>

5.5 timestamp

用来获取时间戳字符串。这个属性很少使用。

有两个属性key和datePattern。

key: 标识此<timestamp> 的名字;

datePattern: 设置将当前时间(解析配置文件的时间)转换为字符串的模式,遵循 java.txt.SimpleDateFormat的格式。

<configuration scan="true" scanPeriod="30 seconds" debug="false">
   <timestamp key="currentDate" datePattern="yyyy-MM-dd" />
</configuration>

5.6.logger和root

5.6.1 说明

logger:

  • name属性:用来指定logger约束的包或者具体类。
  • level属性:用于设置日志打印级别。RACE,DEBUG,INFO,WARN,ERROR,ALll和OFF还有INHERITED(NULL)。如果没有设置则默认为(null)会继承上级的级别
  • addtivity属性:是否向上级logger传递打印信息,默认为true。如果设置为true,在输出日志时,你会发现控制台会输出两遍。所以一般设置additivity为false
<!-- 指定包-->
<logger name="com.top.scd.controller"/>
<!-- 指定类-->
<logger name="com.top.scd.controller.UserController" level="DEBUG" additivity="true"/>
  • <root>也是<logger>元素

  • 它的name就是ROOT。所以只能配置level属性

  • level属性的取值范围只能取 TRACE, DEBUG, INFO, WARN, ERROR, ALL, OFF。

  • level默认是DEBUG。

5.6.2.appender-ref

<logger>和<root> 标签下允许有0个或者多个 <appender-ref>

<appender-ref>只有一个ref 用于指定<appender>标签。

5.7 appender

appender用来定义日志的输出格式,过滤规则,以及日志文件如何生成。

有2个属性name 和class。

name当然是appender的名称啦。

class对应的是实现类。目前有如下三种常用的类:ConsoleAppender,FileAppender,RollingFileAppender。你会发现loback-core.jar包下真的有这3个类。

5.7.1 ConsoleAppender

ConsoleAppender:日志输出到控制台,类名ch.qos.logback.core.FileAppender。

属性名

类型

备注

encoder

ch.qos.logback.core.encoder.Encoder

对日志进行格式化。

使用<pattern>指定格式。具体格式见后面章节。

<charset>指定字符编码。

target

String

有效值为System.out或者System.err,默认为System.out。一般不写。

示例:

注意:禁止向除了ConsoleAppender之外的appender配置彩色日志。

<?xml version="1.0" encoding="UTF-8" ?>
<configuration scan="true" scanPeriod="1 seconds" debug="false">
<!--${CONSOLE_LOG_PATTERN}在这个文件里面引用的-->
    <include resource="org/springframework/boot/logging/logback/defaults.xml" />
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <!--此日志appender是为开发使用,只配置最底级别,控制台输出的日志级别是大于或等于此级别的日志信息-->
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>debug</level>
        </filter>
        <encoder>
            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
            <!-- 设置字符集 -->
            <charset>UTF-8</charset>
        </encoder>
    </appender>
    <root level="INFO">
        <appender-ref ref="CONSOLE" />
    </root>
</configuration>

5.7.2 FileAppender

FileAppender:日志输出到文件,类名ch.qos.logback.core.FileAppender。

属性名

类型

备注

file

String

被写入的文件名,可以是相对目录,也可以是绝对目录,如果上级目录不存在会自动创建,没有默认值

append

boolean

如果是 true,日志被追加到文件结尾,如果是 false,清空现存文件,默认是true。

encoder

ch.qos.logback.core.encoder.Encoder

对日志进行格式化。

使用<pattern>指定格式。具体格式见后面章节。

<charset>指定字符编码。

prudent

boolean

如果是 true,日志会被安全的写入文件(即使其他的FileAppender也在向此文件做写入操作),效率低,默认是 false。

示例:

<?xml version="1.0" encoding="UTF-8" ?>
<configuration scan="true" scanPeriod="1 seconds" debug="false">
    <!--定义日志路径,如果是windows启动并且代码在D盘, 则日志会生成于D:\home\smart-community-docking\scd\logs下-->
    <property name="LOG_HOME" value="/home/smart-community-docking/scd/logs"/>
    <appender name="file" class="ch.qos.logback.core.FileAppender">
        <!--日志格式配置-->
        <encoder>
            <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
        </encoder>
        <!--日志输出路径-->
        <file>${LOG_HOME}/logback.log</file>
        <!--默认日志被追加到文件结尾-->
        <append>true</append>
    </appender>
    <root level="INFO">
        <appender-ref ref="file"/>
    </root>
</configuration>

5.7.3 RollingFileAppender

RollingFileAppender:滚动记录文件,FileAppender的子类。

先将日志文件指定到文件,当符合某个条件时,将日志记录到其他文件

类名:ch.qos.logback.core.rolling.RollingFileAppender。

属性名

类型

备注

file

String

被写入的文件名,可以是相对目录,也可以是绝对目录,如果上级目录不存在会自动创建,没有默认值

append

boolean

如果是 true,日志被追加到文件结尾,如果是 false,清空现存文件,默认是true。

rollingPolicy

ch.qos.logback.core.rolling.RollingPolicy

当发生滚动时,决定 RollingFileAppender 的行为,涉及文件移动和重命名

属性class定义具体的滚动策略类

triggeringPolicy

ch.qos.logback.core.rolling.TriggeringPolicy

告知 RollingFileAppender 何时激活滚动

这个要配合具体的rollingPolicy使用,不同的rollingPolicy会有所不同。目前有用到的就只有SizeBasedTriggeringPolicy。

encoder

ch.qos.logback.core.encoder.Encoder

对日志进行格式化。

使用<pattern>指定格式。具体格式见后面章节。

<charset>指定字符编码。

prudent

boolean

当为true时,不支持FixedWindowRollingPolicy。支持TimeBasedRollingPolicy,但是有两个限制,1不支持也不允许文件压缩,2不能设置file属性,必须留空。

这里有如下常用的4种滚动策略:

TimeBasedRollingPolicy,SizeBasedTriggeringPolicy,FixedWindowRollingPolicy,SizeAndTimeBasedRollingPolicy

5.7.3.1 TimeBasedRollingPolicy

时间滚动策略  ch.qos.logback.core.rolling.TimeBasedRollingPolicy

可以基于时间滚动按时间生成日志

属性名

类型

备注

fileNamePattern

String

定义了归档日志文件的名字。

1、它的值由文件名%d的占位转换符组成,如果没有指定时间和日期格式,默认为yyyy-MM-dd。(由java.text.SimpleDateFormat进行格式化)。

2、轮转周期通过fileNamePattern推断出来的。可以指定多个 %d,但是只能有一个是主要的,用于推断轮转周期,其它的 %d 占位符必须通过 'aux' 标记为辅助的。

例如:/var/log/%d{yyyy/MM,aux}/myapplication.%d{yyyy-MM-dd}.log

3、也可以指定时区:%d{yyyy-MM-dd_HH-mm, UTC},如果指定的时区timezone不能被识别或者拼写错误的话,将会根据TimeZone.getTimeZone(String)方法指定为 GMT。

举例:

/foo.%d  默认%d格式是yyyy-MM-dd,按天滚动。

/%d{yyyy/MM}/foo.log 按月滚动

/%d{yyyy-MM-dd_HH}.log  按小时滚动

/%d{yyyy-MM-dd_HH-mm, UTC}.log 按分钟滚动

/%d{yyyy-MM, aux}/%d.log  按天滚动

maxHistory

int

表示日志文件保存的最大数量。

例如:如果根据fileNamePattern判断出来是按天滚动,该值设为30,则日志文件最多保存30天。

totalSizeCap

int

来控制所有归档文件总的大小。当达到这个大小后,旧的归档文件将会被异步的删除。

使用这个属性时还需要设置 maxHistory 属性。而且,maxHistory 将会被作为第一条件,该属性作为第二条件。

cleanHistoryOnStart

boolean

如果设置为 true,那么在 appender 启动的时候,归档文件将会被删除。默认的值为false。

下面是一些例子:

文件命名格式

滚动计划

备注

/wombat/foo.%d

按日滚动

%d默认是yyyy-MM-dd格式。

昨天:/wombat/foo.2023-04-22

今天:/wombat/foo.2023-04-23

如果RollingFileAppender设置了<file>属性,则当前的日志会被打印到file配置的文件中。例如如果<file>值为/wombat/aaaa.txt, 则:

昨天:/wombat/foo.2023-04-22

今天:/wombat/aaaa.txt

TimeBasedRollingPolicy支持归档日志文件自动压缩,如果fileNamePattern的值以.gz或者.zip结尾则可利用该特性。

文件命名格式

滚动计划

备注

/wombat/foo.%d.gz

按日滚动,归档日志文件会自动GZIP压缩

文件格式如下:

昨天:/wombat/foo.2019-05-05.gz

当天:/wombat/foo.2019-05-06

示例:

<?xml version="1.0" encoding="UTF-8" ?>
<configuration scan="true" scanPeriod="1 seconds" debug="false">
    <!--定义日志路径,使用${LOG_HOME访问}, 如果是windows启动并且代码在D盘, 则日志会生成于D:\home\smart-community-docking\scd\logs下-->
    <property name="LOG_HOME" value="/home/smart-community-docking/scd/logs"/>
    <!-- 滚动文件的方式生成日志日志文件,文件的存储位置通过file标签指定 -->
    <!-- 通过encoder指定日志的生成格式,每个appender的日志格式都可以自定义,不用相同 -->
    <appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_HOME}/test.log</file>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %5level - %msg%n</pattern>
        </encoder>
        <!--
        通过rollingPolicy设置日志滚动的策略,这是使用按照时间滚动
        fileNamePattern属性设置滚动生成文件的格式,这里设置的精确到天,也就是按照天滚动,如果时间设置精确到秒,就按秒来滚动
        maxHistory属性设定最大的文件数,比如按天滚动,这里设置了30天,在第31天日志生成的时候,第一天的日志就会被删掉
        -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--设置按天来滚动,前一天日志打印到23点59分,然后就一直没有请求日志,直到次日的1点才有新的日志进入。
                    在0点到1点这个时间段,日志文件是不会滚动生成新的日志文件。因为滚动的动作是需要日志写入动作来触发。-->
            <fileNamePattern>${LOG_HOME}/test-%d{yyyy-MM-dd}.log</fileNamePattern>
            <maxHistory>30</maxHistory>
            <totalSizeCap>3GB</totalSizeCap>
            <cleanHistoryOnStart>false</cleanHistoryOnStart>
        </rollingPolicy>
    </appender>
    <root level="INFO">
        <appender-ref ref="file"/>
    </root>
</configuration>

5.7.3.2 SizeBasedTriggeringPolicy

观察当前活动文件的大小,如果已经大于了指定的值,它会给 RollingFileAppender 发一个信号触发对当前活动文件的轮转

ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy

属性名

类型

备注

maxFileSize

int

可以为字节,千字节,兆字节,千兆字节,通过在数值后面指定一个后缀 KB,MB 或者 GB。例如,5000000,5000KB,5MB 以及 2GB 都是有效的,前三个是一样的。(默认值是 10 MB)

官方的示例这个要放在<triggeringPolicy>触发策略使用,并且和FixedWindowRollingPolicy一起使用,所以这里先不说。

5.7.3.3 FixedWindowRollingPolicy

基于窗口大小的滚动策略。ch.qos.logback.core.rolling.FixedWindowRollingPolicy

这个听起来可能有点难理解,其实说白了就是将归档日志文件到最大了就写到下一个文件里,而窗口大小就是最多允许多少份日志文件。

属性名

类型

备注

fileNamePattern

String

必须包含“%i”。

假设最小值maxIndex和最大值maxIndex分别为1和2,命名模式为 mylog%i.log,会产生归档文件mylog1.log和mylog2.log。还可以指定文件压缩选项,例如,mylog%i.log.gz 或者log%i.log.zip

minIndex

int

窗口下限。下限一般都是1啦

maxIndex

int

窗口上限。一般我们用上限就可以了。

示例:(官方示例FixedWindowRollingPolic和SizeBasedTriggeringPolicy一起使用:)

<?xml version="1.0" encoding="UTF-8" ?>
<configuration scan="true" scanPeriod="1 seconds" debug="false">
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>test.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
            <fileNamePattern>tests.%i.log.zip</fileNamePattern>
            <minIndex>1</minIndex>
            <maxIndex>3</maxIndex>
        </rollingPolicy>
        <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
            <maxFileSize>5MB</maxFileSize>
        </triggeringPolicy>
        <encoder>
            <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
        </encoder>
    </appender>
    <root level="DEBUG">
        <appender-ref ref="FILE" />
    </root>
</configuration>

5.7.3.4 SizeAndTimeBasedRollingPolicy

前面介绍的TimeBasedRollingPolicy是根据时间生成日志,SizeBasedTriggeringPolicy是根据大小来生成日志,但很尴尬的一点是这2个类冲突,没法同时使用。

故而官方提供了基于时间和文件大小的滚动策略:

ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy

属性名

类型

备注

fileNamePattern

String

必选参数。

具体介绍可以看TimeBasedRollingPolicy的介绍。

区别点是必须包含 %i

maxFileSize

int

每个文件最大多少。可以指定KB,MB 或者 GB,默认值是 10 MB。

maxHistory

int

表示日志文件保存的最大数量。

例如:如果根据fileNamePattern判断出来是按天滚动,该值设为30,则日志文件最多保存30天。

totalSizeCap

int

可选参数,表示所有归档日志文件的的文件总大小。

假如设置每个日志文件到10mb的时候开始切分,最多保留30天,但最大到20GB,哪怕没到30天但容量达到20G了也要删除多余的日志。

cleanHistoryOnStart

boolean

可选参数,表示appender应用程序启动时是否应进行日志存档清理,默认为false。

示例:(每个文件最多5MB,保存60天的历史记录,但最多20GB。)

<?xml version="1.0" encoding="UTF-8" ?>
<configuration scan="true" scanPeriod="1 seconds" debug="false">
    <property name="LOG_HOME" value="/home/smart-community-docking/scd/logs"/>
    <appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <!-- rollover daily -->
            <fileNamePattern>${LOG_HOME}/mylog-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <!-- 每个文件最多5MB,保存60天的历史记录,但最多20GB -->
            <maxFileSize>5MB</maxFileSize>
            <maxHistory>60</maxHistory>
            <totalSizeCap>20GB</totalSizeCap>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX} - %msg%n</pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>
    <root level="DEBUG">
        <appender-ref ref="file" />
    </root>
</configuration>

5.8 encoder说明

encoder表示对参数进行格式化。

<encoder>是0.9.19版本之后引进的,以前的版本使用<layout>,logback极力推荐的是使用<encoder>而不是<layout>

<encoder>节点负责两件事情:

1、把日志信息转换为字节数组

2、把字节数组写到输出流

目前使用的是ch.qos.logback.classic.encoder.PatternLayoutEncoder (是唯一有用的且默认的encoder ),它有一个<pattern>节点,用来设置日志的输入格式。

使用“%”加“转换符”方式

如果要输出“%”,则必须用“\”对“\%”进行转义。

5.8.1 %n

转换符

作用

是否避免使用

n

日志换行。

根据使用平台输出\n或\r\n

说明:

看到最后一列的标题是"是否避免使用",这是因为这些信息是无法直接拿到的(比如请求行号、调用方法名),logback必须通过一些特殊手段去获取这些数据(比如在日志打印出产生一个堆栈信息),这种操作会比较影响效率,因此除非必要,否则不建议打印这些数据。

5.8.2 %c %lo %logger

转换符

作用

是否避免使用

c{length}

lo{length } 

logger{length}

输出日志的logger名,有一个形参,功能是缩短logger名,设置为0表示只输入logger最右边点符号之后的字符串。

{length} 限制了总输出长度,如果输出长度不够,尽可能显示类名压缩包名

示例:

例如我在com.top.scd.web.system.controller.module.xmga.XmgaController打印了一行日志

package com.toptop.scd.web.system.controller.module.xmga;
@RestController
@RequestMapping("/dockset/sc_xmga")
@Slf4j
public class XmgaController {
    @GetMapping("/time")
    @ResponseBody
    public String time() {
        log.warn("aaaaa");
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String timeStr = "厦门公安-当前时间:" + format.format(new Date());
        return timeStr;
    }
}

logback-spring.xml中配置:

<?xml version="1.0" encoding="UTF-8" ?>
<configuration scan="true" scanPeriod="1 seconds" debug="true">
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%c-%lo-%logger</pattern>
        </encoder>
    </appender>
    <root level="info">
        <appender-ref ref="STDOUT"/>
    </root>
</configuration>

打印结果:

com.toptop.scd.web.system.controller.module.xmga.XmgaController-com.toptop.scd.web.system.controller.module.xmga.XmgaController-com.toptop.scd.web.system.controller.module.xmga.XmgaController

可以注意到点号也是有被算进来的

pattern格式

打印结果

打印长度

%logger

com.toptop.scd.web.system.controller.module.xmga.XmgaController

63

%logger{0}

XmgaController

14

%logger{1}

c.l.s.w.s.c.m.x.XmgaController

30

%logger{14}

c.l.s.w.s.c.m.x.XmgaController

30

%logger{30}

c.l.s.w.s.c.m.x.XmgaController

30

%logger{32}

c.l.s.w.s.c.m.x.XmgaController

30

%logger{33}

c.l.s.w.s.c.m.xmga.XmgaController

33

%logger{38}

c.l.s.w.s.c.module.xmga.XmgaController

38

%logger{47}

c.l.s.w.s.controller.module.xmga.XmgaController

47

%logger{52}

c.l.s.w.system.controller.module.xmga.XmgaController

52

%logger{54}

c.l.s.web.system.controller.module.xmga.XmgaController

54

%logger{56}

c.l.scd.web.system.controller.module.xmga.XmgaController

56

%logger{61}

c.toptop.scd.web.system.controller.module.xmga.XmgaController

61

%logger{63}

com.toptop.scd.web.system.controller.module.xmga.XmgaController

63

%logger{100}

com.toptop.scd.web.system.controller.module.xmga.XmgaController

63

 5.8.3 %C %class

转换符

作用

是否避免使用

C{length}

class{length } 

输出日志调用所在类。

length与%logger(简写%c或%lo)的用法相同。

尽量避免使用,除非执行速度不造成任何问题。

不被推荐使用

5.8.4.%d %date

转换符

作用

是否避免使用

d{pattern}

date{pattern} 

输出时间格式,模式语法同

java.text.SimpleDateFormat

可以指定日期格式精确到毫秒:%d{yyyy-MM-dd HH:mm:ss.SSS}

5.8.5.%caller

转换符

作用

是否避免使用

caller{depth}

输出日志的调用者的位置信息,整数选项表示输出信息深度。

<pattern>%caller</pattern>

打印:

Caller+0  at com.toptop.scd.web.system.controller.module.xmga.XmgaController.time(XmgaController.java:49)

Caller+1  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

Caller+2  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)

Caller+3  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

Caller+4  at java.lang.reflect.Method.invoke(Method.java:498)

<pattern>%caller{1}</pattern>

打印:

Caller+0  at com.toptop.scd.web.system.controller.module.xmga.XmgaController.time(XmgaController.java:49)

5.8.6.%L %line

转换符

作用

是否避免使用

L

line

输出执行日志请求的行号。

尽量避免使用,除非执行速度不造成任何问题。

<pattern>%L</pattern>

5.8.7 %m %msg %message

转换符

作用

是否避免使用

m

msg

message

输出应用程序提供的信息

log.warn("aaaaa");
<pattern>%msg</pattern>

打印:

aaaaa

5.8.8.%M %method

转换符

作用

是否避免使用

M

method

数值执行日志请求的方法名。

尽量避免使用,除非执行速度不造成任何问题。

public String time() {
    log.warn("aaaaa");
}
<pattern>%method</pattern>

打印:

time

5.8.9 %p %le %level

转换符

作用

是否避免使用

p

le

level

输出日志级别

log.warn("aaaaa");
<pattern>%level</pattern>

打印:

WARN

5.8.10 %r %relative

转换符

作用

是否避免使用

r

relative

输出从程序启动到创建日志记录的时间,单位是毫秒

<pattern>%r%n</pattern>

多执行几次,打印:

30675581

30676817

30677965

30679104

30680009

5.8.11.%t %thread

转换符

作用

是否避免使用

t

thread

输出产生日志的线程名

<pattern>%thread</pattern>

打印:

http-nio-8998-exec-1

5.8.12 %replace

转换符

作用

是否避免使用

replace(p){r,t}

p为日志内容, r是正则表达式, 将p中符合r的内容替换为t

log.warn("aaaaa");
<pattern>%replace(%msg){"a", "b"}</pattern>

打印结果:

bbbbb

6.8.13 宽度设置

%20logger:当字符数少于20个字符时,则左侧留空白;
%-20logger:当字符数少于20个字符时,则右侧留空白;
%.30logger:当字符数据大于30个时,则截断;

还可以合起来使用,例如:%-20.20logger     %20.20class

{length}可指定长度,如%logger{36}

log.warn("123456789");
<pattern>%80logger%n%-80logger%n%.20logger</pattern>

5.8.14.显示设置

%highligth(日志内容):突出显示

%green(日志内容):字体显示为指定颜色

%clr(日志内容){颜色} 设置颜色

5.8.14.1.%clr和defaults.xml
<pattern>%clr(%msg){faint}%n</pattern>

这里启动会报错说找不到%clr对应的转换器。

Exception in thread "main" java.lang.IllegalStateException: Logback configuration error detected:

ERROR in ch.qos.logback.core.pattern.parser.Compiler@79145d5a - There is no conversion class registered for composite conversion word [clr]

ERROR in ch.qos.logback.core.pattern.parser.Compiler@79145d5a - Failed to create converter for [%clr] keyword

解决方法一:

引入springboot的默认日志配置:

<include resource="org/springframework/boot/logging/logback/defaults.xml" />

<?xml version="1.0" encoding="UTF-8" ?>
<configuration scan="true" scanPeriod="1 seconds" debug="false">
    <include resource="org/springframework/boot/logging/logback/defaults.xml" />
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%clr(%msg){faint}%n</pattern>
        </encoder>
    </appender>
    <root level="INFO">
        <appender-ref ref="STDOUT" />
    </root>
</configuration>

解决方法二:或者手动引入需要的配置:

<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
<?xml version="1.0" encoding="UTF-8" ?>
<configuration scan="true" scanPeriod="1 seconds" debug="false">
    <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%clr(%msg){faint}%n</pattern>
        </encoder>
    </appender>
    <root level="INFO">
        <appender-ref ref="STDOUT" />
    </root>
</configuration>

这个文件是怎么冒出来的呢?查看前面的那个default.xml文件就可以看到啦!

5.8.14.2.支持的颜色

%black 黑色

%red 红色

%green 绿色

%yellow 黄色

%blue 蓝色

%magenta 洋红色

%cyan 青色

%white 白色

%gray 灰色

%faint 灰白色

以下为对应加粗的颜色代码

%boldRed

%boldGreen

%boldYellow

%boldBlue

%boldMagenta

%boldCyan

%boldWhite

 %highlight 高亮色

 5.9 filter过滤规则

5.9.1 LevelFilter

<appender>标签里面可以指定<filter>过滤器,

例如ch.qos.logback.classic.filter.LevelFilter,表示根据日志级别进行过滤。

<level>:设置过滤级别

<onMatch>:用于配置符合过滤条件的操作

<onMismatch>:用于配置不符合过滤条件的操作

举例:

<?xml version="1.0" encoding="UTF-8" ?>
<configuration scan="true" scanPeriod="1 seconds" debug="true">
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>INFO</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <encoder>
            <pattern>
                %-4relative [%thread] %-5level %logger{30} - %msg%n
            </pattern>
        </encoder>
    </appender>
    <root level="DEBUG">
        <appender-ref ref="CONSOLE"/>
    </root>
</configuration>
public String time() {
    log.trace("====trace====");
    log.debug("====debug====");
    log.info("====info====");
    log.warn("====warn====");
    log.error("====error====");
}

打印结果:

186556 [http-nio-8998-exec-6] INFO  c.l.s.w.s.c.m.x.XmgaController - ====info====

5.9.2 ThresholdFilter

临界值过滤器,过滤掉低于指定临界值的日志。

对应的类是ch.qos.logback.classic.filter.ThresholdFilter。

当日志级别等于或高于临界值时,过滤器返回NEUTRAL;当日志级别低于临界值时,日志会被拒绝。

<level>:设置过滤级别

例如:过滤掉所有低于INFO级别的日志:

<?xml version="1.0" encoding="UTF-8" ?>
<configuration scan="true" scanPeriod="1 seconds" debug="true">
    <appender name="CONSOLE"
              class="ch.qos.logback.core.ConsoleAppender">
        <!-- 过滤掉低于INFO级别的日志-->
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>INFO</level>
        </filter>
        <encoder>
            <pattern>
                %-4relative [%thread] %-5level %logger{30} - %msg%n
            </pattern>
        </encoder>
    </appender>
    <root level="DEBUG">
        <appender-ref ref="CONSOLE"/>
    </root>
</configuration>
public String time() {
    log.trace("====trace====");
    log.debug("====debug====");
    log.info("====info====");
    log.warn("====warn====");
    log.error("====error====");
}

打印结果:

393494 [http-nio-8998-exec-9] INFO  c.l.s.w.s.c.m.x.XmgaController - ====info====

393494 [http-nio-8998-exec-9] WARN  c.l.s.w.s.c.m.x.XmgaController - ====warn====

393494 [http-nio-8998-exec-9] ERROR c.l.s.w.s.c.m.x.XmgaController - ====error====

5.9.3 EvaluatorFilter

求值过滤器,评估、鉴别日志是否符合指定条件。

对应类是ch.qos.logback.core.filter.EvaluatorFilter。

需要额外的两个JAR包,commons-compiler.jar和janino.jar。

有以下子节点:

<evaluator>鉴别器,常用的鉴别器是ch.qos.logback.classic.boolex.JaninoEventEvaluator

,也是默认的鉴别器。它以任意的java布尔值表达式作为求值条件,求值条件在配置文件解释过成功被动态编译,布尔值表达式返回true就表示符合过滤条件。

<evaluator>有个子标签<expression>,用于配置求值条件

<onMatch>:用于配置符合过滤条件的操作

<onMismatch>:用于配置不符合过滤条件的操作

求值表达式作用于当前日志,logback向求值表达式暴露日志的各种字段:

Name

Type

Description

event

LoggingEvent

与记录请求相关联的原始记录事件,下面所有变量都来自event,例如,event.getMessage()返回下面"message"相同的字符串

message

String

日志的原始消息,例如,设有logger mylogger,"name"的值是"AUB",对于 mylogger.info("Hello {}",name); "Hello {}"就是原始消息。

formatedMessage

String

日志被各式话的消息,例如,设有logger mylogger,"name"的值是"AUB",对于 mylogger.info("Hello {}",name); "Hello Aub"就是格式化后的消息。

logger

String

logger 名。

loggerContext

LoggerContextVO

日志所属的logger上下文。

level

int

级别对应的整数值,所以 level > INFO 是正确的表达式。

timeStamp

long

创建日志的时间戳。

marker

Marker

与日志请求相关联的Marker对象,注意“Marker”有可能为null,所以你要确保它不能是null。

mdc

Map

包含创建日志期间的MDC所有值得map。访问方法是: mdc.get("myKey") 。mdc.get()返回的是Object不是String,要想调用String的方法就要强转,例如,

((String) mdc.get("k")).contains("val") .MDC可能为null,调用时注意。

throwable

java.lang.Throwable

如果没有异常与日志关联"throwable" 变量为 null. 不幸的是, "throwable" 不能被序列化。在远程系统上永远为null,对于与位置无关的表达式请使用下面的变量throwableProxy

throwableProxy

IThrowableProxy

与日志事件关联的异常代理。如果没有异常与日志事件关联,则变量"throwableProxy" 为 null. 当异常被关联到日志事件时,"throwableProxy" 在远程系统上不会为null

EvaluatorFilter支持使用 java 代码来作为过滤标准。但需要导入额外的包:

<!-- janino -->
<dependency>
    <groupId>org.codehaus.janino</groupId>
    <artifactId>janino</artifactId>
    <version>3.1.0</version>
</dependency>
<?xml version="1.0" encoding="UTF-8" ?>
<configuration scan="true" scanPeriod="1 seconds" debug="true">
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <filter class="ch.qos.logback.core.filter.EvaluatorFilter">
            <evaluator> <!-- 默认为 ch.qos.logback.classic.boolex.JaninoEventEvaluator -->
                <expression>return message.contains("info");</expression>
            </evaluator>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <encoder>
            <pattern>
                %-4relative [%thread] %-5level %logger - %msg%n
            </pattern>
        </encoder>
    </appender>
    <root level="INFO">
        <appender-ref ref="STDOUT" />
    </root>
</configuration>
public String time() {
    log.trace("====trace====");
    log.debug("====debug====");
    log.info("====info====");
    log.warn("====warn====");
    log.error("====error====");
}

打印:

21809 [http-nio-8998-exec-1] INFO  com.toptop.scd.web.system.controller.module.xmga.XmgaController - ====info====

5.9.4 自定义过滤器

自定义类:

package com.toptop.scd.base.common.filter;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.filter.Filter;
import ch.qos.logback.core.spi.FilterReply;

public class SampleFilter extends Filter<ILoggingEvent> {

    @Override
    public FilterReply decide(ILoggingEvent event) {
        if (event.getMessage().contains("info")) {
            return FilterReply.ACCEPT;
        } else {
            return FilterReply.DENY;
        }
    }
}

日志配置文件指定过滤器

<?xml version="1.0" encoding="UTF-8" ?>
<configuration scan="true" scanPeriod="1 seconds" debug="true">
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <filter class="com.toptop.scd.base.common.filter.SampleFilter" />
        <encoder>
            <pattern>%-4relative [%thread] %-5level %logger - %msg%n</pattern>
        </encoder>
    </appender>
    <root level="INFO">
        <appender-ref ref="STDOUT" />
    </root>
</configuration>

打印结果:

186556 [http-nio-8998-exec-6] INFO  c.l.s.w.s.c.m.x.XmgaController - ====info====

六、优化异常堆栈stack日志打印

6.1 未优化前

try {
    int i = 1/0;
} catch (Exception e) {
    log.error("发生异常", e);
}

打印结果:(打印了很多不需要的堆栈信息)

打印结果:
2023-04-25 10:58:03.757 ERROR 36556 --- [nio-8998-exec-1] c.l.s.w.s.c.module.xmga.XmgaController   : 发生异常

java.lang.ArithmeticException: / by zero
	at com.toptop.scd.web.system.controller.module.xmga.XmgaController.time(XmgaController.java:50)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190)
	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138)
	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:879)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:793)
	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040)
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943)
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
	at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:634)
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at com.alibaba.druid.support.http.WebStatFilter.doFilter(WebStatFilter.java:123)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:367)
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1639)
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
	at java.lang.Thread.run(Thread.java:748)

6.2.优化后

1、引入logstash-logback-encoder

<dependency>
    <groupId>net.logstash.logback</groupId>
    <artifactId>logstash-logback-encoder</artifactId>
    <version>7.1.1</version>
</dependency>

注意jdk版本依赖:

2、引入

<conversionRule conversionWord="stack" converterClass="net.logstash.logback.stacktrace.ShortenedThrowableConverter"/>

3、通过%stack{}进行配置

stack{100,16,2048,rootFirst,regex1,regex2,evaluatorName}

参数解释:

建议查看源码 net.logstash.logback.stacktrace.ShortenedThrowableConverter#parseOptions()

  •  示例:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <conversionRule conversionWord="stack"
                    converterClass="net.logstash.logback.stacktrace.ShortenedThrowableConverter"/>
    <property name="STE_EXCLUSIONS" value="\$\$FastClassByCGLIB\$\$,\$\$EnhancerBySpringCGLIB\$\$,^sun\.reflect\..*\.invoke,^com\.sun\.
    ,^sun\.net\.,^net\.sf\.cglib\.proxy\.MethodProxy\.invoke,^org\.springframework\.cglib\.,^org\.springframework\.transaction\.
    ,^org\.springframework\.validation\.,^org\.springframework\.app\.,^org\.springframework\.aop\.,^java\.lang\.reflect\.Method\.invoke
    ,^org\.springframework\.ws\..*\.invoke,^org\.springframework\.ws\.transport\.,^org\.springframework\.ws\.soap\.saaj\.SaajSoapMessage\.
    ,^org\.springframework\.ws\.client\.core\.WebServiceTemplate\.,^org\.springframework\.web\.filter\.,^org\.apache\.tomcat\.
    ,^org\.apache\.catalina\.,^org\.apache\.coyote\.,^java\.util\.concurrent\.ThreadPoolExecutor\.runWorker,^java\.lang\.Thread\.run$"/>
    <!--
        stack{100,16,2048,rootFirst,regex1,regex2,evaluatorName}
        参数解释: 建议查看源码 net.logstash.logback.stacktrace.ShortenedThrowableConverter#parseOptions() 方法
            第1参数: maxDepthPerThrowable:值可以是 full或者short或者int值,表示每个异常最多打印多少个 stackTraceElements 元素
            第2参数: shortenedClassNameLength:值可以是 full或者short或者int值,将尝试将类名长度缩短到小于此值
                     com.huan.springboot.service.ExceptionService 可能会变成 c.h.s.s.ExceptionService
            第3参数: maxLength:值可以是 full或者short或者int值,指的输出到日志中整个堆栈最大能存在多少个字符。
            后面这些参数没有固定顺序
            第4参数: rootFirst: 可选参数,如果使用该参数,值就是 rootFirst ,表示应首先打印堆栈的根本原因
            第5参数: inlineHash: 可选参数,如果使用该参数,值就是 inlineHash, 指示应该计算和内联十六进制错误哈希
            如果参数都不是上方的类型,那么可能是 evaluator 或者 exclude 类型,这2个都是判断这个 stackTraceElement 是否应该被打印,这2个参数没有顺序关系
                evaluator: 值的是需要实现 EventEvaluator<ILoggingEvent> 的类
                exclude: 指的是需要排除的正则表达式, 如果存在.需要转义成 \.
    -->
    <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %msg%n%stack{100,full,2048,rootFirst,inlineHash,${STE_EXCLUSIONS},}</pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>
    <root level="INFO">
        <appender-ref ref="stdout"/>
    </root>
</configuration>

优化后打印的日志,部分堆栈信息被隐藏:

<#6242a0ba> java.lang.ArithmeticException: / by zero
	at com.toptop.scd.web.system.controller.module.xmga.XmgaController.time(XmgaController.java:50)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(NativeMethodAccessorImpl.java)
	... 3 frames excluded
	at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190)
	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138)
	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:879)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:793)
	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040)
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943)
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
	at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:634)
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
	... 5 frames excluded
	at com.alibaba.druid.support.http.WebStatFilter.doFilter(WebStatFilter.java:123)
	... 27 frames excluded
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	... 2 frames excluded

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值