Logback使用总结

Logback使用总结


前言

整理了下logback的常用点功能,并记录了一些在使用过程中的疑问,和问题的排错过程,防止自己再犯类似错误,也希望对路过的你有所帮助。

一、logback如何使用

任何框架的使用都是三步走:
1.导入jar包
2.配置文件
3.开始使用
对于logback自然也是不例外的,这里简单说下,不做过多的赘述。springboot默认集成了logback,所以若是建立springboot项目千万别在导入logback的jar包了,不然就会有问题。
对于配置文件的管理,会在第二部分详细介绍这块。
使用的话就比较简单了,直接增加注解@Slf4j,然后使用log.info就可以正常使用了。

二、知识点

1.logback简单模板

以下是logback的简单模板(其实生产系统也是这一套),涵盖了logback的常用配置,日常使用这些配置也就够了,基于这个配置来总结下logback的使用:

<?xml version="1.0" encoding="UTF-8"  ?>
<configuration debug="false"><!--debug=false表示不打印logback的debug信息-->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%date{yyyy-MM-dd HH:mm:ss.sss}  %-5level  %class{30}  行:%line   %msg%n</pattern>
        </encoder>
    </appender>

    <appender name="RollingFileInfo" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>./logs/info.log</file>
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>info</level>
        </filter>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>error</level>
            <onMatch>DENY</onMatch>
            <onMismatch>ACCEPT</onMismatch>
        </filter>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>./logs/%d{yyyy-MM, aux}/info.%d{yyyy-MM-dd}.log.gz</fileNamePattern>
            <maxHistory>30</maxHistory>
            <totalSizeCap>5GB</totalSizeCap>
        </rollingPolicy>
        <encoder>
            <pattern>%date{yyyy-MM-dd HH:mm:ss.sss}  %-5level  %-60class{60}  行:%-5line  %msg%n</pattern>
        </encoder>
    </appender>


    <appender name="RollingFileError" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>./logs/error.log</file>
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>ERROR</level>
        </filter>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>./logs/%d{yyyy-MM,aux}/error.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
            <maxFileSize>50MB</maxFileSize>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>%date{yyyy-MM-dd HH:mm:ss.sss}  %-5level  %-60class{60}  行:%-5line  %msg%n</pattern>
        </encoder>
    </appender>


    <root level="info">
        <appender-ref ref="STDOUT" />
        <appender-ref ref="RollingFileInfo"/>
        <appender-ref ref="RollingFileError"/>
    </root>
</configuration>

2.解析主要标签

1.configuration标签
它是根标签,包含下面三个属性
scan: 当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。
debug: 当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。
scanPeriod: 设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。
使用解读:一般这三个属性都是使用默认配置,无需更改。


2.root标签

该标签只能有一个,用来声明根记录器root的一些属性它有一个属性level用来声明日志等级:trace< debug< info< warn< error,此外所有的appender都需要告诉root根记录器,常用配置如下:

	<root level="info"> <!-- 声明日志级别,这里声明将作用于所有记录器-->
	    <appender-ref ref="STDOUT" /> <!-- 声明附加器引用-->
	    <appender-ref ref="RollingFileInfo"/>
	    <appender-ref ref="RollingFileError"/>
	</root>

3.appender标签
该标签用来定义附加器,所谓附加器就是日志附着的方式,该标签与root标签同级,它包含2个属性
name:该属性指定附加器的名称与root标签中保持一致。
class:指定日志的附加方式,常用的附加器有:

        控制台附加器:ch.qos.logback.core.ConsoleAppender
		文件附加器:ch.qos.logback.core.FileAppender
		滚动文件附加器:ch.qos.logback.core.rolling.RollingFileAppender

三种附加器的区别,显而易见。 控制台附加器将日志输出到控制台,文件附加器将日志输出到文件,滚动文件附加器将日志输出到文件,滚动文件附加器支持文件的分割而文件附加器不支持。所以基本都是用:控制台附加器、滚动文件附加器。


