xxljob是一个非常简单易用的国产调度工具,它分为调度器和执行器。其中调度器是一个web系统,可以管理任务和查看日志。我们经常使用xxljob来做微服务的任务调度,但是在服务中,我们的执行器可能就是一个方法,这个方法中可能层层嵌套有大量的logback(log4j没有试)的logger打印日志的代码,我们怎么让xxljob的web管理端可以看到这些logger打印的日志呢?

原理就是利用logback的SiftingAppender,这个appender的作用,就是当检测到MDC中的某个变量值(被称为discriminator)改变时,会产生一个新的子appender去输出该日志事件。我们只要把这个discriminator的值设置为xxljob执行器的执行日志路径,并把这个路径设置为子appender的日志文件路径,那么就可以实现:logger的日志输出到xxljob的执行日志文件中的效果。然后就可以在web端点执行日志查看详细日志了。

代码如下:
一,配置logback的SiftingAppender,注意设置两个参数,否则如果日志执行太频繁,会导致服务器文件句柄耗尽。
timeout:60秒内没有日志输出到这个appender,就关闭日志文件流
maxAppenderCount:最大子appender个数设置为100,同时最多打开100个日志文件,多了就关闭最先打开的日志文件流

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false" scan="true" scanPeriod="1 seconds">

    <contextName>logback</contextName>
    <property name="log.path" value="/data/applogs/xxl-job/xxl-job-executor-sample-springboot.log"/>

    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${log.path}</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${log.path}.%d{yyyy-MM-dd}.zip</fileNamePattern>
        </rollingPolicy>
        <encoder>
            <pattern>%date %level [%thread] %logger{36} [%file : %line] %msg%n
            </pattern>
        </encoder>
    </appender>

    <appender name="sift" class="ch.qos.logback.classic.sift.SiftingAppender">
        <timeout>60000</timeout>
        <maxAppenderCount>100</maxAppenderCount>
        <discriminator>
            <key>xxlJobLogPath</key>
            <defaultValue>default</defaultValue>
        </discriminator>
        <sift>

            <appender name="XXL_JOB_FILE" class="ch.qos.logback.core.FileAppender">
                <file>${xxlJobLogPath}</file>
                <append>true</append>
                <encoder charset="UTF-8">
                    <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} %n</pattern>
                </encoder>
                <filter class="com.xxl.job.executor.core.config.XxlJobLogFilter"/>
            </appender>
        </sift>
    </appender>

    <root level="info">
        <appender-ref ref="console"/>
        <appender-ref ref="file"/>
        <appender-ref ref="sift"/>
    </root>

</configuration>

二,给所有执行器方法添加切面,当进入这些方法时,更新discriminator为本次执行的执行日志文件路径。

package com.xxl.job.executor.core.config;

import com.xxl.job.core.log.XxlJobFileAppender;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;

/**
 * @Author: 遛猫达人
 * @Description: xxljob执行方法的切面
 * @DateTime: 2021/12/26 23:09
 **/
@Aspect
@Component
public class XxlJobAspect {

    public static final String XXL_JOB_LOG_PATH = "xxlJobLogPath";

    @Pointcut("@annotation(com.xxl.job.core.handler.annotation.XxlJob)")
    public void execute(){}

    @Before("execute()")
    public void before(){
        String xxlJobLogPath = XxlJobFileAppender.getLogPath();
        MDC.put(XXL_JOB_LOG_PATH, xxlJobLogPath);
    }

    @After("execute()")
    public void after(){
        MDC.remove(XXL_JOB_LOG_PATH);
    }
}

三,给SiftingAppender的子appender添加日志过滤器,如果不加这个过滤器,那么当discriminator没有值的时候(就是没有进入切面的时候)会打印大量日志到名为default的文件中。

package com.xxl.job.executor.core.config;

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.filter.Filter;
import ch.qos.logback.core.spi.FilterReply;
import org.slf4j.MDC;
import org.springframework.util.StringUtils;

import static com.xxl.job.executor.core.config.XxlJobAspect.XXL_JOB_LOG_PATH;

/**
 * @Author: 遛猫达人
 * @Description: xxljob日志自定义过滤器
 * @DateTime: 2021/12/26 23:10
 **/
public class XxlJobLogFilter extends Filter<ILoggingEvent> {

    @Override
    public FilterReply decide(ILoggingEvent event) {
        String xxlJobLogFile = MDC.get(XXL_JOB_LOG_PATH);
        if (!StringUtils.isEmpty(xxlJobLogFile)) {
            return FilterReply.ACCEPT;
        }
        return FilterReply.DENY;
    }
}

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