RT
最近在学习总结logback的一些知识,不知道你们是何感觉,反正我看了很多文章之后,印象总是不深刻。所谓“好记性不如烂笔头”,所以就干脆写个blog,顺带学习一下。另外你们懂的,就像LOL王者大佬永远无法预判青铜仔的操作一样,只有“菜鸡才知道菜鸡”的想法、痛点。
阅读对象
- logback.xml配置小白
温馨提示
- 本文总共有5912个字,预计耗时15分钟可以看完
- 所有标题带“*”的皆为重点知识,一定要理解
- 本文只是简单的介绍每个标签的意思,但我可以保证,通过本文你基本能够在生产项目上使用、配置logback日志框架了。
*读前须知
什么是logback?
答:logback是一个非常好用,且流行的日志框架。 面对当下java环境日志产品的内卷(4个Log产品,3个Log接口,非常驳杂),如果你对”应该使用什么日志接口“没什么概念,推荐直接使用 logback+slf4j的日志组合!
*logback日志的核心是什么?
答:关于Logback日志的核心,我们只需要了解3个单词:Logger(记录器),Appenders(附加器),Layout(布局)
- Logger:记录器,是我们在编写logback.xml文件的时候,configuration下的基本标签。它的作用,等价于如下代码
//import ch.qos.logback.classic.Logger; 这是org.slf4j.Logger的一个实现示例
//import org.slf4j.LoggerFactory;
Logger logger = (Loggger)LoggerFactory.getLogger(TestRestFulController.class);
准确来说,当我们在logback-spring.xml下这样写的时候,等价于上面这条语句:
<configuration>
<logger name="com.this.is.a.example.name"/>
</configuration>
- Appender:有道翻译一下,他的意思是“附加器”,“输出目的地”,“输出源”。到了这里其实就不难理解了,顾名思义,它表示的是:日志输出目的地、输出源、输出媒介。那我们日志输出的目的地有什么?是什么?无非就是“控制台”、“某个指定的日志文件”呗。
- Layout:暂时不介绍。其实核心是上面两个元素
*logback.xml文件标签解析
下面是一个简单的logback.xml文件解析,请仔细阅读,关键内容就在里面了,并且已经写了注释
<?xml version="1.0" encoding="UTF-8" ?>
<!--
configuration是logback的根标签
scan="true" 表示配置文件发生了改变会自动加载
scanPeriod="60 seconds" 检测配置文件修改的时间间隔,默认的单位是毫秒,这里我们设置的表示每分钟检测
debug="false" debug如果设置为true,表示我们会打印出来logback自身实时的运行信息,很显然我们是不需要的,人家成熟的软件比咱小辣鸡写的好多了
-->
<configuration
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNameSpaceSchemaLocation="logback.xsd"
scan="true"
scanPeriod="60 seconds"
debug="false">
<!--
定义一些常量,方便后续使用
1、日志默认打印格式
name:DEFAULT_LOG_PATTERN。 表示定义的常量名叫DEFAULT_LOG_PATTERN,后面可以通过el表达式使用这个常量。
value解析:
%d{yyyy-MM-dd HH:mm:ss.SSS}:定义一条日志的展示时间
%-5level:表示空出5个空格
%thread:表示执行日志的线程
%logger{40}:显示我们在哪个类(全限定名)里面记录日志,后面的{40}表示这个类名展示的最大长度是40
%msg:表示我们的日志信息
%n:表示换行
-->
<property name="DEFAULT_LOG_PATTERN"
value="%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level --- [%thread] %logger{40} : %msg%n"/>
<!-- 定义日志的输出路径-->
<property name="FILE_PATH" value="F:\\log"/>
<!--
2、定义日志输出的媒介
我们知道,日志的输出是有级别的,优先级从低到高分别是:trace < debug < info < warn < error
-->
<!--
2.1、定义console的日志打印,即控制台。注意,这里的媒介是【控制台】,我们再联系一下最上面提到的Appender的概念
name="consoleLog":定义一个名字叫做"consoleLog"的日志附加器
class="ch.qos.logback.core.ConsoleAppender":表示日志附加器采用的策略是ConsoleAppender。即打印到控制台
-->
<appender name="consoleLog" class="ch.qos.logback.core.ConsoleAppender">
<!-- 默认情况下,每个日志事件都会立即刷新到基础输出流。 这种默认方法更安全,因为如果应用程序在没有正确关闭appender的情况下退出,则日志事件不会丢失。
但是,为了显着增加日志记录吞吐量,您可能希望将immediateFlush属性设置为false -->
<immediateFlush>false</immediateFlush>
<encoder>
<!--输出格式,上面声明的常量-->
<pattern>${DEFAULT_LOG_PATTERN}</pattern>
<!-- 控制台也要使用UTF-8,不要使用GBK,否则会中文乱码 -->
<charset>UTF-8</charset>
</encoder>
</appender>
<!--
2.2、定义debug级别的日志输出,并且采用RollingFileAppender表示日志会滚动打印。注意,这里的媒介是【文本文件】
name="debugLog":定义一个名字叫做"debugLog"的日志附加器
class="ch.qos.logback.core.rolling.RollingFileAppender":表示日志附加器采用的策略,以滚动文件形式记录
-->
<appender name="debugLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!--定义文件的路径-->
<file>${FILE_PATH}/debug.log</file>
<!--定义日志滚动策略,TimeBasedRollingPolicy是一个基于时间的滚动策略-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--
设置按每天的方式来生成文件
如果把%d{yyyy-MM-dd}换成%d{yyyy-MM-dd_HH_mm}则表示按每分钟来生成。(PS:文件名不能有空格)
log.gz:gz则表示自动压缩日志文件,不需要压缩,则不要以gz结尾
-->
<fileNamePattern>${LOG_PATH}/debug-%d{yyyy-MM-dd}.log.gz</fileNamePattern>
<!--设置文件最搭保存的历史数据,这里默认30天-->
<maxHistory>30</maxHistory>
</rollingPolicy>
<!--定义日志输出的格式,上面声明的常量-->
<encoder>
<pattern>${DEFAULT_LOG_PATTERN}</pattern>
</encoder>
<!-- 定义日志记录的过滤器,很显然,在这里我们当然选择“级别”过滤器-->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!--定义debug级别-->
<level>DEBUG</level>
<!--如果匹配,则同意打印-->
<onMatch>ACCEPT</onMatch>
<!--如果不匹配,则拒绝打印-->
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 2.3、定义info级别的日志输出,并且采用RollingFileAppender表示日志会滚动打印-->
<appender name="infoLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!--定义文件的路径-->
<file>${FILE_PATH}/info.log</file>
<!--定义日志滚动策略,TimeBasedRollingPolicy是一个基于时间的滚动策略-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--
设置按每天的方式来生成文件
如果把%d{yyyy-MM-dd}换成%d{yyyy-MM-dd_HH_mm}则表示按每分钟来生成。(PS:文件名不能有空格)
log.gz:gz则表示自动压缩日志文件,不需要压缩,则不要以gz结尾
-->
<fileNamePattern>${LOG_PATH}/debug-%d{yyyy-MM-dd}.log.gz</fileNamePattern>
<!--设置文件最搭保存的历史数据,这里默认30天-->
<maxHistory>30</maxHistory>
</rollingPolicy>
<!--定义日志输出的格式-->
<encoder>
<!--定义日志输出的格式,上面声明的常量-->
<pattern>${DEFAULT_LOG_PATTERN}</pattern>
</encoder>
<!-- 定义日志记录的过滤器,很显然,在这里我们当然选择“级别”过滤器-->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!--定义info级别-->
<level>INFO</level>
<!--如果匹配,则同意打印-->
<onMatch>ACCPET</onMatch>
<!--如果不匹配,则拒绝打印-->
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!--
根记录器(项目所有的日志都会经过这里)
其实,日志的记录器就像是管道设计一样。字符流进来的时候,会一一通过<appender-ref>设置的管道中,然后根据设置的条件(<filter>标签),匹配则输出
-->
<root level="info">
<appender-ref ref="consoleLog" />
</root>
<!--
PS:这里有一个重重重重重重重重重重重重重重重重点,也是我之前没理解的点
<logger>跟<root>是什么关系呢?父子关系!(大哥们这个是java代码写的一个api,xml只是约定一些配置!约定!!配置!!understand??)
<logger>继承于<root>,所以,当<logger>没有指定level的时候,会自动继承<root>标签的level属性
所以,日志记录的过程,就是在走职责链的过程,即一一通过logger过滤器
-->
<!--
配置我们自己写的代码的日志记录器
name="com.shen.boot":表示对应包里面的类代码产生的日志才会被下面的记录器记录,否则不会
level="debug":表示我们的日志记录的级别是debug
additivity="true":表示我们自定义的记录器日志输出
-->
<logger name="com.shen" level="debug" additivity="true">
<appender-ref ref="debugLog" />
</logger>
<logger name="com.shen.boot" level="debug" additivity="true">
<appender-ref ref="debugLog" />
<appender-ref ref="infoLog" />
</logger>
</configuration>
我知道,应该有不少人没有耐心阅读上面这个文件,但我告诉你,B也要B自己看完,里面我加了很多注释,基本上都是必要的内容。自觉点,赶紧回头再看一遍
以上内容都看完了吧,我想你也许有如下疑问,下面还是用我最喜欢的问答形式解答疑问。
日志记录的内部流程是怎样的?
答:如下图所示,用一个简单的流程图表示:
上图有两点需要补充:(我希望你们最好知道“职责链”这个设计模式,可以更好的了解。设计模式传送门)
- logger过滤器,里面的各个过滤器是如何排序,并且传递的?他们是自下而上,逐级调用的,如下图所示:
我相信这个已经很清晰了吧?这张图也说明了additivity这个属性的用法。 - appender过滤器里面的逻辑是什么?
答:过滤器嘛,逻辑不就是逐个调用吗,至于在这个appender会不会写日志,就是根据你在appender里面的配置咯。如下,你的infoLog配置不就是”只要info级别“的日志吗
<!--infoLog的filter片段-->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!--定义info级别-->
<level>INFO</level>
<!--如果匹配,则同意打印-->
<onMatch>ACCPET</onMatch>
<!--如果不匹配,则拒绝打印-->
<onMismatch>DENY</onMismatch>
</filter>
root标签跟logger标签的关系如何理解?
答:root跟logger之间是父子关系,logger继承于root。细心的朋友肯定看到我在xml文件中的注释了,他们就是用java代码写的,这两个之间就是logger类继承了root类
感谢
来自B站的V哥教程 logback小白入门课程
以及一些CSDN大佬的博客,查阅了不少资料,但这次是第二次总结了,然后第一次总结的时候没记录出处,所以没办法溯源了