4.encoder标签
编码器,用来声明日志的格式,常用方式如下

	<encoder>
	    <pattern>%date{yyyy-MM-dd HH:mm:ss.sss}  %-5level  %-60class{60}  行:%-5line  %msg%n</pattern>
	</encoder>

常用的信息获取方式如下:
1)%date 获取当前日期,也可以指定更具体的格式比如:%date{yyyy-MM–dd HH:mm:ss.sss}
2)%level 获取当前日志等级
3)%msg 获取打印的日志信息,也就是通过代码中通过记录器打印的日志
4)%n 换行,日志在pattern都是要加的,这样日志在输出时才会正确换行
5)%logger 获取当前的记录器,还可以指定记录器的最长显示长度,比如:%logger{50},注意这个长度只是限制打印出的记录器的名字的长度
6)%class 获取当前类名,与logger类似,class也可以指明长度,比如:%class{40}
7)%line 获取打印日志的语句所处代码的行数。
8)%thread 获取当前线程名称
9)%10class 不足10位前面补空格
10%-10class 不足10位后面补空格


5.file标签
该标签比较简单用来声明日志文件,比较简单。


6.filter标签
改标签为过滤器,常用的过滤器有两种都是基于日志级别的过滤:
级别过滤器:ch.qos.logback.classic.filter.LevelFilter
当输出日志匹配到level时,就会返回onMatch中的信息,不匹配时返回onMismatch中的信息。
若是返回DENY则是拒绝输出,返回ACCEPT则是输出,此外还可以返回NEUTRAL保持中立,下面过滤器的作用是:若是日志是error则不输出日志,否则输出日志。

	<filter class="ch.qos.logback.classic.filter.LevelFilter">
	    <level>error</level>
	    <onMatch>DENY</onMatch>
	    <onMismatch>ACCEPT</onMismatch>
	</filter>

阈值过滤器:ch.qos.logback.classic.filter.ThresholdFilter
若是输出的日志级别高于level中声明的级别则会输出,否则不会输出。高于info的也就是warn和error了。

	<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
	    <level>info</level>
	</filter>

7.rollingPolicy标签
这个标签内容会多一些,这里主要是定义日志的分割行为的。用官方话说就是定义日志的滚动策略。常用的滚动策略有两种:
基于时间的滚动策略:ch.qos.logback.core.rolling.TimeBasedRollingPolicy
他的常用配置如下main所示了,fileNamePattern用来声明日志分割的具体策略,%d{yyyy-MM, aux}这里需要特别注意,fileNamePattern中只允许一个日期不声明aux,其他都必须声明为aux,这样logback才知道是以哪个时间为基准来分割日志。maxHistory标识日志存放的时间,这个时间的单位就是fileNamePattern中日志分割的时间单位。totalSizeCap标识所有日志文件的最大大小,超过这个值,logback会清理掉最初的日志,以满足这个限制。

	<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
	    <fileNamePattern>./logs/%d{yyyy-MM, aux}/info.%d{yyyy-MM-dd}.log.gz</fileNamePattern>
	    <maxHistory>30</maxHistory>
	    <totalSizeCap>5GB</totalSizeCap>
	</rollingPolicy>

基于大小和时间的滚动策略:ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy
这个比上面的多了个maxFileSize,这个是限制文件大小的。当日志达到这个限制就会被分割。此外需要特别注意的是,若是指定的大小未达到,即使指定的时间达到了日志也不会分割,必须是日志大小达到了日志才会分割。这种分割场景比较适合日志量特别大的系统,因为此时一天只能日志可能需要多次分割,若是日志量不大的系统日志一天一分割即可,此时使用上面的基于时间分割的策略为最优。这里还两点需要注意%i,这个是分割日志时标注日志需要所用,必须要有。后面跟的点gz,是用来压缩文件的。fileNamePattern语句最后跟gz或者zip时,logback都会自动识别将文件进行压缩。

<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
    <fileNamePattern>./logs/%d{yyyy-MM,aux}/error.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
    <maxFileSize>50MB</maxFileSize>
    <maxHistory>30</maxHistory>
