diff --git a/README.md b/README.md index 0765afe4..49753394 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ git.osc地址:http://git.oschina.net/xuxueli0323/xxl-job 7、支持任务执行日志; 8、支持自定义参数; 9、支持任务失败次数超阈值邮件报警; + 10、支持在线查看,执行器详细日志; # 新版本 V1.2.x,新特性 1、支持任务分组; diff --git a/xxl-job-admin/src/main/java/com/xxl/job/controller/JobLogController.java b/xxl-job-admin/src/main/java/com/xxl/job/controller/JobLogController.java index d2cf9edc..5a840c6a 100644 --- a/xxl-job-admin/src/main/java/com/xxl/job/controller/JobLogController.java +++ b/xxl-job-admin/src/main/java/com/xxl/job/controller/JobLogController.java @@ -16,8 +16,12 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; +import com.xxl.job.client.handler.HandlerRepository; +import com.xxl.job.client.util.HttpUtil; import com.xxl.job.client.util.HttpUtil.RemoteCallBack; +import com.xxl.job.client.util.JacksonUtil; import com.xxl.job.core.constant.Constants.JobGroupEnum; +import com.xxl.job.core.model.ReturnT; import com.xxl.job.core.model.XxlJobLog; import com.xxl.job.dao.IXxlJobLogDao; @@ -88,4 +92,76 @@ public class JobLogController { return callBack; } + @RequestMapping("/logDetail") + @ResponseBody + public ReturnT logDetail(int id){ + // base check + XxlJobLog log = xxlJobLogDao.load(id); + if (log == null) { + return new ReturnT(500, "参数异常"); + } + + // server address + @SuppressWarnings("unchecked") + Map jobDataMap = JacksonUtil.readValue(log.getJobData(), Map.class); + String handler_address = jobDataMap.get(HandlerRepository.HANDLER_ADDRESS); + if (!handler_address.startsWith("http")){ + handler_address = "http://" + handler_address + "/"; + } + // trigger id, trigger time + Map reqMap = new HashMap(); + reqMap.put(HandlerRepository.NAMESPACE, HandlerRepository.NameSpaceEnum.LOG.name()); + reqMap.put(HandlerRepository.TRIGGER_LOG_ID, String.valueOf(id)); + reqMap.put(HandlerRepository.TRIGGER_TIMESTAMP, String.valueOf(log.getTriggerTime().getTime())); + + RemoteCallBack callBack = HttpUtil.post(handler_address, reqMap); + if (HttpUtil.RemoteCallBack.SUCCESS.equals(callBack.getStatus())) { + return new ReturnT(callBack.getMsg()); + } else { + return new ReturnT(500, callBack.getMsg()); + } + } + + @RequestMapping("/logDetailPage") + public String logDetailPage(int id, Model model){ + ReturnT data = logDetail(id); + model.addAttribute("result", data); + return "joblog/logdetail"; + } + + @RequestMapping("/logKill") + @ResponseBody + public ReturnT logKill(int id){ + // base check + XxlJobLog log = xxlJobLogDao.load(id); + if (log == null) { + return new ReturnT(500, "参数异常"); + } + + // server address + @SuppressWarnings("unchecked") + Map jobDataMap = JacksonUtil.readValue(log.getJobData(), Map.class); + String handler_address = jobDataMap.get(HandlerRepository.HANDLER_ADDRESS); + if (!handler_address.startsWith("http")){ + handler_address = "http://" + handler_address + "/"; + } + String handler_name = jobDataMap.get(HandlerRepository.HANDLER_NAME); + + // trigger id, trigger time + Map reqMap = new HashMap(); + reqMap.put(HandlerRepository.NAMESPACE, HandlerRepository.NameSpaceEnum.KILL.name()); + reqMap.put(HandlerRepository.HANDLER_NAME, handler_name); + reqMap.put(HandlerRepository.TRIGGER_TIMESTAMP, String.valueOf(System.currentTimeMillis())); + + RemoteCallBack callBack = HttpUtil.post(handler_address, reqMap); + if (HttpUtil.RemoteCallBack.SUCCESS.equals(callBack.getStatus())) { + log.setHandleStatus(HttpUtil.RemoteCallBack.FAIL); + log.setHandleMsg("人为操作主动终止"); + log.setHandleTime(new Date()); + xxlJobLogDao.updateHandleInfo(log); + return new ReturnT(callBack.getMsg()); + } else { + return new ReturnT(500, callBack.getMsg()); + } + } } diff --git a/xxl-job-admin/src/main/java/com/xxl/job/core/thread/JobMonitorHelper.java b/xxl-job-admin/src/main/java/com/xxl/job/core/thread/JobMonitorHelper.java index eee7ffb2..6cf209b7 100644 --- a/xxl-job-admin/src/main/java/com/xxl/job/core/thread/JobMonitorHelper.java +++ b/xxl-job-admin/src/main/java/com/xxl/job/core/thread/JobMonitorHelper.java @@ -41,6 +41,11 @@ public class JobMonitorHelper { XxlJobLog log = DynamicSchedulerUtil.xxlJobLogDao.load(jobLogId); if (log!=null) { if (RemoteCallBack.SUCCESS.equals(log.getTriggerStatus()) && StringUtils.isBlank(log.getHandleStatus())) { + try { + TimeUnit.SECONDS.sleep(10); + } catch (InterruptedException e) { + e.printStackTrace(); + } JobMonitorHelper.monitor(jobLogId); } if (RemoteCallBack.SUCCESS.equals(log.getTriggerStatus()) && RemoteCallBack.SUCCESS.equals(log.getHandleStatus())) { diff --git a/xxl-job-admin/src/main/java/com/xxl/job/service/job/RemoteHttpJobBean.java b/xxl-job-admin/src/main/java/com/xxl/job/service/job/RemoteHttpJobBean.java index cf0b0cb4..dab63033 100644 --- a/xxl-job-admin/src/main/java/com/xxl/job/service/job/RemoteHttpJobBean.java +++ b/xxl-job-admin/src/main/java/com/xxl/job/service/job/RemoteHttpJobBean.java @@ -55,6 +55,7 @@ public class RemoteHttpJobBean extends QuartzJobBean { // trigger request HashMap params = new HashMap(); + params.put(HandlerRepository.NAMESPACE, HandlerRepository.NameSpaceEnum.RUN.name()); params.put(HandlerRepository.TRIGGER_LOG_URL, PropertiesUtil.getString(HandlerRepository.TRIGGER_LOG_URL)); params.put(HandlerRepository.TRIGGER_LOG_ID, String.valueOf(jobLog.getId())); params.put(HandlerRepository.TRIGGER_TIMESTAMP, String.valueOf(System.currentTimeMillis())); diff --git a/xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobLogMapper.xml b/xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobLogMapper.xml index 5b0c6afa..c2dee0a7 100644 --- a/xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobLogMapper.xml +++ b/xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobLogMapper.xml @@ -29,7 +29,7 @@ t.job_cron, t.job_desc, t.job_class, - t.job_desc, + t.job_data, t.trigger_time, t.trigger_status, t.trigger_msg, diff --git a/xxl-job-admin/src/main/webapp/WEB-INF/template/jobinfo/index.ftl b/xxl-job-admin/src/main/webapp/WEB-INF/template/jobinfo/index.ftl index 3dec89c3..393a0147 100644 --- a/xxl-job-admin/src/main/webapp/WEB-INF/template/jobinfo/index.ftl +++ b/xxl-job-admin/src/main/webapp/WEB-INF/template/jobinfo/index.ftl @@ -67,8 +67,8 @@ id 任务组 任务名 - Cron 描述 + Cron JobBean 任务数据 新增时间 diff --git a/xxl-job-admin/src/main/webapp/WEB-INF/template/joblog/index.ftl b/xxl-job-admin/src/main/webapp/WEB-INF/template/joblog/index.ftl index ce55effa..80bf9dee 100644 --- a/xxl-job-admin/src/main/webapp/WEB-INF/template/joblog/index.ftl +++ b/xxl-job-admin/src/main/webapp/WEB-INF/template/joblog/index.ftl @@ -83,6 +83,7 @@ 执行时间 执行结果 执行日志 + 操作 diff --git a/xxl-job-admin/src/main/webapp/WEB-INF/template/joblog/logdetail.ftl b/xxl-job-admin/src/main/webapp/WEB-INF/template/joblog/logdetail.ftl new file mode 100644 index 00000000..bc2c90d8 --- /dev/null +++ b/xxl-job-admin/src/main/webapp/WEB-INF/template/joblog/logdetail.ftl @@ -0,0 +1,7 @@ + +
+
+<#if result.code == 200>${result.content} +<#else>${result.msg} +
+ diff --git a/xxl-job-admin/src/main/webapp/static/js/jobinfo.index.1.js b/xxl-job-admin/src/main/webapp/static/js/jobinfo.index.1.js index dcaadc7c..2cadde24 100644 --- a/xxl-job-admin/src/main/webapp/static/js/jobinfo.index.1.js +++ b/xxl-job-admin/src/main/webapp/static/js/jobinfo.index.1.js @@ -18,6 +18,7 @@ $(function() { { "data": 'id', "bSortable": false, "visible" : false}, { "data": 'jobGroup', + "visible" : false, "render": function ( data, type, row ) { var groupMenu = $("#jobGroup").find("option"); for ( var index in $("#jobGroup").find("option")) { @@ -29,14 +30,18 @@ $(function() { } }, { "data": 'jobName'}, + { "data": 'jobDesc', "visible" : true}, { "data": 'jobCron', "visible" : true}, - { "data": 'jobDesc', "visible" : false}, - { "data": 'jobClass', "visible" : true}, + { "data": 'jobClass', "visible" : false}, { "data": 'jobData', "visible" : true, "render": function ( data, type, row ) { - return data?'查看'+ data +'':"无"; + var _jobData = eval('(' + data + ')'); // row.jobData + var html = "

执行器:" + _jobData.handler_name + + "
执行参数:" + _jobData.handler_params + + "
执行机器:" + _jobData.handler_address + "

"; + return html; } }, { @@ -101,8 +106,8 @@ $(function() { ' handler_name="'+ jobDataMap.handler_name +'" '+ '>'+ pause_resume + - ' '+ - '
'+ + ''+ + '
'+ ' '+ ' '+ @@ -140,12 +145,6 @@ $(function() { } }); - // 日志弹框提示 - $('#job_list').on('click', '.logTips', function(){ - var msg = $(this).find('span').html(); - ComAlertTec.show(msg); - }); - // 搜索按钮 $('#searchBtn').on('click', function(){ jobTable.fnDraw(); diff --git a/xxl-job-admin/src/main/webapp/static/js/joblog.index.1.js b/xxl-job-admin/src/main/webapp/static/js/joblog.index.1.js index 447ba58d..2c248806 100644 --- a/xxl-job-admin/src/main/webapp/static/js/joblog.index.1.js +++ b/xxl-job-admin/src/main/webapp/static/js/joblog.index.1.js @@ -48,6 +48,7 @@ $(function() { { "data": 'id', "bSortable": false, "visible" : false}, { "data": 'jobGroup', + "visible" : false, "bSortable": false, "render": function ( data, type, row ) { var groupMenu = $("#jobGroup").find("option"); @@ -65,9 +66,14 @@ $(function() { { "data": 'jobClass', "visible" : false}, { "data": 'jobData', - "visible" : false, + "visible" : true, "render": function ( data, type, row ) { - return data?'查看'+ data +'':"无"; + var _jobData = eval('(' + data + ')'); // row.jobData + var html = "

执行器:" + _jobData.handler_name + + "
执行参数:" + _jobData.handler_params + + "
执行机器:" + _jobData.handler_address + "

"; + + return data?'查看'+ html +'':"无"; } }, { @@ -95,6 +101,27 @@ $(function() { "render": function ( data, type, row ) { return data?'查看'+ data +'':"无"; } + }, + { "data": 'handleMsg' , "bSortable": false, + "render": function ( data, type, row ) { + // better support expression or string, not function + return function () { + // local job do not support trigger detail log, now + var _jobData = eval('(' + row.jobData + ')'); + if (!_jobData.handler_address) { + return; + } + + if (row.triggerStatus == 'SUCCESS'){ + var temp = '查看日志'; + if(!row.handleStatus){ + temp += '
终止任务'; + } + return temp; + } + return null; + } + } } ], "language" : { @@ -123,17 +150,65 @@ $(function() { } }); + // 任务数据 + $('#joblog_list').on('click', '.logMsg', function(){ + var msg = $(this).find('span').html(); + ComAlert.show(2, msg); + }); + // 日志弹框提示 $('#joblog_list').on('click', '.logTips', function(){ var msg = $(this).find('span').html(); ComAlertTec.show(msg); }); - - // 搜索按钮 $('#searchBtn').on('click', function(){ logTable.fnDraw(); }); + // 查看执行器详细执行日志 + $('#joblog_list').on('click', '.logDetail', function(){ + var _id = $(this).attr('_id'); + + window.open(base_url + 'joblog/logDetailPage?id=' + _id); + return; + + /* + $.ajax({ + type : 'POST', + url : base_url + 'joblog/logDetail', + data : {"id":_id}, + dataType : "json", + success : function(data){ + if (data.code == 200) { + ComAlertTec.show('
'+ data.content +'
'); + } else { + ComAlertTec.show(data.msg); + } + }, + }); + */ + }); + + $('#joblog_list').on('click', '.logKill', function(){ + var _id = $(this).attr('_id'); + ComConfirm.show("确认主动终止任务?", function(){ + $.ajax({ + type : 'POST', + url : base_url + 'joblog/logKill', + data : {"id":_id}, + dataType : "json", + success : function(data){ + if (data.code == 200) { + ComAlert.show(1, '操作成功'); + logTable.fnDraw(); + } else { + ComAlert.show(2, data.msg); + } + }, + }); + }); + }); + }); diff --git a/xxl-job-client-demo/src/main/java/com/xxl/job/service/handler/DemoJobHandler.java b/xxl-job-client-demo/src/main/java/com/xxl/job/service/handler/DemoJobHandler.java index 135280c5..611596e6 100644 --- a/xxl-job-client-demo/src/main/java/com/xxl/job/service/handler/DemoJobHandler.java +++ b/xxl-job-client-demo/src/main/java/com/xxl/job/service/handler/DemoJobHandler.java @@ -1,6 +1,5 @@ package com.xxl.job.service.handler; -import java.util.Random; import java.util.concurrent.TimeUnit; import org.slf4j.Logger; @@ -25,7 +24,10 @@ public class DemoJobHandler extends IJobHandler { @Override public JobHandleStatus handle(String... params) throws Exception { logger.info(" ... params:" + params); - TimeUnit.SECONDS.sleep(new Random().nextInt(5)); + for (int i = 0; i < 60; i++) { + TimeUnit.SECONDS.sleep(1); + logger.info("handler run:{}", i); + } return JobHandleStatus.SUCCESS; } diff --git a/xxl-job-client-demo/src/main/resources/log4j.properties b/xxl-job-client-demo/src/main/resources/log4j.properties deleted file mode 100644 index a79ff48f..00000000 --- a/xxl-job-client-demo/src/main/resources/log4j.properties +++ /dev/null @@ -1,10 +0,0 @@ -log4j.rootLogger=info,console - -log4j.appender.console=org.apache.log4j.ConsoleAppender -log4j.appender.console.layout=org.apache.log4j.PatternLayout -log4j.appender.console.layout.ConversionPattern=%d - xxl-job-client-demo - %p [%c] - <%m>%n - -log4j.appender.logFile=org.apache.log4j.DailyRollingFileAppender -log4j.appender.logFile.File=${catalina.base}/logs/xxl-job-client-demo.log -log4j.appender.logFile.layout=org.apache.log4j.PatternLayout -log4j.appender.logFile.layout.ConversionPattern=%d - xxl-job-client-demo - %p [%c] - <%m>%n diff --git a/xxl-job-client-demo/src/main/resources/log4j.xml b/xxl-job-client-demo/src/main/resources/log4j.xml new file mode 100644 index 00000000..c5e21201 --- /dev/null +++ b/xxl-job-client-demo/src/main/resources/log4j.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/xxl-job-client/src/main/java/com/xxl/job/client/handler/HandlerRepository.java b/xxl-job-client/src/main/java/com/xxl/job/client/handler/HandlerRepository.java index 4fdfd0b4..ecd28b57 100644 --- a/xxl-job-client/src/main/java/com/xxl/job/client/handler/HandlerRepository.java +++ b/xxl-job-client/src/main/java/com/xxl/job/client/handler/HandlerRepository.java @@ -1,5 +1,6 @@ package com.xxl.job.client.handler; +import java.util.Date; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -7,6 +8,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.xxl.job.client.util.HttpUtil.RemoteCallBack; +import com.xxl.job.client.log.XxlJobFileAppender; import com.xxl.job.client.util.JacksonUtil; /** @@ -16,6 +18,9 @@ import com.xxl.job.client.util.JacksonUtil; public class HandlerRepository { private static Logger logger = LoggerFactory.getLogger(HandlerRepository.class); + public static final String NAMESPACE = "namespace"; + public enum NameSpaceEnum{RUN, KILL, LOG} + public static final String HANDLER_ADDRESS = "handler_address"; public static final String HANDLER_NAME = "handler_name"; public static final String HANDLER_PARAMS = "handler_params"; @@ -35,36 +40,96 @@ public class HandlerRepository { } // handler push to queue - public static String pushHandleQueue(Map _param) { - logger.info(">>>>>>>>>>> xxl-job pushHandleQueue start, _param:{}", new Object[]{_param}); + public static String service(Map _param) { + logger.info(">>>>>>>>>>> xxl-job service start, _param:{}", new Object[]{_param}); // callback RemoteCallBack callback = new RemoteCallBack(); callback.setStatus(RemoteCallBack.FAIL); - - // encryption check - long timestamp = _param.get(HandlerRepository.TRIGGER_TIMESTAMP)!=null?Long.valueOf(_param.get(HandlerRepository.TRIGGER_TIMESTAMP)):-1; - if (System.currentTimeMillis() - timestamp > 60000) { - callback.setMsg("Timestamp check failed."); + + // check namespace + String namespace = _param.get(HandlerRepository.NAMESPACE); + if (namespace==null || namespace.trim().length()==0) { + callback.setMsg("param[NAMESPACE] can not be null."); return JacksonUtil.writeValueAsString(callback); } - - // push data to queue - String handler_name = _param.get(HandlerRepository.HANDLER_NAME); - if (handler_name!=null && handler_name.trim().length()>0) { - HandlerThread handlerThread = handlerTreadMap.get(handler_name); - if (handlerThread != null) { - handlerThread.pushData(_param); - callback.setStatus(RemoteCallBack.SUCCESS); - } else { - callback.setMsg("handler[" + handler_name + "] not found."); + + // parse namespace + if (namespace.equals(HandlerRepository.NameSpaceEnum.RUN.name())) { + // encryption check + long timestamp = _param.get(HandlerRepository.TRIGGER_TIMESTAMP)!=null?Long.valueOf(_param.get(HandlerRepository.TRIGGER_TIMESTAMP)):-1; + if (System.currentTimeMillis() - timestamp > 60000) { + callback.setMsg("Timestamp check failed."); + return JacksonUtil.writeValueAsString(callback); + } + + // push data to queue + String handler_name = _param.get(HandlerRepository.HANDLER_NAME); + if (handler_name!=null && handler_name.trim().length()>0) { + HandlerThread handlerThread = handlerTreadMap.get(handler_name); + if (handlerThread != null) { + handlerThread.pushData(_param); + callback.setStatus(RemoteCallBack.SUCCESS); + } else { + callback.setMsg("handler[" + handler_name + "] not found."); + } + }else{ + callback.setMsg("param[HANDLER_NAME] can not be null."); + } + + } else if (namespace.equals(HandlerRepository.NameSpaceEnum.LOG.name())) { + String trigger_log_id = _param.get(HandlerRepository.TRIGGER_LOG_ID); + String trigger_timestamp = _param.get(HandlerRepository.TRIGGER_TIMESTAMP); + if (trigger_log_id==null || trigger_timestamp==null) { + callback.setMsg("trigger_log_id | trigger_timestamp can not be null."); + return JacksonUtil.writeValueAsString(callback); } - }else{ - callback.setMsg("param[HANDLER_NAME] can not be null."); + int logId = -1; + Date triggerDate = null; + try { + logId = Integer.valueOf(trigger_log_id); + triggerDate = new Date(Long.valueOf(trigger_timestamp)); + } catch (Exception e) { + } + if (logId<=0 || triggerDate==null) { + callback.setMsg("trigger_log_id | trigger_timestamp is not parsed valid."); + return JacksonUtil.writeValueAsString(callback); + } + String logConteng = XxlJobFileAppender.readLog(triggerDate, trigger_log_id); + callback.setStatus(RemoteCallBack.SUCCESS); + callback.setMsg(logConteng); + } else if (namespace.equals(HandlerRepository.NameSpaceEnum.KILL.name())) { + // encryption check + long timestamp = _param.get(HandlerRepository.TRIGGER_TIMESTAMP)!=null?Long.valueOf(_param.get(HandlerRepository.TRIGGER_TIMESTAMP)):-1; + if (System.currentTimeMillis() - timestamp > 60000) { + callback.setMsg("Timestamp check failed."); + return JacksonUtil.writeValueAsString(callback); + } + + // kill handlerThread, and create new one + String handler_name = _param.get(HandlerRepository.HANDLER_NAME); + if (handler_name!=null && handler_name.trim().length()>0) { + HandlerThread handlerThread = handlerTreadMap.get(handler_name); + if (handlerThread != null) { + IJobHandler handler = handlerThread.getHandler(); + handlerThread.toStop(); + handlerThread.interrupt(); + regist(handler_name, handler); + callback.setStatus(RemoteCallBack.SUCCESS); + } else { + callback.setMsg("handler[" + handler_name + "] not found."); + } + }else{ + callback.setMsg("param[HANDLER_NAME] can not be null."); + } + + } else { + callback.setMsg("param[NAMESPACE] is not valid."); + return JacksonUtil.writeValueAsString(callback); } - logger.info(">>>>>>>>>>> xxl-job pushHandleQueue end, triggerData:{}", new Object[]{callback}); - return JacksonUtil.writeValueAsString(callback); + logger.info(">>>>>>>>>>> xxl-job service end, triggerData:{}", new Object[]{callback}); + return JacksonUtil.writeValueAsString(callback); } } diff --git a/xxl-job-client/src/main/java/com/xxl/job/client/handler/HandlerThread.java b/xxl-job-client/src/main/java/com/xxl/job/client/handler/HandlerThread.java index aa8c401b..f8babda0 100644 --- a/xxl-job-client/src/main/java/com/xxl/job/client/handler/HandlerThread.java +++ b/xxl-job-client/src/main/java/com/xxl/job/client/handler/HandlerThread.java @@ -12,6 +12,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.xxl.job.client.handler.IJobHandler.JobHandleStatus; +import com.xxl.job.client.log.XxlJobFileAppender; import com.xxl.job.client.util.HttpUtil; import com.xxl.job.client.util.HttpUtil.RemoteCallBack; @@ -25,6 +26,7 @@ public class HandlerThread extends Thread{ private IJobHandler handler; private LinkedBlockingQueue> handlerDataQueue; private ConcurrentHashSet logIdSet; // avoid repeat trigger for the same TRIGGER_LOG_ID + private boolean toStop = false; public HandlerThread(IJobHandler handler) { this.handler = handler; @@ -32,6 +34,18 @@ public class HandlerThread extends Thread{ logIdSet = new ConcurrentHashSet(); } + public IJobHandler getHandler() { + return handler; + } + public void toStop() { + /** + * Thread.interrupt只支持终止线程的阻塞状态(wait、join、sleep), + * 在阻塞出抛出InterruptedException异常,但是并不会终止运行的线程本身; + * 所以需要注意,此处彻底销毁本线程,需要通过共享变量方式; + */ + this.toStop = true; + } + public void pushData(Map param) { if (param.get(HandlerRepository.TRIGGER_LOG_ID)!=null && !logIdSet.contains(param.get(HandlerRepository.TRIGGER_LOG_ID))) { handlerDataQueue.offer(param); @@ -41,7 +55,7 @@ public class HandlerThread extends Thread{ int i = 1; @Override public void run() { - while(true){ + while(!toStop){ try { Map handlerData = handlerDataQueue.poll(); if (handlerData!=null) { @@ -63,6 +77,7 @@ public class HandlerThread extends Thread{ JobHandleStatus _status = JobHandleStatus.FAIL; String _msg = null; try { + XxlJobFileAppender.contextHolder.set(trigger_log_id); _status = handler.handle(handlerParams); } catch (Exception e) { logger.info("HandlerThread Exception:", e); @@ -100,5 +115,6 @@ public class HandlerThread extends Thread{ logger.info("HandlerThread Exception:", e); } } + logger.info(">>>>>>>>>>>> xxl-job handlerThrad stoped, hashCode:{}", Thread.currentThread()); } } diff --git a/xxl-job-client/src/main/java/com/xxl/job/client/log/XxlJobFileAppender.java b/xxl-job-client/src/main/java/com/xxl/job/client/log/XxlJobFileAppender.java new file mode 100644 index 00000000..625f437f --- /dev/null +++ b/xxl-job-client/src/main/java/com/xxl/job/client/log/XxlJobFileAppender.java @@ -0,0 +1,180 @@ +package com.xxl.job.client.log; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.text.SimpleDateFormat; +import java.util.Date; + +import org.apache.log4j.AppenderSkeleton; +import org.apache.log4j.Layout; +import org.apache.log4j.spi.LoggingEvent; + +/** + * store trigger log in each log-file + * @author xuxueli 2016-3-12 19:25:12 + */ +public class XxlJobFileAppender extends AppenderSkeleton { + + // for HandlerThread + public static ThreadLocal contextHolder = new ThreadLocal(); + public static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); + + // trogger log file path + public static volatile String filePath; + public void setFilePath(String filePath) { + XxlJobFileAppender.filePath = filePath; + } + + @Override + protected void append(LoggingEvent event) { + String trigger_log_id = contextHolder.get(); + if (trigger_log_id==null || trigger_log_id.trim().length()==0) { + return; + } + + // filePath/ + File filePathDir = new File(filePath); + if (!filePathDir.exists()) { + filePathDir.mkdirs(); + } + + // filePath/yyyy-MM-dd/ + String nowFormat = sdf.format(new Date()); + File filePathDateDir = new File(filePathDir, nowFormat); + if (!filePathDateDir.exists()) { + filePathDateDir.mkdirs(); + } + + // filePath/yyyy-MM-dd/9999.log + String logFileName = trigger_log_id.concat(".log"); + File logFile = new File(filePathDateDir, logFileName); + if (!logFile.exists()) { + try { + logFile.createNewFile(); + } catch (IOException e) { + e.printStackTrace(); + return; + } + } + + // append file content + try { + FileOutputStream fos = null; + try { + fos = new FileOutputStream(logFile, true); + fos.write(layout.format(event).getBytes("utf-8")); + if (layout.ignoresThrowable()) { + String[] throwableInfo = event.getThrowableStrRep(); + if (throwableInfo != null) { + for (int i = 0; i < throwableInfo.length; i++) { + fos.write(throwableInfo[i].getBytes("utf-8")); + fos.write(Layout.LINE_SEP.getBytes("utf-8")); + } + } + } + fos.flush(); + } finally { + if (fos != null) { + try { + fos.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } catch (Exception e) { + e.printStackTrace(); + } + + } + + @Override + public void close() { + // TODO Auto-generated method stub + + } + + @Override + public boolean requiresLayout() { + // TODO Auto-generated method stub + return false; + } + + /** + * support read log-file + * @param triggerDate + * @param trigger_log_id + * @return + */ + public static String readLog(Date triggerDate, String trigger_log_id ){ + if (triggerDate==null || trigger_log_id==null || trigger_log_id.trim().length()==0) { + return null; + } + + // filePath/ + File filePathDir = new File(filePath); + if (!filePathDir.exists()) { + filePathDir.mkdirs(); + } + + // filePath/yyyy-MM-dd/ + String nowFormat = sdf.format(triggerDate); + File filePathDateDir = new File(filePathDir, nowFormat); + if (!filePathDateDir.exists()) { + filePathDateDir.mkdirs(); + } + + // filePath/yyyy-MM-dd/9999.log + String logFileName = trigger_log_id.concat(".log"); + File logFile = new File(filePathDateDir, logFileName); + if (!logFile.exists()) { + try { + logFile.createNewFile(); + } catch (IOException e) { + e.printStackTrace(); + return null; + } + } + + try { + InputStream ins = null; + BufferedReader reader = null; + try { + ins = new FileInputStream(logFile); + reader = new BufferedReader(new InputStreamReader(ins, "utf-8")); + if (reader != null) { + String content = null; + StringBuilder sb = new StringBuilder(); + while ((content = reader.readLine()) != null) { + sb.append(content).append("\n"); + } + return sb.toString(); + } + } finally { + if (ins != null) { + try { + ins.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + if (reader != null) { + try { + reader.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + +} diff --git a/xxl-job-client/src/main/java/com/xxl/job/client/netcom/jetty/XxlJobJettyServerHandler.java b/xxl-job-client/src/main/java/com/xxl/job/client/netcom/jetty/XxlJobJettyServerHandler.java index 82bc48a9..c223ce54 100644 --- a/xxl-job-client/src/main/java/com/xxl/job/client/netcom/jetty/XxlJobJettyServerHandler.java +++ b/xxl-job-client/src/main/java/com/xxl/job/client/netcom/jetty/XxlJobJettyServerHandler.java @@ -32,7 +32,7 @@ public class XxlJobJettyServerHandler extends AbstractHandler { } } - String resp = HandlerRepository.pushHandleQueue(_param); + String resp = HandlerRepository.service(_param); httpServletResponse.setContentType("text/html;charset=utf-8"); httpServletResponse.setStatus(HttpServletResponse.SC_OK); diff --git a/xxl-job-client/src/main/java/com/xxl/job/client/netcom/servlet/XxlJobServlet.java b/xxl-job-client/src/main/java/com/xxl/job/client/netcom/servlet/XxlJobServlet.java index 4ea7264c..47f42df1 100644 --- a/xxl-job-client/src/main/java/com/xxl/job/client/netcom/servlet/XxlJobServlet.java +++ b/xxl-job-client/src/main/java/com/xxl/job/client/netcom/servlet/XxlJobServlet.java @@ -44,7 +44,7 @@ public class XxlJobServlet extends HttpServlet { } } - String resp = HandlerRepository.pushHandleQueue(_param); + String resp = HandlerRepository.service(_param); response.getWriter().append(resp); return; }