详解Spring Boot下使用logback 记录多个文件日志
背景
这两天遇到一个比较有意思的日志问题.
近期对我之前的python代码进行java的重构,一方面是因为java使用的医疗库非常健全稳定,可以商用.另一方面是因为java速度快,这个库的实现的效率也高,性能是Python版本的好几倍.
但是作为这个项目的唯一作者,我的癖好也成为这个项目的风格.这个项目会给很多部署工程师使用.当然项目的可用性和性能作为第一考虑的因素,但是作为一个懒人,对使用软件时候的复杂部署过程和混乱调试信息深恶痛绝.所以我在项目中使用了高度可配置/易用和多文件日志.
说道多文件日志,它的优点是每个日志只容纳自身的逻辑,所以对于一般的入门开发者或者是初级运维工程师查看起来非常方便.
初步尝试
因为springboot的配置一般来讲是application.properties,但是同时开发者可以使用yml格式的配置,二者相比,yml文件更为简洁.熟读python之禅的我当然是简洁胜于冗余选择了yml.
发现spring-boot可以通过application.yml配置日志.高兴的配置一番之后发现没法配置多个logger,弃用!改用logback-spring.xml(为什么不用logback.xml?因为-spring这种文件可以获取到spring配置中的变量.下面再说)
第一次实现
我有好几个服务需要打日志.一般来讲我的日志风格是*.log保存INFO以上级别日志.*.err.log保存ERROR以上级别日志.我如果每个文件日志都使用一个Appender的话,配置文件太长了.而且很难看,不是我的风格.
Google了一下,发现了这种方案:
#下面这一行的意思是使用application.yml中的global.log-dir变量 #这个是一个可以定义变量的Appender #使用LoggerNameBasedDiscriminator这个类根据当前Logger获取变量 general #根据变量loggerName名字生成根据日期滚动的Appender ${LOG_DIR}/${loggerName}.log ${LOG_DIR}/${loggerName}.%d{yyyy-MM-dd}.log.gz 15 INFO %d{HH:mm:ss.SSS}%-5level-%msg%n ${LOG_DIR}/${loggerName}.err.log ${LOG_DIR}/${loggerName}.%d{yyyy-MM-dd}.err.log.gz 15 ERROR %d{HH:mm:ss.SSS}%-5level-%msg%n
下的是对应的LoggerNameBasedDiscriminator类
packagecom.utils.loggers; importch.qos.logback.classic.spi.ILoggingEvent; importch.qos.logback.core.sift.AbstractDiscriminator; publicclassLoggerNameBasedDiscriminatorextendsAbstractDiscriminator{ privatestaticfinalStringKEY="loggerName"; privateStringdefaultValue; publicStringgetDefaultValue(){ returndefaultValue; } publicvoidsetDefaultValue(StringdefaultValue){ this.defaultValue=defaultValue; } #这就是之所以xml里面可以引用loggerName变量的原因 publicStringgetKey(){ returnKEY; } publicvoidsetKey(){ thrownewUnsupportedOperationException("Keynotsettable.Using"+KEY); } publicStringgetDiscriminatingValue(ILoggingEvente){ StringloggerName=e.getLoggerName(); if(loggerName==null) returndefaultValue; else{ String[]split=loggerName.split("\\."); returnsplit[split.length-1]; } } }
最开始我的日志里面没有报错信息,正常的生成INFO日志.但是后来发现事情好像不是想象的那样
问题出现
后来我把程序改成多线程.发现所有涉及到多线程的服务日志里面都没信息了.Google半天,发现几个令我震惊的真相:
- 真相1:所有滚动Appender都不支持异步追加(其实也不是,但是那种方式需要写死日志文件名,不推荐,不讲)
- 真相2:SiftingAppender内部最多嵌套一个Appender.所以理论上我的ERROR的日志里面应该永远不会有内容.
问题解决
对于之前的两个问题,分而治之.
不支持异步
再次谷歌(到这里读者基本上发现了我搬砖的本质),发现有个Appender名字叫AsyncAppender,这玩意是一个其他Appender的Wrapper.说白了,就是你打日志的命令是异步的,放到队列里面,而它真正的打日志的动作是一个单独的同步线程.这就牛逼了,使用这玩意收集我所有日志,然后再转发给SiftingAppender进行分发即可.
SiftingAppender内部最多嵌套一个Appender
这个好办,把INFO的Appender和ERROR的Appender拆开放到两个SiftingAppender里面就行了,不过这样的话,前面提到的的AsyncAppender也要写两个.
最后,logback-spring.xml文件如下
${LOG_DIR}/${loggerName}.log ${LOG_DIR}/${loggerName}.%d{yyyy-MM-dd}.log 15 INFO %d{HH:mm:ss.SSS}%-5level-%msg%n ${LOG_DIR}/${loggerName}.err.log ${LOG_DIR}/${loggerName}.%d{yyyy-MM-dd}.err.log 15 50MB ERROR %d{HH:mm:ss.SSS}%-5level-%msg%n 0 512 0 512
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。