//获得需要进行输出的缓存列表 ArrayList<StringBuffer> alWrtLog = null; synchronized(lfi){ if(lfi.currLogBuff == 'A'){ alWrtLog = lfi.alLogBufA; lfi.currLogBuff = 'B'; }else{ alWrtLog = lfi.alLogBufB; lfi.currLogBuff = 'A'; } lfi.currCacheSize = 0; } //创建日志文件 createLogFile(lfi); //输出日志 int iWriteSize = writeToFile(lfi.fullLogFileName,alWrtLog); lfi.currLogSize += iWriteSize;
多刷盘机制FLogger 支持多种刷盘机制:
下面就来一一分析。
刷盘时间间隔触发配置项如下:
# 日志写入文件的间隔时间(默认为1000毫秒) WRITE_LOG_INV_TIME = 1000
当距上次刷盘时间超过间隔时间,将执行内存日志刷盘。
内存缓冲大小触发配置项如下:
# 单个日志文件缓存的大小(默认为10KB) SINGLE_LOG_CACHE_SIZE = 10240
当内存缓冲队列的大小超过配置大小时,将执行内存日志刷盘。
退出强制触发FLogger 内部注册了 JVM 关闭钩子 ShutdownHook ,当 JVM 正常关闭时,由钩子触发强制刷盘,避免内存日志丢失。相关代码如下:
public FLogger(){ Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { @Override public void run() { close(); } })); }
当 JVM 异常退出时无法保证内存中的日志全部落盘,但可以通过一种妥协的方式来提高日志刷盘的实时度:设置 SINGLE_LOG_CACHE_SIZE = 0 或者 WRITE_LOG_INV_TIME = 0 。
刷盘代码如下:
/** 线程方法 */ public void run(){ int i = 0 ; while(bIsRun){ try{ //输出到文件 flush(false); //重新获取日志级别 if(i++ % 100 == 0){ Constant.CFG_LOG_LEVEL = CommUtil.getConfigByString("LOG_LEVEL","0,1,2,3,4"); i = 1; } }catch(Exception e){ System.out.println("开启日志服务错误..."); e.printStackTrace(); } } } /** 关闭方法 */ public void close(){ bIsRun = false; try{ flush(true); }catch(Exception e){ System.out.println("关闭日志服务错误..."); e.printStackTrace(); } } /** * 输出缓存的日志到文件 * @param bIsForce 是否强制将缓存中的日志输出到文件 */ private void flush(boolean bIsForce) throws IOException{ long currTime = System.currentTimeMillis(); Iterator<String> iter = logFileMap.keySet().iterator(); while(iter.hasNext()){ LogFileItem lfi = logFileMap.get(iter.next()); if(currTime >= lfi.nextWriteTime || SINGLE_LOG_CACHE_SIZE <= lfi.currCacheSize || bIsForce == true){ //获得需要进行输出的缓存列表 ArrayList<StringBuffer> alWrtLog = null; synchronized(lfi){ if(lfi.currLogBuff == 'A'){ alWrtLog = lfi.alLogBufA; lfi.currLogBuff = 'B'; }else{ alWrtLog = lfi.alLogBufB; lfi.currLogBuff = 'A'; } lfi.currCacheSize = 0; } //创建日志文件 createLogFile(lfi); //输出日志 int iWriteSize = writeToFile(lfi.fullLogFileName,alWrtLog); lfi.currLogSize += iWriteSize; } } }
多 RollingFile 机制同 log4j/logback,FLogger 也支持多种 RollingFile 机制:
其中按文件大小 Rolling,配置项为:
# 单个日志文件的大小(默认为10M) SINGLE_LOG_FILE_SIZE = 10485760
即当文件大小超过配置大小时,将创建新的文件记录日志,同时重命名旧文件为"日志文件名_日期_时间.log"(如 info_20161208_011105.log)。
按天 Rolling 即每天产生不同的文件。
产生的日志文件列表可参考如下:
info_20161207_101105.log info_20161207_122010.log info_20161208_011110.log info_20161208_015010.log info.log
当前正在写入的日志文件为 info.log。
关键代码如下:
/** * 创建日志文件 * @param lfi */ private void createLogFile(LogFileItem lfi){ //当前系统日期 String currPCDate = TimeUtil.getPCDate('-'); //如果超过单个文件大小,则拆分文件 if(lfi.fullLogFileName != null && lfi.fullLogFileName.length() > 0 && lfi.currLogSize >= LogManager.SINGLE_LOG_FILE_SIZE ){ File oldFile = new File(lfi.fullLogFileName); if(oldFile.exists()){ String newFileName = Constant.CFG_LOG_PATH + "http://www.cnblogs.com/" + lfi.lastPCDate + "http://www.cnblogs.com/" + lfi.logFileName + "_" + TimeUtil.getPCDate() + "_"+ TimeUtil.getCurrTime() + ".log"; File newFile = new File(newFileName); boolean flag = oldFile.renameTo(newFile); System.out.println("日志已自动备份为 " + newFile.getName() + ( flag ? "成功!" : "失败!" ) ); lfi.fullLogFileName = ""; lfi.currLogSize = 0; } } //创建文件 if ( lfi.fullLogFileName == null || lfi.fullLogFileName.length() <= 0 || lfi.lastPCDate.equals(currPCDate) == false ){ String sDir = Constant.CFG_LOG_PATH + "http://www.cnblogs.com/" + currPCDate ; File file = new File(sDir); if(file.exists() == false){ file.mkdir(); } lfi.fullLogFileName = sDir + "http://www.cnblogs.com/" + lfi.logFileName + ".log"; lfi.lastPCDate = currPCDate; file = new File(lfi.fullLogFileName); if(file.exists()){ lfi.currLogSize = file.length(); }else{ lfi.currLogSize = 0; } } }
多日志级别FLogger 支持多种日志级别:
FLogger 为每个日志级别都提供了简易 API,在此就不再赘述了。
打印 error 和 fatal 级别日志时,FLogger 默认会将日志内容输出到控制台。
热加载