XXL-Job调用说明相关, 将log日志同步输出到XXL-JOB调度日志中
https://blog.csdn.net/zack_GZ/article/details/124974986
通常我们需要将日志打印到xxl-job的调度日志中时,会使用xxl-job提供的接口 XxlJobLogger.log("message xxxxxxxxxx")
但是这种方式不会将日志打印到spring应用输出的日志中,这就会导致程序出现缺陷时我们需要结合xxl-job后台的调度日志和spring的log日志,两边对照排查问题。
还有一种情况就是调度平台的模块a依赖xxl-job,可以正常使用
XxlJobLogger.log("message xxxxxxxxxx")
接口输出日志。但是如果另一个业务模块b被a依赖,而b没有导入xxl-job依赖,就会导致模块b无法使用接口XxlJobLogger.log输出日志。
依赖关系
模块a中打印日志时可以同时使用sl4j和XxlJobLogger,模块b中只能使用sl4j,导致模块b中的日志信息无法记录到xxl-job调度平台中。
为了集中管理日志信息,统一日志输出信息,希望一个接口就能同时将日志信息记录到spring的log文件中以及xxl-job的调度日志中。
解决方案:sl4j + cglib代理
因为原有业务模块(例如模块b)都是使用sl4j日志框架,沿用该框架对原有代码改动最小,只需要覆盖类里面原有的log对象。
再者就是如果有一个模块c调用模块b,则不需要将日志信息记录到xxl-job中,所以只需要模块a调用模块b时开启cglib代理即可。
实现方式:
使用cglib创建Logger代理类,代理info、debug、error方法,在方法拦截中判断参数模式并输出。
在方法拦截中获取到常规的logger对象并输出日志到spring的log日志中,再通过XxlJobLogger输出到xxl-job中。这一步是在模块a(依赖了xxl-job)中做的,所以可以使用xxl-job的日志接口。
使用createLogger方法创建log代理对象:
public class LoggerEnhancer { private static final Map<Class<?>, Logger> LOGGER_CACHE = new HashMap<>(); private final static List<String> ENHANCE_METHOD_LIST = Arrays.asList("info", "debug", "error"); private static Logger getLogger(Class<?> clazz) { if (LOGGER_CACHE.containsKey(clazz)) { return LOGGER_CACHE.get(clazz); } Logger logger = LoggerFactory.getLogger(clazz); LOGGER_CACHE.put(clazz, logger); return logger; } public static Logger createLogger(Class<?> clazz) { Object proxy = Enhancer.create(Logger.class, new LoggerMethodInterceptor(clazz)); return (Logger) proxy; } public static class LoggerMethodInterceptor implements MethodInterceptor{ private final Class<?> clazz; public LoggerMethodInterceptor(Class<?> clazz) { this.clazz = clazz; } @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { if (ENHANCE_METHOD_LIST.contains(method.getName()) && objects != null && objects.length > 0) { Object[] args = new Object[0]; if (objects.length > 1) { // 超过3个参数时 logger 通过不定参数形式重载 if (method.getParameterCount() == 2 && method.getParameters()[1].getType().isArray()) { args = (Object[]) objects[1]; }else { args = new Object[objects.length - 1]; System.arraycopy(objects, 1, args, 0, args.length); } } String temp = (String) objects[0]; log(method.getName(), temp, args); } return null; } public void log(String methodName, String str, Object... args) { Logger logger = getLogger(clazz); switch (methodName) { case "log": logger.info(str, args); XxlJobLogger.log(str, args); break; case "debug": logger.debug(str, args); XxlJobLogger.log(str, args); break; case "error": logger.error(str, args); logError(str, args); break; default: logger.info(str, args); XxlJobLogger.log(str, args); } } public static void logError(String str, Object... args) { for (Object arg : args) { if (arg instanceof Throwable) { XxlJobLogger.log((Throwable)arg); return; } } XxlJobLogger.log(str, args); } } }
在xxl-job的调度入口JobHandler实现中将创建的log代理对象传入其他模块中,覆盖原有的log对象:
public class DemoHandler extends IJobHandler { @Autowired private LogTestService logTestService; private static final Logger log = LoggerEnhancer.createLogger(DemoHandler.class); @Override public ReturnT<String> execute(String param) throws Exception { // 创建LogTestService的代理logger并覆盖原有logger对象 LogTestService.setLog(LoggerEnhancer.createLogger(LogTestService.class)); Date date = new Date(); log.info("现在时间是:{}", date); XxlJobLogger.log("这行日志不会打印到控制台,但是会打印到XXL-JOB后台调度日志中"); logTestService.logMessage(); return ReturnT.SUCCESS; } }
LogTestService:
@Service public class LogTestService { private static Logger log = LoggerFactory.getLogger(LogTestService.class); public static void setLog(Logger log){ LogTestService.log = log; } public void logMessage(){ log.info("LogTestService : 输出一些日志信息"); } }
通过xxl-job调度执行demoHandler效果:
xxl-job调度日志: