java logback 按业务,logback 按照业务主键分文件打印日志,使用SiftingAppender结合MDC. 实现项目中定时任务的日志单独打印,使用FilterReplay.NEUTR...

本文介绍了如何使用Java Logback的SiftingAppender结合MDC按业务主键划分日志文件,以及如何通过自定义Filter实现定时任务日志的单独记录,避免与其他业务日志混淆。同时,文章提到了线程池导致的MDC上下文丢失问题及解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 需求: 按照业务主键划分日志分件,可以使用siftingAppender结合MDC 实现。

sysUUID // 代码使用使用MDC.put("sysUUID",value)即可

unknown

UTF-8

${log.base}/wtposp_${sysUUID}.log // 按天生成文件夹为实现(%d{yyyyMMdd}这样写不行。)

%date{HH:mm:ss} [%X{sysUUID}] %-5level %c{0}:%L - %msg%n

TRACE

DENY

ACCEPT

2. 需求: 项目中有多个定时任务,每10s 执行一次,如果和其他业务日志打印到同一个文件,会造成日志文件很乱,业务日志中插着定时日志,

日志不连贯,不易拍错,不易查看。于是想要把定时的相关日志打印到一个单独的文件,其他日志另外打印。

(1)最先想到的解决办法是运用 logger 属性 additivity=false ,可以让日志不再往下传递。

这种方式需要指定哪些包或文件 使用这个logger , 但是我项目的定时任务调用的方法和其他业务是同一个方法(比如查询,我定时要用,其他业务也用啊),

没法区分开。

(2)使用一个标记,不如定时的日志内容都以 "JOB-" 开头,我想办法过滤下,使用appender 的 filter .  符合条件就打印,不符合就忽略或拒绝。

需要定义两个appender ,一个只接受JOB- 内容开头的日志。 另一个不接受JOB- 内容开头的日志。

知识点:一个appender 可以包含多个filter ,常见的filter 是LevelFilter ,可以按日志级别过滤日志。  FilterReply的三个枚举:

DENY: 放弃该日志,不再往下一个filter 传递

ACCEPT:  处理该条日志,不再往下传递。

NEUTRAL: 不处理,交给下一个filter 处理。

UTF-8

${log.base}/job_xx.log

${log.base}/job-xx-%d{yyyy-MM-dd}.log.zip

30

%date{HH:mm:ss} %-5level %c{0}:%L - %msg%n

ACCEPT

NEUTRAL

UTF-8

${log.base}/xx.log

${log.base}/xx-%d{yyyy-MM-dd}.log.zip

30

%date{HH:mm:ss} %-5level %c{0}:%L - %msg%n

DENY

NEUTRAL

TRACE

DENY

ACCEPT

/*** @Author zc

* @Date 2019/8/16

* 继承AbstractMatcherFilter是为了使用其属下onMatch 和 onMisMatch 。 也可以直接继承Filter,但是没法传递参数进去,不能灵活区分是否打印日志。*/

public class JobLogFilter extends AbstractMatcherFilter{

@OverridepublicFilterReply decide(ILoggingEvent event) {

String uuid= event.getMDCPropertyMap().get("sysUUID");if (uuid != null && uuid.contains("job")){return this.onMatch;

}else{return this.onMismatch;

}

}

}

20190906 补充:

结合MDC 知识,我使用MDC 存放业务主键,然后把业务主键在日志中打印,这样我就能比较清晰的查看一个请求的所有日志了。

在请求入口

MDC.put("sysUUID",xx业务唯一);

然后

%date{HH:mm:ss} [%X{sysUUID}] %-5level %c{0}:%L - %msg%n

打印出来的每行日志都有业务唯一标识。

但是最近看日志发现一个问题: 日志中夹杂着多行sysUUID 不一样的行,很是奇怪,排查发现都是使用线程池中的代码打印的日志。

跟踪代码发现MDC中有ThreadLocal , 看来问题就是线程池使用的是单独的线程,自然不一样。

解决办法: 自定义线程,把主线程的上下文发给子线程,并保存到MDC.

Map _cm = MDC.getCopyOfContextMap();

pool.submit(new Runnable() {

@Override

public void run() {

MDC.setContextMap(_cm);

//业务代码

//.....................

//

MDC.clear();

}

});

封装线程写法样例:

public abstract class A implments Runable{

private Map x ;

public A(Map a){

x = a;

}

public abstract void cm();

public void run(){

MDC.setContextMap(x);

//业务代码

//.....................

cm();

//

MDC.clear();

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值