syslog日志系统——日志采集
摘自:https://blog.csdn.net/hunkcai/article/details/83859172
日志采集的流程
客户端埋点
客户端埋点就是在客户系统植入日志发送的代码,可以是前端页面、app界面、后端服务等,把需要的日志信息发送到指定的日志采集接口。
日志的发送应该采用异步方式,这样不会对客户系统代码的执行造成影响。
日志采集
日志采集接口把接收过来的日志数据写入到日志文件,日志文件以天为单位进行存储。
日志采集接口代码
接口采用@IgnoreToken标记不需要进行令牌校验。
采用@CrossOrigin标记,让接口能够跨域访问,支持AJAX跨域请求。
@ApiOperation(value = "提交日志") @ApiImplicitParams({ @ApiImplicitParam(name = "logData", value = "日志数据", dataType = "LogData", paramType = "body") }) @RequestMapping(path = "/log/add", method = RequestMethod.POST, produces = "application/json;charset=UTF-8") @IgnoreToken @CrossOrigin public ResponseData addLog(@RequestBody LogData logData) { String logID = logService.logToFile(logData); return ResponseData.success(logID); }
日志文件格式的设计
日志以文本格式保存,每条日志按行写入。
日志行的格式如下:
时间戳|日志ID|日志数据JSON
日志按行保存的好处是,可以利用BufferedReader的readLine方法进行日志的按行读取。
日志按行保存,要对日志数据JSON部分进行处理,把换行符替换掉。
日志按行保存的主要代码
public String logToFile(LogData logData){ LogGroupData groupData = getLogGroup(logData.getKey()); logData.setGroupID(groupData.getGroupID()); String date = ConvertUtil.formatDate(new Date(), "yyyyMMdd"); String path = PathUtil.combine(logFolder, "LOG" + date + ".log"); String uuid = UUID.randomUUID().toString(); String json = JSON.toJSONString(logData); StringBuilder sb = new StringBuilder(); sb.append("[" + ConvertUtil.formatDate(new Date()) + "]|"); sb.append(uuid + "|"); for (int i = 0; i < json.length(); i++) { char c = json.charAt(i); if (c == '\r' || c == '\n')//换行符替换为空格 sb.append(" "); else sb.append(c); } FileUtil.appendLine(path, sb.toString()); return uuid; }
日志写入文件的主要代码
利用RandomAccessFile类写入,用文件琐控制并发。如果是海量日志的场合,可以部署多几个采集点。
public static void appendLine(String path, String line) { RandomAccessFile fout = null; FileChannel channel = null; try { File file = new File(path); if (!file.getParentFile().exists()) { file.getParentFile().mkdirs(); } byte[] bytes = line.getBytes("utf-8"); fout = new RandomAccessFile(file, "rw"); long filelength = fout.length();//获取文件的长度 fout.seek(filelength);//将文件的读写指针定位到文件的末尾 channel = fout.getChannel();//打开文件通道 FileLock fileLock = null; while (true) { try { fileLock = channel.tryLock();//不断的请求锁,如果请求不到,等一秒再请求 break; } catch (Exception e) { logger.error("[FileUtil.appendLine]请求琐失败。"); Thread.sleep(1000); } } fout.write(bytes); fout.writeChar('\n'); fileLock.release(); channel.close(); fout.close(); } catch (Exception e1) { logger.error(e1.getMessage(), e1); } finally { IOUtil.close(channel); IOUtil.close(fout); } }
日志汇总
通过定时任务,把各个采集点的日志文本导入数据库/大数据存储。