目录
- 1 寻找日志相关文件
- 2 保存日志相关代码文件
- 3 服务端实时调用日志信息
1 寻找日志相关文件
xxl-job 中,什么地方会使用日志,就是在各个执行过程中,会记录日志,在服务端执行错误会保存日志,还有我们打开页面,查看日志的时候,会调用接口,从后台查看日志信息
- 首先是记录日志的相关代码文件
我们在使用xxl-job记录日志时只需要在任务执行过程中使用XxlJobHelper.log()即可,方法和log4j/slf4j一样简单。
你写代码,想记录日志,那么直接写
代码语言:javascript复制XxlJobHelper.log("hello world");
以上这个代码背后就会 先进行日志内容的格式化,也就是将乱七八糟的日志信息格式化的好看一些,或者变成实体类,之后的话,将格式化之后的日志信息 保存为文件
- 客户端 要实时查看日志的接口
我们打开任务调度中心的项目,要实时查看某一个执行任务的日志,点击日志信息,就会调用接口
代码语言:javascript复制前端调用这个方法 进行调用查看
logDetailCat()
当执行状态属于未完成的情况下,xxl-job日志控制台会循环调用该接口直至任务完成。
2 保存日志相关代码文件
核心源码里面 ,就涉及到这些文件
我们在自己的项目里面,记录日志,使用的是 XxlJobHelper.log(“hello world”);
所以就从这个方法开始,这个方法所在的文件是
进去找到这个方法,有两个同名方法,一个是我们普通的记录日志的,一个是对异常进行记录日志的,就是在catch里面进行记录日志的
首先看对异常进行记录日志的,一般就是在catch里面进行记录日志
代码语言:javascript复制 /**
* append exception stack
* 添加异常堆栈
* @param e
*/
public static boolean log(Throwable e) {
StringWriter stringWriter = new StringWriter();
e.printStackTrace(new PrintWriter(stringWriter));
// 将异常变成 字符串
String appendLog = stringWriter.toString();
// 获取调用这个log方法的 类方法的所有信息
StackTraceElement callInfo = new Throwable().getStackTrace()[1];
// 最后调用 另一个方法进行保存
return logDetail(callInfo, appendLog);
}
之后看 记录普通信息 的日志信息
代码语言:javascript复制 /**
* append log with pattern
* 用模式追加日志
* @param appendLogPattern like "aaa {} bbb {} ccc"
* @param appendLogArguments like "111, true" String[] str2={"rrrr","yyyyy"};
*/
public static boolean log(String appendLogPattern, Object ... appendLogArguments) {
// 使用slf4j解析器格式化日志内容
FormattingTuple ft = MessageFormatter.arrayFormat(appendLogPattern, appendLogArguments);
String appendLog = ft.getMessage(); // aaa rrrr bbb yyyyy ccc
/*appendLog = appendLogPattern;
if (appendLogArguments!=null && appendLogArguments.length>0) {
appendLog = MessageFormat.format(appendLogPattern, appendLogArguments);
}*/
// 获得栈帧信息
// 这是获得调用栈帧方法,索引0为当前栈帧,
// 1为调用栈帧,以此类推,此处获得的是索引1,
// 也就是说获得的是调用该方法的栈帧信息,
// 可以通过StackTraceElement获得调用类名,方法名,行数等信息
StackTraceElement callInfo = new Throwable().getStackTrace()[1];
// 记录 日志
return logDetail(callInfo, appendLog);
}
以上的两个log()结尾都调用了 logDetail(callInfo, appendLog) 参数callInfo 是调用方的所有信息,appendLog是具体的日志信息
代码语言:javascript复制 /**
* append log
* 追加 日志
* @param callInfo 哪个方法调用这个log方法,就把哪个方法的全部的信息 保存到StackTraceElement里
* @param appendLog 我们要记录的日志
*/
private static boolean logDetail(StackTraceElement callInfo, String appendLog) {
// 获得当前上下文对象
XxlJobContext xxlJobContext = XxlJobContext.getXxlJobContext();
if (xxlJobContext == null) {
return false;
}
/*// "yyyy-MM-dd HH:mm:ss [ClassName]-[MethodName]-[LineNumber]-[ThreadName] log";
StackTraceElement[] stackTraceElements = new Throwable().getStackTrace();
StackTraceElement callInfo = stackTraceElements[1];*/
// 拼接格式化日志信息
// 就是拼接 哪个方法记录了哪个 日志
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append(DateUtil.formatDateTime(new Date())).append(" ")
.append("[" callInfo.getClassName() "#" callInfo.getMethodName() "]").append("-")
.append("[" callInfo.getLineNumber() "]").append("-")
.append("[" Thread.currentThread().getName() "]").append(" ")
.append(appendLog!=null?appendLog:"");
// 最后的拼接的日志信息 里面包含 哪个方法记录哪个日志
String formatAppendLog = stringBuffer.toString();
// appendlog // 获得日志文件路径
String logFileName = xxlJobContext.getJobLogFileName();
if (logFileName!=null && logFileName.trim().length()>0) {
// 流的形式将日志写入本地文件
// 根据日志文件路径 ,将拼接的东西写进去
XxlJobFileAppender.appendLog(logFileName, formatAppendLog);
return true;
} else {
logger.info(">>>>>>>>>>> {}", formatAppendLog);
return false;
}
}
以上代码的意思是;对传进来的日志信息做一定的格式化处理之后,调用 XxlJobFileAppender.appendLog(logFileName, formatAppendLog);
进行保存
就去了这个文件里面了
代码语言:javascript复制 /**
* append log
* 添加日志内容
* @param logFileName 日志路径
* @param appendLog 日志内容
*/
public static void appendLog(String logFileName, String appendLog) {
// log file
if (logFileName==null || logFileName.trim().length()==0) {
return;
}
File logFile = new File(logFileName);
if (!logFile.exists()) {
try {
logFile.createNewFile();
} catch (IOException e) {
logger.error(e.getMessage(), e);
return;
}
}
// log
if (appendLog == null) {
appendLog = "";
}
appendLog = "rn";
// append file content
FileOutputStream fos = null;
try {
fos = new FileOutputStream(logFile, true);
fos.write(appendLog.getBytes("utf-8"));
fos.flush();
} catch (Exception e) {
logger.error(e.getMessage(), e);
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
logger.error(e.getMessage(), e);
}
}
}
}
以上代码就是 保存到具体的文件里面 了
3 服务端实时调用日志信息
代码语言:javascript复制/**
* 当我们后台打开任务日志时,服务端会到客户端来拉取日志
* @author xuxueli 2015-12-19 16:13:16
* 服务器 地址
* 触发时间
* 任务id
* 从第几行开始读取
*
* 当执行状态属于未完成的情况下,xxl-job日志控制台会循环调用该接口直至任务完成。
*/
@RequestMapping("/logDetailCat")
@ResponseBody
public ReturnT<LogResult> logDetailCat(String executorAddress, long triggerTime, long logId, int fromLineNum){
try {
// 根据 地址 创建远程调用对象
ExecutorBiz executorBiz = XxlJobScheduler.getExecutorBiz(executorAddress);
// 读取到 日志信息
ReturnT<LogResult> logResult = executorBiz.log(new LogParam(triggerTime, logId, fromLineNum));
// is end 判断日志是否结束
if (logResult.getContent()!=null && logResult.getContent().getFromLineNum() > logResult.getContent().getToLineNum()) {
// 根据日志id 从数据库获取日志信息
XxlJobLog jobLog = xxlJobLogDao.load(logId);
// 如果 执行状态 大于0
if (jobLog.getHandleCode() > 0) {
// 设置 结果 为 TRUE
logResult.getContent().setEnd(true);
}
}
return logResult;
} catch (Exception e) {
logger.error(e.getMessage(), e);
return new ReturnT<LogResult>(ReturnT.FAIL_CODE, e.getMessage());
}
}