</rollingPolicy>

8.property标签

用来定义变量值,它有两个属性name和value,通过property定义的值会在配置文件中都可以正常获取,可以使“${}”来使用变量。

	<configuration scan="true" scanPeriod="60 seconds" debug="false"> 
	   <property name="APP_Name" value="myAppName" /> 
	   <contextName>${APP_Name}</contextName> 
	</configuration>

三、问题和排错

这里记录下使用时碰到的一些问题,有些问题很白痴,但是还是记录下防止自己再犯好了。


1.描述:只引入logback的依赖,启动工程时报错:Failed to load class “org.slf4j.impl.StaticLoggerBinder”.
解决:增加一个依赖,该依赖应该是上方类的实现了,如下:

 <dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-simple</artifactId>
  <version>1.6.6</version>
</dependency>

增加该依赖后使用代码可以正常获取记录器,但是配置文件还是不行,其实报上面这个错最根本的原因还是springboot默认已经集成了logback,无需在引入了,取消引入就ok了。


2.描述:logback配置文件不生效,直接在classpath下增加了logback.xml或者logback-spring.xml都是不生效的。
解决:测试了很多方法,更换jar包,文件位置不对的都不行,现在测试以下降低springboot的版本,当前使用是springboot2.6.3降到2.5.9最后发现也不是springboot版本的问题,而是因为springboot默认集成了logback,无需再手动引入该依赖,引入了就是多余操作,jar包多余了,删除掉引用发现正常。


3.描述:使用springboot默认的logback,发现增加配置文件后日志在控制台全部不打印了
解决:这里是因为使用了配置文件,默认都从配置文件读取配置,但是又没有指定这块配置就导致了这样的结果。
这里需要指定日志输出到控制台,并且为其指定一个appender,这样就ok了,详细配置如下:

	<?xml version="1.0" encoding="UTF-8" ?>
	<configuration>
	    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
		<encoder>
		    <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
		</encoder>
	    </appender>
	    <root level="debug">
		<appender-ref ref="STDOUT" />
	    </root>
	</configuration>

4.问题:附加器和各个记录器之间的关系?
解答:可以在记录器中声明附加器,记录器中声明的附加器会被记录器的子记录器通过继承来获得吗,我感觉可能是可以的,如果可以那我的日志为什么不生效呢,理论上根记录器有了附加器那其他的子记录器也应该有了附加器,都应该可以正常输出的啊,奇怪。
再测试下,之前的encoder中的pattern是否有日志输出
结论:父记录器的附加器确实可以被子记录器来获取,因此我们在声明附着器时是可以只在根记录器声明可以了。另外刚刚碰到的问题自己加的pattern日志没有出现,原来是因为日志忘了加%n进行换行,所有日志都输出到了一行。


5.问题:滚动文件附加器在使用基于时间的滚动分割时,文件不会自动分割。
解答:两个原因:
1).需要指定日志输出文件,并且分割文件的位置也要指定(不然会在项目的根目录里)
2).必须要有日志正在打印才会分割,若是日志文件是空的,即使满足基于时间的分割要求,日志也是不会发生分割的。


6.问题:在滚动文件附加器中使用基于大小和时间的滚动策略,若是项目重启,对待之前存在的日志文件maxHistory还能生效吗?
经验证还是生效的,所以重启并不会影响到文件备份的整体性。


7.问题:日志的分割是以fileNamePattern中的哪个值来分割的
解答:经测试,是以指定文件名的最后的%d{}来进行分割,不过其他的%d都需要使用aux来声明,如%d{yyyy-MM,aux}


8.问题;配置了logback是否还需要使用nohup命令来指定日志的输出文件?
解答:猜测是不需要的,测试下。经验证是不需要,启动命令中将控制台日志输出到黑洞即可,>/dev/null &。


9.问题:lombok中如何使用logback
解答:直接使用注解 @Slf4j即可,不过需要idea中预先装好lombok。


