当前位置:网站首页>xxl-job中 关于所有日志系统的源码的解读(一行一行源码解读)
xxl-job中 关于所有日志系统的源码的解读(一行一行源码解读)
2022-07-25 22:13:00 【一天不写代码难受】
1 寻找日志相关文件
xxl-job 中,什么地方会使用日志,就是在各个执行过程中,会记录日志,在服务端执行错误会保存日志,还有我们打开页面,查看日志的时候,会调用接口,从后台查看日志信息
- 首先是记录日志的相关代码文件

我们在使用xxl-job记录日志时只需要在任务执行过程中使用XxlJobHelper.log()即可,方法和log4j/slf4j一样简单。
你写代码,想记录日志,那么直接写
XxlJobHelper.log("hello world");
以上这个代码背后就会 先进行日志内容的格式化,也就是将乱七八糟的日志信息格式化的好看一些,或者变成实体类,之后的话,将格式化之后的日志信息 保存为文件
- 客户端 要实时查看日志的接口
我们打开任务调度中心的项目,要实时查看某一个执行任务的日志,点击日志信息,就会调用接口
前端调用这个方法 进行调用查看
logDetailCat()


当执行状态属于未完成的情况下,xxl-job日志控制台会循环调用该接口直至任务完成。
2 保存日志相关代码文件
核心源码里面 ,就涉及到这些文件

我们在自己的项目里面,记录日志,使用的是
XxlJobHelper.log(“hello world”);
所以就从这个方法开始,这个方法所在的文件是

进去找到这个方法,有两个同名方法,一个是我们普通的记录日志的,一个是对异常进行记录日志的,就是在catch里面进行记录日志的

首先看对异常进行记录日志的,一般就是在catch里面进行记录日志
/** * 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);
}
之后看 记录普通信息 的日志信息
/** * 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是具体的日志信息
/** * 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);
进行保存
就去了这个文件里面了
/** * 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 += "\r\n";
// 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 服务端实时调用日志信息

/** * 当我们后台打开任务日志时,服务端会到客户端来拉取日志 * @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());
}
}
边栏推荐
- Redis为何选择单线程?
- Can I buy financial products with a revenue of more than 6% after opening an account
- 数据库进阶·如何针对所有用户数据中没有的数据去加入随机的数据-蜻蜓Q系统用户没有头像如何加入头像数据-优雅草科技kir
- Minor GC 和 Full GC 有什么不同呢?
- Redis是什么?简述它的优缺点
- Children's programming electronic society graphical programming level examination scratch level 1 real problem analysis (judgment question) June 2022
- Detailed summary of C language game dual cache to solve the flash screen problem [easy to understand]
- JS timer and swiper plug-in
- jenkins+SVN配置
- Unity performance optimization direction
猜你喜欢

信息安全建设原则指导

3. Editors (vim)

面了个腾讯三年经验的测试员,让我见识到了真正的测试天花板

Wechat applet application development competition works comprehensive development record - Jinlu cultural tourism (cloud development - Overview)

在腾讯干软件测试3年,7月无情被辞,想给划水的兄弟提个醒...

How to resolve a domain name to multiple IP addresses?

『Skywalking』. Net core fast access distributed link tracking platform

TFrecord写入与读取

TS:typora代码片段缩进显示异常(已解决)-2022.7.24
![[go basics 02] the first procedure](/img/af/f32762a828f384bf6aa063ebf959aa.png)
[go basics 02] the first procedure
随机推荐
Which is reliable between qiniu business school and WeiMiao business school? Is it safe to open an account recommended by the teacher?
TS:typora代码片段缩进显示异常(已解决)-2022.7.24
Ts:typera code fragment indentation display exception (resolved) -2022.7.24
【C语法】void*浅说
[go basics 02] the first procedure
2 lines of code to generate a solid desktop background
Jmeter--- set proxy recording request
【GO基础02】第一个程序
Synchronized and volatile
Internship: writing common tool classes
Some summary about function
如何实现一个App应用程序,限制用户时间使用?
After three years of software testing at Tencent, I was ruthlessly dismissed in July, trying to wake up my brother who was paddling
C语言:随机生成数+冒泡排序
[test development methodology] experience of test development platform PK - choice
c sqlite ... ...
Golang: MVC models
MySQL - subquery - column subquery (multi row subquery)
微信发卡小程序源码-自动发卡小程序源码-带流量主功能
Solutions to the failure of win key in ikbc keyboard