功能简介
在当前微服务和容器化越来越流行的情况下,机器数量太多、没有权限登录容器,成为了业务同学无法快速定位业务失败的痛点。可视化白屏日志工具,越来越成为了企业的刚需。
阿里巴巴分布式任务调度系统SchedulerX2.0的日志服务,可以让业务方不需要修改一行代码,只需要增加一个log4j/logback的配置,即可在控制台看到每次任务调度(包括分布式任务)的业务日志,方便排查问题。
使用限制
开通专业版
需要开通专业版,在应用管理里,高级配置下,改成专业版,开启日志服务,如下图
日志保存时间
当前日志最多保留2周,超过2周的日志会被清理
接入配置
升级schedulerx客户端版本
将schedulerx客户端版本升级到1.4.1.1以上版本,以springboot starter为例
<dependency><groupId>com.aliyun.schedulerx</groupId><artifactId>schedulerx2-spring-boot-starter</artifactId><version>1.4.1.1</version></dependency>
配置Log Appender采集日志服务
Log4j2 Appender
方法一:单独收集任务调度日志(推荐)
该方法可以由业务自己控制,只将任务调度日志采集,其他业务代码不会采集,方便排查问题:
- log4j2增加一个Appender和Logger如下
<Configurationstatus="off"><Appenders><Consolename="Console"target="SYSTEM_OUT"><PatternLayoutpattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %m%n"/></Console><SchedulerxLog4j2Appendername="schedulerxLog"timeFormat="yyyy-MM-dd'T'HH:mmZ"timeZone="UTC"ignoreExceptions="true"><PatternLayoutpattern="%d %-5level [%thread] %logger{0}: %msg"/></SchedulerxLog4j2Appender></Appenders><Loggers><Rootlevel="info"><AppenderRefref="Console"/></Root><Loggername="schedulerx"level="info"additivity="false"><AppenderRefref="schedulerxLog"/></Logger></Loggers></Configuration>
- 业务代码略微修改,使用"schedulerx"这个logger采集任务调度的日志:
packagecom.hxm.test.processor; importcom.alibaba.schedulerx.worker.domain.JobContext; importcom.alibaba.schedulerx.worker.processor.JavaProcessor; importcom.alibaba.schedulerx.worker.processor.ProcessResult; importorg.slf4j.Logger; importorg.slf4j.LoggerFactory; importorg.springframework.stereotype.Component; publicclassHelloWorldJob3extendsJavaProcessor { privatestaticfinalLoggerlogger=LoggerFactory.getLogger("schedulerx"); publicProcessResultprocess(JobContextcontext) throwsException { LOGGER.info("hello HelloWorldJob3"); returnnewProcessResult(true); } }
方法二:搜集所有业务日志
该方法只需要配置log4j文件,不需要修改一行业务代码。将schedulerxLog配置在rootLogger里,会把所有日志都通过schedulerx采集,性能不好,而且其他业务日志和任务调度日志在一起,比较难排查问题,配置如下:
<Configurationstatus="off"><Appenders><Consolename="Console"target="SYSTEM_OUT"><PatternLayoutpattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %m%n"/></Console><SchedulerxLog4j2Appendername="schedulerxLog"timeFormat="yyyy-MM-dd'T'HH:mmZ"timeZone="UTC"ignoreExceptions="true"><PatternLayoutpattern="%d %-5level [%thread] %logger{0}: %msg"/></SchedulerxLog4j2Appender></Appenders><Loggers><Rootlevel="info"><AppenderRefref="Console"/><AppenderRefref="schedulerxLog"/></Root></Loggers></Configuration>
Log4j Appender
方法一:单独收集任务调度日志(推荐)
- log4j.properties增加一个appender和logger
log4j.rootLogger = INFO,console log4j.logger.schedulerx=schedulerxLog log4j.appender.schedulerxLog=com.alibaba.schedulerx.worker.log.appender.SchedulerxLog4jAppender
- 业务代码略微修改,使用"schedulerx"这个logger采集任务调度的日志:
packagecom.hxm.test.processor; importcom.alibaba.schedulerx.worker.domain.JobContext; importcom.alibaba.schedulerx.worker.processor.JavaProcessor; importcom.alibaba.schedulerx.worker.processor.ProcessResult; importorg.slf4j.Logger; importorg.slf4j.LoggerFactory; importorg.springframework.stereotype.Component; publicclassHelloWorldJob3extendsJavaProcessor { privatestaticfinalLoggerlogger=LoggerFactory.getLogger("schedulerx"); publicProcessResultprocess(JobContextcontext) throwsException { LOGGER.info("hello HelloWorldJob3"); returnnewProcessResult(true); } }
方法二:收集所有日志
log4j.properties增加一个appender,将这个appender配置在rootLogger里:
log4j.rootLogger = INFO,console,schedulerxLog log4j.appender.schedulerxLog=com.alibaba.schedulerx.worker.log.appender.SchedulerxLog4jAppender
Logback Appender
方法一:单独收集任务调度日志(推荐)
- logback.xml增加一个appender和logger
<appendername="schedulerxLog"class="com.alibaba.schedulerx.worker.log.appender.SchedulerxLogbackAppender"><timeFormat>yyyy-MM-dd'T'HH:mmZ</timeFormat><timeZone>UTC</timeZone></appender><rootlevel="INFO"><appender-refref="CONSOLE"/></root><loggername="schedulerx"level="INFO"><appender-refref="schedulerxLog"/></logger>
- 业务代码略微修改,使用"schedulerx"这个logger采集任务调度的日志:
packagecom.hxm.test.processor; importcom.alibaba.schedulerx.worker.domain.JobContext; importcom.alibaba.schedulerx.worker.processor.JavaProcessor; importcom.alibaba.schedulerx.worker.processor.ProcessResult; importorg.slf4j.Logger; importorg.slf4j.LoggerFactory; importorg.springframework.stereotype.Component; publicclassHelloWorldJob3extendsJavaProcessor { privatestaticfinalLoggerlogger=LoggerFactory.getLogger("schedulerx"); publicProcessResultprocess(JobContextcontext) throwsException { LOGGER.info("hello HelloWorldJob3"); returnnewProcessResult(true); } }
方法二:收集所有日志
logback.xml增加一个appender,将这个appender配置在rootLogger里:
<appendername="schedulerxLog"class="com.alibaba.schedulerx.worker.log.appender.SchedulerxLogbackAppender"><timeFormat>yyyy-MM-dd'T'HH:mmZ</timeFormat><timeZone>UTC</timeZone></appender><rootlevel="INFO"><appender-refref="CONSOLE"/><appender-refref="schedulerxLog"/></root>
日志输出
日志输出样例如下:
- ip: 打印该日志的执行机器
- executionId: 本次任务实例的执行id,格式为${jobId}_${jobInstanceId}_${taskId}
- level: 日志级别
- log:日志的信息
- throwable: 如果抛异常,会打印该字段
应用场景及演示
查询业务失败的原因
SchedulerX2.0的日志服务,可以搜集任务的执行日志和异常,包括service的日志都可以收集,demo如下
- 新建任务代码和service业务代码,配置使用schedulerx打印日志
packagecom.hxm.test.processor; importcom.alibaba.schedulerx.test.service.TestService; importcom.alibaba.schedulerx.worker.domain.JobContext; importcom.alibaba.schedulerx.worker.processor.JavaProcessor; importcom.alibaba.schedulerx.worker.processor.ProcessResult; importorg.apache.logging.log4j.LogManager; importorg.apache.logging.log4j.Logger; importorg.springframework.beans.factory.annotation.Autowired; importorg.springframework.stereotype.Component; publicclassHelloWorldJobextendsJavaProcessor { //使用schedulerx收集日志privatestaticfinalLoggerLOGGER=LogManager.getLogger("schedulerx"); privateTestServicetestService; publicProcessResultprocess(JobContextcontext) throwsException { Stringparameters=context.getJobParameters(); Stringtokens[] =parameters.split(" "); inta=Integer.valueOf(tokens[0]); intb=Integer.valueOf(tokens[1]); intc=testService.doDivision(a, b); LOGGER.info("testService.doDivision finished, a={}, b={}, c={}", a, b, c); if (c<0) { returnnewProcessResult(false, "result="+c); } returnnewProcessResult(true); } }
packagecom.hxm.test.service; importorg.apache.logging.log4j.LogManager; importorg.apache.logging.log4j.Logger; importorg.springframework.stereotype.Service; "testService") (publicclassTestServiceImplimplementsTestService { //使用schedulerx收集日志privatestaticfinalLoggerLOGGER=LogManager.getLogger("schedulerx"); publicintdoDivision(inta, intb) { try { LOGGER.info("start to do division c = "+a+"/"+b); intc=a/b; LOGGER.info("c="+c); returnc; } catch (Exceptione) { LOGGER.error("", e); } return-1; } }
- 控制台配置任务如下,根据代码来看,1除以0肯定会抛异常
- 运行一次后,通过任务实例列表,查看日志
如上图所以,我们可以看到失败的原因是在TestServiceImpl抛出了除0的异常
查询分布式任务失败原因
SchedulerX2.0的分布式任务用来跑批,某个批次跑批失败了,想查询具体是哪个子任务失败了,demo如下
- 新建任务代码如下
packagecom.hxm.test.processor; importjava.util.ArrayList; importjava.util.List; importcom.alibaba.schedulerx.worker.domain.JobContext; importcom.alibaba.schedulerx.worker.processor.MapJobProcessor; importcom.alibaba.schedulerx.worker.processor.ProcessResult; importorg.apache.logging.log4j.LogManager; importorg.apache.logging.log4j.Logger; publicclassTestMapJobProcessorextendsMapJobProcessor { privatestaticfinalLoggerLOGGER=LogManager.getLogger("schedulerx"); publicProcessResultprocess(JobContextcontext) throwsException { StringtaskName=context.getTaskName(); Stringparameter=context.getJobParameters(); intdispatchNum=Integer.valueOf(parameter); if (isRootTask(context)) { LOGGER.info("start root task"); List<String>msgList=newArrayList<>(); for (inti=0; i<=dispatchNum; i++) { msgList.add("msg_"+i); } returnmap(msgList, "Level1Dispatch"); } elseif (taskName.equals("Level1Dispatch")) { Stringtask= (String)context.getTask(); if (task.equals("msg_23")) { LOGGER.error("msg={}, failed", task); returnnewProcessResult(false); } else { LOGGER.info("msg={}, success", task); } returnnewProcessResult(true); } returnnewProcessResult(false); } }
- 配置任务如下
- 运行一次后,查看日志
- 搜索关键字,快速定位失败的机器和原因
根据关键字查询历史日志
任务实例历史记录,只保留最近60条,如果想排查历史上任务失败的原因,可以通过左边栏日志查询。支持根据任务id和关键字搜索,支持查询时间区间搜索