10.问题:文件的路径到底使用斜杠还是反斜杠
解答:好像是绝对路径使用反斜杠,相对路径使用斜杠。
经测试,无论是相对路径还是绝对路径斜杠和反斜杠在windows中都可以使用,且无区别。
但是在linux服务器上测试时,发现相对路径用反斜杠不好使,还得用斜杠。


11.问题:logback能不能直接将日志进行压缩打包,这样可以节省很多空间
解答:网上说直接在分割文件末尾加.gz即可。项目上好像也是这么用的,测试一下。
验证后确实可以,其实在末尾加.zip也是可以的,logback支持这两种声明方式进行压缩。


12.问题:加上.gz以后,日志没有正常按时间进行切分
猜测:因为加了大小1mb限制,时间是以分钟来切割。那么会不会必须先达到1mb大小才会按照时间来切割。如果这样的话就是说必须以最大的限制条件来进行切割的,若是以分钟切割,一分钟内不达到最大的文件大小是不会切割的,达不到就会达到设定的最大值才来切割,若是达到了则会 按大小来切割。那么是不是可以说,其实基于时间和大小的滚动策略,其实基础是以大小来切割的,时间只是辅助。
验证下:验证确实如此。和加.gz没有任何关系。


13.问题:若是使用logback记录日志的话,那么启动不能使用nohup来记录日志了,那启动命令该怎么写?
解答:java -server -jar -Xss1m -server -Xms512m -Xmx2048m -Xmn128m -XX:MetaspaceSize=128M -XX:+UseConcMarkSweepGC mdm-project-server-biz.jar >/dev/null &
本地测试都正常使用该命令,在uat启动时发现日志并没有正常输出。
经历了一次深刻的教训,使用jenkinsn打包发布时,因为ssh到目标服务器以后,默认路径是你登录的用户工作路径。在这里执行上面的命令就会将脚本中创建的文件在这里创建,而不是以jar包所在的路径为相对路径进行创建这个问题前后解决了3小时,深痛教训,在jenkins中写脚本时,ssh以后一定要先cd再操作。


14.问题:以时间为基准的滚动策略对比以大小和时间为基准的滚动策略
解答:时间为基准的滚动策略:指定滚动时间,就只会以时间为大小往下滚动,但是还可以为他加上这个限制:

 <timeBasedFileNamingAndTriggeringPolicy  class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
 	<!-- maxFileSize:这是活动文件的大小,默认值是10MB,测试时可改成1KB看效果 -->
 	 <maxFileSize>3kb</maxFileSize>
 </timeBasedFileNamingAndTriggeringPolicy>

这样的话,滚动效果就是和基于大小合和时间的滚动策略一模一样了。他们都是先看大小是否达到,大小达到了就会以时间进行分割日志文件,如果大小未达到是不会分割文件的。经验值加上他的效果和以大小时间为基础的滚动策略基本一致。


15.问题:fileNamePattern日志输出路径(绝对路径)增加了%{yyyy-MM}后,日志不能正常分割
解答:经验证,加了该路径后再windows系统中不能正常生产当前日期的文件夹。
下面测试下相对路径增加这个路径是否可行:不可行,在windows系统中无论是绝对路径还是相对路径增加这个文件夹都失败了。
可是记得之前是可以的,很奇怪。
再验证下linux系统中的情况:好像突然也不行了。
是不是和这个有关:%d{yyyy-MM-dd, aux}:经检查这个配置是告诉logback不是以这个时间进行分割的,一个fileNamePattern中除了指定的分割的日期,其他的日期都是需要加上这个aux的。
验证后发现确实和这个配置有关,问题解决。


总结

这是自己学习logback的总结,同时纪录了自己解决学习过程中问题的思路,logback东西不多这些基本就够用了,至于控制台的彩色日志,没啥用也就没研究。在这里记录下希望可以帮到想要学习的人。

上一篇:Spring Boot 年前最后一个版本发布,一招解决 Log4j2、Logback 漏洞。。


下一篇:println()输出中文正常,logback输出日志就是乱码,