[XXL job source code 03] interpretation of XXL job log system source code

Reading guide

XXL job not only has its own rpc, registration center, but also its own log system. Although a sparrow is small, it has all kinds of internal organs.

This chapter will analyze the logging system of XXL job from the source code level. When using XXL job to record logs, we only need to use xxljobhelper during task execution Log (), which is as simple as log4j/slf4j. We can see the real-time log of the task running through the console.

XxlJobHelper.log("hello world");

This is too advanced. The log can be accurate to the task level, and it can also be viewed in real time on the console. There are scheduling methods and class information. So how does it achieve? Next, I will take you to analyze the implementation of XXL job.

Source code analysis

This time, let's directly push the log system logic forward and locate the log method

com.xxl.job.core.context.XxlJobHelper#log(java.lang.String, java.lang.Object...)

public static boolean log(String appendLogPattern, Object ... appendLogArguments) {
    // Use slf4j parser to format log content
    FormattingTuple ft = MessageFormatter.arrayFormat(appendLogPattern, appendLogArguments);
    String appendLog = ft.getMessage();
    // Get stack frame information
    StackTraceElement callInfo = new Throwable().getStackTrace()[1];
    return logDetail(callInfo, appendLog);
}

Note that stacktraceelement callinfo = new throwable() getStackTrace()[1];

This is to obtain the calling stack frame method. Index 0 is the current stack frame, 1 is the calling stack frame, and so on. Index 1 is obtained here, that is, the stack frame information of calling the method is obtained. The calling class name, method name, line number and other information can be obtained through StackTraceElement

Continue to com Xxl job. core. context. XxlJobHelper#logDetail

private static boolean logDetail(StackTraceElement callInfo, String appendLog) {
    // Gets the current context object
    XxlJobContext xxlJobContext = XxlJobContext.getXxlJobContext();
    if (xxlJobContext == null) {
        return false;
    }
    // Splice format log information
    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();
    // Get log file path
    String logFileName = xxlJobContext.getJobLogFileName();
    if (logFileName!=null && logFileName.trim().length()>0) {
        // The log is written to the local file in the form of stream
        XxlJobFileAppender.appendLog(logFileName, formatAppendLog);
        return true;
    } else {
        logger.info(">>>>>>>>>>> {}", formatAppendLog);
        return false;
    }
}

Here is a key piece of code

XxlJobContext xxlJobContext = XxlJobContext.getXxlJobContext();

This is the context object of the current task, which will be initialized when it is scheduled, as I mentioned earlier[ [XXL job source code 01] XXL job source code interpretation magical time wheel trigger process interpretation ]Yes, it's not here

We can see that XXL job writes logs to local files and does not push them to the server. Here, the log is not in the push mode, but in the pull mode. When we open the task log in the background, the server will pull the log from the client.

Navigate to interface com Xxl job. admin. controller. JobLogController#logDetailCat

@RequestMapping("/logDetailCat")
@ResponseBody
public ReturnT<LogResult> logDetailCat(String executorAddress, long triggerTime, long logId, int fromLineNum){
   try {
      // Get the remote instance of the executor
      ExecutorBiz executorBiz = XxlJobScheduler.getExecutorBiz(executorAddress);
      // Call the client netty http interface to pull logs
      ReturnT<LogResult> logResult = executorBiz.log(new LogParam(triggerTime, logId, fromLineNum));
      // Judge whether the log ends
           if (logResult.getContent()!=null && logResult.getContent().getFromLineNum() > logResult.getContent().getToLineNum()) {
               XxlJobLog jobLog = xxlJobLogDao.load(logId);
               if (jobLog.getHandleCode() > 0) {
                   logResult.getContent().setEnd(true);
               }
           }
      return logResult;
   } catch (Exception e) {
      logger.error(e.getMessage(), e);
      return new ReturnT<LogResult>(ReturnT.FAIL_CODE, e.getMessage());
   }
}

When the execution status is incomplete, the XXL job log console will call the interface repeatedly until the task is completed.

Next, let's take a look at the code related to the log file read by the client. Com Xxl job. core. biz. impl. ExecutorBizImpl#log

@Override
public ReturnT<LogResult> log(LogParam logParam) {
    // log filename: logPath/yyyy-MM-dd/9999.log
    // Get log file name
    String logFileName = XxlJobFileAppender.makeLogFileName(new Date(logParam.getLogDateTim()), logParam.getLogId());
    // Read stream information
    LogResult logResult = XxlJobFileAppender.readLog(logFileName, logParam.getFromLineNum());
    return new ReturnT<LogResult>(logResult);
}

com.xxl.job.core.log.XxlJobFileAppender#readLog

public static LogResult readLog(String logFileName, int fromLineNum){
   if (logFileName==null || logFileName.trim().length()==0) {
           return new LogResult(fromLineNum, 0, "readLog fail, logFile not found", true);
   }
   File logFile = new File(logFileName);
   if (!logFile.exists()) {
           return new LogResult(fromLineNum, 0, "readLog fail, logFile not exists", true);
   }
   // read file
   StringBuffer logContentBuffer = new StringBuffer();
   int toLineNum = 0;
   LineNumberReader reader = null;
   try {
      reader = new LineNumberReader(new InputStreamReader(new FileInputStream(logFile), "utf-8"));
      String line = null;
      while ((line = reader.readLine())!=null) {
         toLineNum = reader.getLineNumber();       // [from, to], start as 1
         if (toLineNum >= fromLineNum) {
            logContentBuffer.append(line).append("\n");
         }
      }
   } catch (IOException e) {
      logger.error(e.getMessage(), e);
   } finally {
      if (reader != null) {
         try {
            reader.close();
         } catch (IOException e) {
            logger.error(e.getMessage(), e);
         }
      }
   }
   LogResult logResult = new LogResult(fromLineNum, toLineNum, logContentBuffer.toString(), false);
   return logResult;
}

The XXL job client will save the log file to the disk. When the log console is opened, the server will request the client circularly. The client will read the log with the specified number of lines in the form of io stream and return it to the server

link

[XXL job source code 01] XXL job source code interpretation magical time wheel trigger process interpretation

[XXL job source code 02] the application of RPC netty developed by the registry?

Tags: Java xxl-job

Posted by gavinr98 on Thu, 14 Apr 2022 11:37:32 +0930