详解Spring Boot下使用logback 记录多个文件日志

这篇文章主要介绍了详解Spring Boot下使用logback 记录多个文件日志,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

背景

这两天遇到一个比较有意思的日志问题.

近期对我之前的python代码进行java的重构, 一方面是因为java使用的医疗库非常健全稳定, 可以商用. 另一方面是因为java速度快, 这个库的实现的效率也高, 性能是Python版本的好几倍.

但是作为这个项目的唯一作者, 我的癖好也成为这个项目的风格. 这个项目会给很多部署工程师使用. 当然项目的可用性和性能作为第一考虑的因素, 但是作为一个懒人, 对使用软件时候的复杂部署过程和混乱调试信息深恶痛绝. 所以我在项目中使用了高度可配置/易用和多文件日志.

说道多文件日志, 它的优点是每个日志只容纳自身的逻辑, 所以对于一般的入门开发者或者是初级运维工程师查看起来非常方便.

初步尝试

因为spring boot的配置一般来讲是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 15INFO%d{HH:mm:ss.SSS} %-5level - %msg%n ${LOG_DIR}/${loggerName}.err.log ${LOG_DIR}/${loggerName}.%d{yyyy-MM-dd}.err.log.gz 15ERROR%d{HH:mm:ss.SSS} %-5level - %msg%n

下的是对应的 LoggerNameBaseddiscriminator 类

package com.utils.loggers; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.sift.Abstractdiscriminator; public class LoggerNameBaseddiscriminator extends Abstractdiscriminator { private static final String KEY = "loggerName"; private String defaultValue; public String getDefaultValue() { return defaultValue; } public void setDefaultValue(String defaultValue) { this.defaultValue = defaultValue; } # 这就是之所以xml里面可以引用loggerName变量的原因 public String getKey() { return KEY; } public void setKey() { throw new UnsupportedOperationException("Key not settable. Using " + KEY); } public String getdiscriminatingValue(ILoggingEvent e) { String loggerName = e.getLoggerName(); if (loggerName == null) return defaultValue; else { String[] split = loggerName.split("\."); return split[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 15INFO%d{HH:mm:ss.SSS} %-5level - %msg%n ${LOG_DIR}/${loggerName}.err.log ${LOG_DIR}/${loggerName}.%d{yyyy-MM-dd}.err.log 1550MBERROR%d{HH:mm:ss.SSS} %-5level - %msg%n 05120512

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程之家。

相关文章

Java中的String是不可变对象 在面向对象及函数编程语言中,不...
String, StringBuffer 和 StringBuilder 可变性 String不可变...
序列化:把对象转换为字节序列的过程称为对象的序列化. 反序...
先说结论,是对象!可以继续往下看 数组是不是对象 什么是对...
为什么浮点数 float 或 double 运算的时候会有精度丢失的风险...
面试题引入 这里引申出一个经典问题,看下面代码 Integer a ...