本文概览:在将一个jar包迁移成一个spring boot服务时,需要根据定时任务划分日志文件,通过Logback 的SiftingAppender和阿里的TTL线程池来实现。

1 问题背景

之前定时任务是一个jar包,每一个任务是一个main函数,通过执行一个shell命令或者shell脚本来执行一个任务。在执行任务之前通过设置一个环境变量filename,如通过-Dfilename=taskName设定,在log4j中通过如下配置可以为每个任务定义一个日志文件。

使用xxl-job改造任务之后,任务工程模块由一个jar包改成一个服务,每一个任务由原来的一个main函数改造成一个jobhandler(可以理解为一个http接口),现在我们需要每一个JobHandler相关操作日志输出到一个日志文件中,而且考虑到不同任务配置相同的JobHandler,所以此时我们需要根据JobHandler和任务参数 来共同决定一个日志文件名称。

为了解决这个问题需要:

(1)不同任务写入不同日志文件。通过logback SiftingAppender来完成,参考https://www.jianshu.com/p/a33902d58530

(2)线程池都是公用的,在两个任务JobHandler并发执行时,这两个任务会公用线程池的work线程,如何保证一个worker线程根据调用方输出到不同的日志文件。通过TTL来实现,参考:https://www.jianshu.com/p/4093add7f2cd

2 Logback SiftingAppender

2.1 SiftingAppender介绍

之前的logger是如下配置,在com.myapp.services包下面有个Services类,这个Service的日志都会输出到固定文件service.log中,

假设有两个请求接口/test1和/test2,都会调用这个Service类,但是需要为这两个接口分别建一个日志文件,一个接口相关操作日志都需要写入到同一个日志文件中:在/test1执行时,service日志写入到test1接口的日志文件中; 在执行/test2时,servcie日志写入到test2接口的日志文件中。SiftingAppender可以动态根据运行时参数来分离log,将log输出到不同日志文件。

2.2 SiftingAppender实例

实现一个根据userid来划分日志,参考官网https://logback.qos.ch/manual/appenders.html的例子。

1、在src/main/resuources添加logback.xml

2、测试

执行结果如下

生成日志文件如下图

38801536

3、问题

刚开始没有生效,只有一个user-unkonwn.log,发现是<file>和<fileNamePattern>配置的文件名称不一致造成。

2.3 自定义描述符

在上面的logback.xml并没有使用具体类名来标识的描述符,表示我们使用了默认的描述符MDCBasedDiscriminator。

MDCBasedDiscriminator实现了MDC中根据key获取value作为日志文件名称,参考上面的例子,就是把${userid}的值放在MDC中。如上面的test1代码

为了解决本文最初引入的问题:根据JobHandler和任务参数 来生成一个日志文件。我们通过自定义描述符来实现,这个自定义描述符不再从MDC中获取key,而是从自定义的Context中获取,代码如下:

1、自定义context

参考

Threadlocal的实例

(1)定义context。保存任务名字和任务参数信息

(2)定义TaskContextHolder

2、自定义描述符。实现从自定义的context获取日志的名称。

3、修改logback.xml

原来描述符配置

替换为如下

4、测试

如下一个简单测试demo

执行结果如下:

Snip20191007_138

当使用Xxl-Job调度定时任务时,可以在JobHandler执行时进行初始化上下文的taskName和taskParam。如下

2.4 Sift归档策略(时间和大小)

SiftingAppender通过RollingFileAppender来实现归档,RollingFileAppender的归档常用的策略有两种:基于时间(TimeBasedRollingPolicy)和基于大小(SizeBasedTriggeringPolicy)。

TimeBasedRollingPolicy和SizeBasedTriggeringPolicy这两种策略同时使用时,会有冲突,为了同时兼有两种策略功能,引入了SizeAndTimeBasedRollingPolicy。

3 改造线程池

为了线程池的work线程可以获取调用线程的Threadlocal,使用阿里的TTL,参考 如下

InheritableThreadLocal和TransmittableThreadLocal介绍

在代码工程中,我们使用了spring的线程池ThreadPoolTaskExecutor。为了使用阿里TTL,需要重写一个线程池如下,这里只是重新了submit操作,如果使用到线程池其他操作,再重新实现下就可以了。

参考文章

1、logback根据不同任务ID输出到不同日志文件件 https://www.jianshu.com/p/a33902d58530

2、logback实现每个用户一个独立的日志文件 http://www.voidcn.com/article/p-vfnoregb-bob.html

3、了解logback的SiftingAppender  https://lihuanghe.github.io/2015/08/15/logback.html

4、logback官网-介绍SiftingAppender:https://logback.qos.ch/manual/appenders.html

5、归档策略-同时按时间和大小归档 https://blog.csdn.net/bluestarjava/article/details/82722033

 

分类&标签