log4j日志按日期拆分压缩及按大小拆分压缩
此次对于项目中的日志打印做了一些了解及线上系统的应用。
项目中用到的日志打印的工具是org.apache.log4j中的工具。此次主要用到的是三个:
org.apache.log4j.ConsoleAppender(控制台)
org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件)
org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件)
在小项目中,日志按天拆分就够了,此时我就没有自己去重写日志打印工具了,直接利用的org.apache.log4j.DailyRollingFileAppender来生成,其中配置如下:
# log4j配置 info级别,控制台输出,文件输出 log4j.rootCategory=INFO,stdout,dailyRollingFile # 控制台输出 log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} %t %5p %c{1}:%L - %m%n # 文件输出 log4j.appender.dailyRollingFile=org.apache.log4j.DailyRollingFileAppender log4j.appender.dailyRollingFile.file=./logs/channelserver.log log4j.appender.dailyRollingFile.DatePattern='.'yyyy-MM-dd log4j.appender.dailyRollingFile.layout=org.apache.log4j.PatternLayout log4j.appender.dailyRollingFile.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} %t %5p %c{1}:%L - %m%n log4j.appender.dailyRollingFile.Append=true
最终日志文件的形式为:
channelserver.log channelserver.log.2018-11-11
这种形式拆分日志时,没有对日志文件进行压缩,所以日志文件占用的总内存和不拆分的时候是一样的,只是在去服务器找日志时方便一点,避免日志文件过大,不易操作。
下面介绍自定义的日志打印拆分及压缩:
我们去查看DailyRollingFileAppender、RollingFileAppender都是继承自FileAppender,那么我们自定义一个类,继承FileAppender,然后实现我们自己的日志打印拆分压缩的逻辑。其中DailyRollingFileAppender中已经实现了日志按日期拆分的逻辑,RollingFileAppender实现了按大小拆分的逻辑,此时我主要用到的是DailyRollingFileAppender,然后由于DailyRollingFileAppender中缺少按大小拆分的逻辑及压缩,那么此时我们将RollingFileAppender中按大小拆分的逻辑引用过来,那么此时我们就还缺少一个压缩的逻辑,此时压缩的逻辑我们可以自己编写,同时也可以将日志文件名称重新规范一下,最终代码如下:
package me.config; import org.apache.commons.lang3.StringUtils; import org.apache.log4j.FileAppender; import java.io.*; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; import java.util.TimeZone; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; import org.apache.log4j.Layout; import org.apache.log4j.helpers.CountingQuietWriter; import org.apache.log4j.helpers.LogLog; import org.apache.log4j.helpers.OptionConverter; import org.apache.log4j.spi.LoggingEvent; /** * 类描述:日志文件生成类 * 1、根据日期拆分 * 2、根据大小拆分 * 3、拆分后的日志压缩 * * @Author:wangjinhui * @date:2018年12月03日 * @Version:1.1.0 */ public class TestDailyRollingFileAppender extends FileAppender { static final int TOP_OF_TROUBLE = -1; static final int TOP_OF_MINUTE = 0; static final int TOP_OF_HOUR = 1; static final int HALF_DAY = 2; static final int TOP_OF_DAY = 3; static final int TOP_OF_WEEK = 4; static final int TOP_OF_MONTH = 5; private String datePattern = "'.'yyyy-MM-dd"; private String scheduledFilename;//上一次生成的文件名称 private long nextCheck = System.currentTimeMillis() - 1L;//下一次的校验时间 Date now = new Date(); SimpleDateFormat sdf; TestRollingCalendar rc = new TestRollingCalendar(); int checkPeriod = -1; static final TimeZone gmtTimeZone = TimeZone.getTimeZone("GMT"); protected long maxFileSize = 10485760L;//文件的最大大小 private long nextRollover = 0L;//下一次处理文件压缩的大小 private int fileIndex = 0;//生成的文件的下标 private String fileNamePrefix;//文件名称前缀 private String fileNameSuffix;//文件名称后缀 public TestDailyRollingFileAppender() { } public TestDailyRollingFileAppender(Layout layout, String filename, String datePattern) throws IOException { super(layout, filename, true); this.datePattern = datePattern; this.activateOptions(); } public void setDatePattern(String pattern) { this.datePattern = pattern; } public String getDatePattern() { return this.datePattern; } public void activateOptions() { super.activateOptions(); if (this.datePattern != null && this.fileName != null) { this.now.setTime(System.currentTimeMillis()); this.sdf = new SimpleDateFormat(this.datePattern); int type = this.computeCheckPeriod(); this.printPeriodicity(type); this.rc.setType(type); File file = new File(this.fileName); // this.scheduledFilename = this.fileNamePrefix + this.sdf.format(new Date(file.lastModified())) + "_" + fileIndex + this.fileNameSuffix; //给参数fileNamePrefix与fileNameSuffix赋值初始值 if (StringUtils.isEmpty(this.fileNamePrefix) || StringUtils.isEmpty(this.fileNameSuffix)) { this.setFileNamePrefix(this.fileName); this.setFileNameSuffix(this.fileName); } this.scheduledFilename = initScheduleFilename(this.sdf.format(new Date(file.lastModified()))); } else { LogLog.error("Either File or DatePattern options are not set for appender [" + this.name + "]."); } } /** * 利用递归确定此次在生成文件时的名称 * @param lastModifiedDate * @return */ public String initScheduleFilename(String lastModifiedDate){ scheduledFilename = this.fileNamePrefix + lastModifiedDate + "_" + fileIndex + this.fileNameSuffix; File target = new File(scheduledFilename + ".zip"); if (target.exists()) { fileIndex++; return initScheduleFilename(lastModifiedDate); } else { return scheduledFilename; } } void printPeriodicity(int type) { switch(type) { case 0: LogLog.debug("Appender [" + this.name + "] to be rolled every minute."); break; case 1: LogLog.debug("Appender [" + this.name + "] to be rolled on top of every hour."); break; case 2: LogLog.debug("Appender [" + this.name + "] to be rolled at midday and midnight."); break; case 3: LogLog.debug("Appender [" + this.name + "] to be rolled at midnight."); break; case 4: LogLog.debug("Appender [" + this.name + "] to be rolled at start of week."); break; case 5: LogLog.debug("Appender [" + this.name + "] to be rolled at start of every month."); break; default: LogLog.warn("Unknown periodicity for appender [" + this.name + "]."); } } int computeCheckPeriod() { TestRollingCalendar rollingCalendar = new TestRollingCalendar(gmtTimeZone, Locale.getDefault()); Date epoch = new Date(0L); if (this.datePattern != null) { for(int i = 0; i <= 5; ++i) { SimpleDateFormat simpleDateFormat = new SimpleDateFormat(this.datePattern); simpleDateFormat.setTimeZone(gmtTimeZone); String r0 = simpleDateFormat.format(epoch); rollingCalendar.setType(i); Date next = new Date(rollingCalendar.getNextCheckMillis(epoch)); String r1 = simpleDateFormat.format(next); if (r0 != null && r1 != null && !r0.equals(r1)) { return i; } } } return -1; } /** * 重新生成文件的方法 * @throws IOException */ void rollOver() throws IOException { if (this.datePattern == null) { this.errorHandler.error("Missing DatePattern option in rollOver()."); } else { String datedFilename = this.fileNamePrefix + this.sdf.format(this.now) + "_" + fileIndex + this.fileNameSuffix; fileIndex++; if (!this.scheduledFilename.equals(datedFilename)) { long size = ((CountingQuietWriter)this.qw).getCount(); this.nextRollover = size + this.maxFileSize; this.closeFile(); File file = new File(this.fileName); //新建压缩文件 FileInputStream fis = null; ZipOutputStream out = null; byte[] buf = new byte[1024]; try { fis = new FileInputStream(file); out = new ZipOutputStream(new FileOutputStream(scheduledFilename + ".zip")); out.putNextEntry(new ZipEntry(file.getPath())); LogLog.debug(fileName + " -> " + scheduledFilename + ".zip"); int len; while ((len = fis.read(buf)) > 0) { out.write(buf, 0, len); } out.closeEntry(); fis.close(); LogLog.debug(fileName + " -> " + scheduledFilename + ".zip successful!"); } catch (Exception e) { LogLog.error("Failed to zip [" + this.fileName + "] is error."); } finally { if (out != null) { out.closeEntry(); out.close(); } if (fis != null) fis.close(); } //压缩文件生成后,源文件删除 file.delete(); //新建日志文件 try { this.setFile(this.fileName, true, this.bufferedIO, this.bufferSize); this.nextRollover = 0L; } catch (IOException var6) { this.errorHandler.error("setFile(" + this.fileName + ", true) call failed."); } //重新赋值前一次的文件名称 this.scheduledFilename = datedFilename; } } } protected void setQWForFiles(Writer writer) { this.qw = new CountingQuietWriter(writer, this.errorHandler); } public synchronized void setFile(String fileName, boolean append, boolean bufferedIO, int bufferSize) throws IOException { super.setFile(fileName, append, this.bufferedIO, this.bufferSize); if (append) { File f = new File(fileName); ((CountingQuietWriter)this.qw).setCount(f.length()); } } protected void subAppend(LoggingEvent event) { long n = System.currentTimeMillis(); long size = ((CountingQuietWriter)this.qw).getCount(); if (n >= this.nextCheck) { this.now.setTime(n); this.nextCheck = this.rc.getNextCheckMillis(this.now); try { this.rollOver(); fileIndex = 0; } catch (IOException var5) { if (var5 instanceof InterruptedIOException) { Thread.currentThread().interrupt(); } LogLog.error("rollOver() failed.", var5); } } else if (size >= this.maxFileSize && size >= this.nextRollover) { try { this.rollOver(); } catch (IOException var5) { if (var5 instanceof InterruptedIOException) { Thread.currentThread().interrupt(); } LogLog.error("rollOver() failed.", var5); } } super.subAppend(event); } public void setMaxFileSize(String value) { this.maxFileSize = OptionConverter.toFileSize(value, this.maxFileSize + 1L); } public void setFileNamePrefix(String fileNamePrefix) { this.fileNamePrefix = fileName.substring(0, fileName.lastIndexOf(".")); } public void setFileNameSuffix(String fileNameSuffix) { this.fileNameSuffix = fileName.substring(fileName.lastIndexOf(".")); } }
配置文件如下:
# log4j配置 info级别,控制台输出,文件输出 log4j.rootCategory=INFO,stdout,dailyRollingFile # 控制台输出 log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} %t %5p %c{1}:%L - %m%n # 文件输出 #log4j.appender.dailyRollingFile=org.apache.log4j.DailyRollingFileAppender log4j.appender.dailyRollingFile=me.config.TestDailyRollingFileAppender log4j.appender.dailyRollingFile.file=./logs/channelserver.log log4j.appender.dailyRollingFile.DatePattern='.'yyyy-MM-dd log4j.appender.dailyRollingFile.layout=org.apache.log4j.PatternLayout log4j.appender.dailyRollingFile.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} %t %5p %c{1}:%L - %m%n log4j.appender.dailyRollingFile.Append=true log4j.appender.dailyRollingFile.MaxFileSize=100KB
最终的结果如图:
此处还是有点儿缺陷,就是fileNamePrefix及fileNameSuffix两个参数是通过配置文件log4j.appender.dailyRollingFile.file的值来拆分的,如果命名不规范的话,最终的结果就会出错,所以此处也是可以做成配置的,如同log4j.appender.dailyRollingFile.MaxFileSize参数配置一样去配置即可。
转载于:https://my.oschina.net/u/3308528/blog/2967409
关注公众号:程序新视界,一个让你软实力、硬技术同步提升的平台
除非注明,否则均为程序新视界原创文章,转载必须以链接形式标明本文链接