摘要:日志实时监控+上传
关键字:common-io/monitor包,生产消费模式,非阻塞队列,钩子
看点:
1.如何监听文件改变
common-io提供minitor工具,可以监控文件的改变
File directory = new File(logPath); long interval = TimeUnit.SECONDS.toMillis(3); FileFilter fileFilter = FileFilterUtils.or(FileFilterUtils.nameFileFilter("async_task.log"), FileFilterUtils.nameFileFilter("error.log"), FileFilterUtils.nameFileFilter("moviebar_local.log"), FileFilterUtils.nameFileFilter("push-message.log")); FileAlterationObserver observer = new FileAlterationObserver(directory, fileFilter); FileIncreListener listener = new FileIncreListener(); observer.addListener(listener); initReadMark(directory.listFiles(fileFilter), listener); FileAlterationMonitor monitor = new FileAlterationMonitor(interval,observer); monitor.start();
2.文件改变后做什么
文件改变后应该获取最新的改变内容,所以需要记录上次读完后的内容偏移量offset
public void onFileChange(File file) { InputStream inputStream = null; ByteArrayOutputStream outputStream = null; try { String filepath = file.getPath(); Long readOffset = readMark.get(filepath); if (readOffset == null) { readMark.put(filepath, file.length()); } else { long fileLength = file.length(); //兼容文件内容减少的情况 if(readOffset > fileLength) { readOffset = fileLength; return; } long readLength = fileLength - readOffset; inputStream = new FileInputStream(file); outputStream = new ByteArrayOutputStream(); IOUtils.copyLarge(inputStream, outputStream, readOffset, readLength); readMark.put(filepath, file.length()); byte[] increByte = outputStream.toByteArray(); if(increByte.length >= 1024 * 1024 * 1){ return; } String increCon = new String(increByte); Map<String, String> params = new HashMap<>(); LocalBar bar = ((BarCache)ApplicationContextUtil.getBean("barCache")).get(); if(bar != null){ params.put("barId", String.valueOf(bar.getId())); params.put("barName", bar.getName()); } params.put("filename", file.getName()); params.put("increCon", increCon); LogUpdProcessor.offer(params); } }catch (Exception e) { ; }finally { IOUtils.closeQuietly(outputStream); IOUtils.closeQuietly(inputStream); }
3.监听改变与处理日志的解耦
监听到日志马上就处理最新的内容吗?如果处理工作阻塞了怎么办?使用队列解耦!采用生产者、消费者模式!!!
public class LogUpdProcessor implements Runnable { private static ConcurrentLinkedQueue<Map> logMessageQueue = new ConcurrentLinkedQueue<Map>(); @Resource private Environment env; public static boolean offer(Map map) { if(logMessageQueue.size() >= 1000){ logMessageQueue.poll(); } if(!Constants.UOLOAD_LOG_SWITCH){ return false; } return logMessageQueue.offer(map); } public static Map poll() { return logMessageQueue.poll(); } public static int queueSize(){ return logMessageQueue.size(); } @Override public void run() { while(true) { try{ if(!Constants.UOLOAD_LOG_SWITCH) { Thread.sleep(2000); continue; } Map params = logMessageQueue.poll(); if(params == null){ try { Thread.sleep(2000); } catch (InterruptedException e) { ; } }else{ SecurityClientUtils.getInstance().exePostFormMethod(env.getProperty("url_cloud_service") + "/api/log/instantCollect", params); } }catch (Exception e){ ; } } } }4.监控程序对监控目标保持静默
这个监控过程需要对监控对象的日志保持静默,不产生任何输出,否则就会造成递归死循环的场景:日志改变-监控捕获-处理-产生日志-日志改变,所以任何log,catch都不输出
5.对offset的维护(初始化,新建,增加,减少)
当程序启动时,offset初始为null,要二次更新时才能获取更新内容,存在消息丢失的情况!
故而当初始化时,offset置为file.length(),日志创建时同理
当日志减少时,offset小于length,会有读异常,兼容下
6.上传的开关与上传大小控制
上传增加开关,可控制
上传size做下限制,不能无限量上传
7.jvm退出前清扫队列(钩子)
为了保证消息不丢失,在jvm退出前clean日志队列
Runtime.getRuntime().addShutdownHook(cleanLogThread);
8.云端简述
服务器端,同样可以采取生产者、消费者模式,消息到达时入队列,有其他线程完成日志后续处理工作
可提高服务器端吞吐能力