Log4j

2019-03-11 1342 words 7 mins read

定时删除超过N天前的日志文件


    package com.viathink.sys.log;

    import java.io.File;
    import java.io.FilenameFilter;
    import java.io.IOException;
    import java.io.InterruptedIOException;
    import java.io.Serializable;
    import java.net.URI;
    import java.text.SimpleDateFormat;
    import java.util.ArrayList;
    import java.util.Calendar;
    import java.util.Collections;
    import java.util.Date;
    import java.util.GregorianCalendar;
    import java.util.List;
    import java.util.Locale;
    import java.util.TimeZone;
    
    import org.apache.log4j.FileAppender;
    import org.apache.log4j.Layout;
    import org.apache.log4j.helpers.LogLog;
    import org.apache.log4j.spi.LoggingEvent;
    
    /**
     * @项目名称:log4jplugins
     * @类名称:CustomDailyRollingFileAppender
     * @类描述:DailyRollingFileAppender extends FileAppender
* 重写RollingFileAppender类
* 实现maxBackupIndex限制日志文件数量功能(定时删除超过N天前的日志文件) * * @UserGuid 分log4j.xml或log4j.properties两种情况:
* 1)log4j.xml:设置中的class属性为:com.han.log4jplugins.CustomDailyRollingFileAppender;
* 2)log4j.properties:替换log4j.appender.debug=org.apache.log4j.RollingFileAppender中的RollingFileAppender,设置为com.han.log4jplugins.CustomDailyRollingFileAppender。 * * @author WUHAOTIAN * @version V1.0.0 * @email nghsky@foxmail.com * @date 2019年3月11日下午4:12:11 */ public class CustomDailyRollingFileAppender 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; /** * 生产日志文件后缀("'.'yyyy-MM-dd") */ private String datePattern = "'.'yyyy-MM-dd"; /** * 默认:5个日志文件 */ protected int maxBackupIndex = 5; /** * 保存前一天的日志文件名称 =filename+("'.'yyyy-MM-dd") */ private String scheduledFilename; // The next time we estimate a rollover should occur. private long nextCheck = System.currentTimeMillis() - 1; Date now = new Date(); SimpleDateFormat sdf; RollingCalendar rc = new RollingCalendar(); int checkPeriod = TOP_OF_TROUBLE; // The gmtTimeZone is used only in computeCheckPeriod() method. static final TimeZone gmtTimeZone = TimeZone.getTimeZone("GMT"); // 无参构造 public CustomDailyRollingFileAppender() { } /** * 有参构造 * Instantiate aDailyRollingFileAppender and open the file designated byfilename. * The opened filename will become the ouput destination for thisappender. * * @param layout * @param filename */ public CustomDailyRollingFileAppender(Layout layout, String filename, String datePattern) throws IOException { super(layout, filename, true); this.datePattern = datePattern; activateOptions(); } public void setDatePattern(String pattern) { datePattern = pattern; } public void setMaxBackupIndex(int maxBackups) { this.maxBackupIndex = maxBackups; } public int getMaxBackupIndex() { return maxBackupIndex; } public String getDatePattern() { return datePattern; } @Override public void activateOptions() { super.activateOptions(); if (datePattern != null && fileName != null) { now.setTime(System.currentTimeMillis()); sdf = new SimpleDateFormat(datePattern); int type = computeCheckPeriod(); printPeriodicity(type); rc.setType(type); File file = new File(fileName); scheduledFilename = fileName + sdf.format(new Date(file.lastModified())); } else { LogLog.error("EitherFile or DatePattern options are not set for appender [" + name + "]."); } } void printPeriodicity(int type) { switch (type) { case TOP_OF_MINUTE: LogLog.debug("Appender[" + name + "] to be rolled every minute."); break; case TOP_OF_HOUR: LogLog.debug("Appender[" + name + "] to be rolled on top of every hour."); break; case HALF_DAY: LogLog.debug("Appender[" + name + "] to be rolled at midday and midnight."); break; case TOP_OF_DAY: LogLog.debug("Appender[" + name + "] to be rolled at midnight."); break; case TOP_OF_WEEK: LogLog.debug("Appender[" + name + "] to be rolled at start of week."); break; case TOP_OF_MONTH: LogLog.debug("Appender[" + name + "] to be rolled at start of every month."); break; default: LogLog.warn("Unknownperiodicity for appender [" + name + "]."); } } /* * This method computes the roll over period by looping over the periods, starting with the shortest, * and stopping when the r0 is different from from r1, * where r0 is the epoch formatted according the datePattern (supplied by the user) * and r1 is the epoch+nextMillis(i) formatted according to datePattern. * All date formatting is done in GMT and not local format because the test logic is based on comparisons relative to 1970-01-01 00:00:00 GMT (the epoch). */ int computeCheckPeriod() { RollingCalendar rollingCalendar = new RollingCalendar(gmtTimeZone, Locale.getDefault()); // set sate to 1970-01-01 00:00:00 GMT Date epoch = new Date(0); if (datePattern != null) { for (int i = TOP_OF_MINUTE; i <= TOP_OF_MONTH; i++) { SimpleDateFormat simpleDateFormat = new SimpleDateFormat(datePattern); simpleDateFormat.setTimeZone(gmtTimeZone);// do all date // formatting in GMT String r0 = simpleDateFormat.format(epoch); rollingCalendar.setType(i); Date next = new Date(rollingCalendar.getNextCheckMillis(epoch)); String r1 = simpleDateFormat.format(next); // System.out.println("Type = "+i+", r0 = "+r0+", r1 ="+r1); if (r0 != null && r1 != null && !r0.equals(r1)) { return i; } } } return TOP_OF_TROUBLE; // Deliberately head for trouble... } /** * 核心方法:生成日志文件,并完成对备份数量的监测以及历史日志的删除 * Rollover the current file to a new file. */ void rollOver() throws IOException { // 获取所有日志历史文件:并完成对备份数量的监测以及历史日志的删除 List files = getAllFiles(); Collections.sort(files); // 如果文件数量不小于设置的最大备份数量,则启用日志删除策略 if (files.size() >= maxBackupIndex) { int index = 0; int diff = files.size() - (maxBackupIndex - 1); for (ModifiedTimeSortableFile file : files) { if (index >= diff) break; file.delete(); index++; } } /* Compute filename, but only if datePattern is specified */ if (datePattern == null) { errorHandler.error("MissingDatePattern option in rollOver()."); return; } LogLog.debug("maxBackupIndex=" + maxBackupIndex); String datedFilename = fileName + sdf.format(now); // It is too early to roll over because we are still within the bounds of the current interval. // Rollover will occur once the next interval is reached. if (scheduledFilename.equals(datedFilename)) { return; } // close current file, and rename it to datedFilename this.closeFile(); File target = new File(scheduledFilename); if (target.exists()) { target.delete(); } File file = new File(fileName); boolean result = file.renameTo(target); if (result) { LogLog.debug(fileName + " -> " + scheduledFilename); } else { LogLog.error("Failedto rename [" + fileName + "] to [" + scheduledFilename + "]."); } try { // This will also close the file. This is OK since multiple close operations are safe. this.setFile(fileName, true, this.bufferedIO, this.bufferSize); } catch (IOException e) { errorHandler.error("setFile(" + fileName + ", true) call failed."); } scheduledFilename = datedFilename; } /** * * This method differentiatesDailyRollingFileAppender from its super class. *

* Before actually logging, thismethod will check whether it is time to do a rollover. If it is, * it willschedule the next rollover time and then rollover. * */ @Override protected void subAppend(LoggingEvent event) { long n = System.currentTimeMillis(); if (n >= nextCheck) { now.setTime(n); nextCheck = rc.getNextCheckMillis(now); try { rollOver(); } catch (IOException ioe) { if (ioe instanceof InterruptedIOException) { Thread.currentThread().interrupt(); } LogLog.error("rollOver()failed.", ioe); } } super.subAppend(event); } /** * * 获取同一项目日志目录下所有文件 * * This method searches list of log files * * based on the pattern given in the log4jconfiguration file * * and returns a collection * * @returnList<ModifiedTimeSortableFile> * */ private List getAllFiles() { List files = new ArrayList(); FilenameFilter filter = new FilenameFilter() { // 重写accept方法,确认是否是同一项目日志文件名称前缀。 @Override public boolean accept(File dir, String name) { String directoryName = dir.getPath(); LogLog.debug("directoryname: " + directoryName); File file = new File(fileName); String perentDirectory = file.getParent(); if (perentDirectory != null) { // name=demo.log // directoryName= /logs , // 直接fileName.substring(directoryName.length());切割之后的localFile=/demo.log…,会导致name.startsWith(localFile)始终是false // so解决方案:长度 +1 String localFile = fileName.substring(directoryName.length() + 1); return name.startsWith(localFile); } return name.startsWith(fileName); } }; File file = new File(fileName); String perentDirectory = file.getParent(); if (file.exists()) { if (file.getParent() == null) { String absolutePath = file.getAbsolutePath(); perentDirectory = absolutePath.substring(0, absolutePath.lastIndexOf(fileName)); } } File dir = new File(perentDirectory); String[] names = dir.list(filter); for (int i = 0; i < names.length; i++) { files.add(new ModifiedTimeSortableFile(dir + System.getProperty("file.separator") + names[i])); } return files; } } /** * 自定义file类,重写compareTo方法,比较文件的修改时间 * * The ClassModifiedTimeSortableFile extends java.io.File class and * * implements Comparable to sortfiles list based upon their modified date * */ class ModifiedTimeSortableFile extends File implements Serializable, Comparable { private static final long serialVersionUID = 1373373728209668895L; public ModifiedTimeSortableFile(String parent, String child) { super(parent, child); } public ModifiedTimeSortableFile(URI uri) { super(uri); } public ModifiedTimeSortableFile(File parent, String child) { super(parent, child); } public ModifiedTimeSortableFile(String string) { super(string); } @Override public int compareTo(File anotherPathName) { long thisVal = this.lastModified(); long anotherVal = anotherPathName.lastModified(); return (thisVal < anotherVal ? -1 : (thisVal == anotherVal ? 0 : 1)); } } /** * * RollingCalendar is a helper class toDailyRollingFileAppender. * * Given a periodicity type and the currenttime, it computes the * * start of the next interval. * */ class RollingCalendar extends GregorianCalendar { private static final long serialVersionUID = -8295691444111406775L; int type = CustomDailyRollingFileAppender.TOP_OF_TROUBLE; RollingCalendar() { super(); } RollingCalendar(TimeZone tz, Locale locale) { super(tz, locale); } void setType(int type) { this.type = type; } public long getNextCheckMillis(Date now) { return getNextCheckDate(now).getTime(); } public Date getNextCheckDate(Date now) { this.setTime(now); switch (type) { case CustomDailyRollingFileAppender.TOP_OF_MINUTE: this.set(Calendar.SECOND, 0); this.set(Calendar.MILLISECOND, 0); this.add(Calendar.MINUTE, 1); break; case CustomDailyRollingFileAppender.TOP_OF_HOUR: this.set(Calendar.MINUTE, 0); this.set(Calendar.SECOND, 0); this.set(Calendar.MILLISECOND, 0); this.add(Calendar.HOUR_OF_DAY, 1); break; case CustomDailyRollingFileAppender.HALF_DAY: this.set(Calendar.MINUTE, 0); this.set(Calendar.SECOND, 0); this.set(Calendar.MILLISECOND, 0); int hour = get(Calendar.HOUR_OF_DAY); if (hour < 12) { this.set(Calendar.HOUR_OF_DAY, 12); } else { this.set(Calendar.HOUR_OF_DAY, 0); this.add(Calendar.DAY_OF_MONTH, 1); } break; case CustomDailyRollingFileAppender.TOP_OF_DAY: this.set(Calendar.HOUR_OF_DAY, 0); this.set(Calendar.MINUTE, 0); this.set(Calendar.SECOND, 0); this.set(Calendar.MILLISECOND, 0); this.add(Calendar.DATE, 1); break; case CustomDailyRollingFileAppender.TOP_OF_WEEK: this.set(Calendar.DAY_OF_WEEK, getFirstDayOfWeek()); this.set(Calendar.HOUR_OF_DAY, 0); this.set(Calendar.MINUTE, 0); this.set(Calendar.SECOND, 0); this.set(Calendar.MILLISECOND, 0); this.add(Calendar.WEEK_OF_YEAR, 1); break; case CustomDailyRollingFileAppender.TOP_OF_MONTH: this.set(Calendar.DATE, 1); this.set(Calendar.HOUR_OF_DAY, 0); this.set(Calendar.MINUTE, 0); this.set(Calendar.SECOND, 0); this.set(Calendar.MILLISECOND, 0); this.add(Calendar.MONTH, 1); break; default: throw new IllegalStateException("Unknown periodicity type."); } return getTime(); } }

log4j配置:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE log4j:configuration PUBLIC "-//APACHE//DTD LOG4J 1.2//EN" "http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/xml/doc-files/log4j.dtd">
    <log4j:configuration xmlns:log4j='http://jakarta.apache.org/log4j/'>
    
    	<!-- 控制台输出日志 -->
    	<appender name="stdout" class="org.apache.log4j.ConsoleAppender">
    		<layout class="org.apache.log4j.PatternLayout">
    			<param name="ConversionPattern"
    				value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] [%-5p]  %c {%F:%L} - %m%n" />
    		</layout>
    	</appender>
    
    	<!-- 输出日志到文件  文件大小到达指定尺寸的时候产生一个新的文件 -->
      	<!-- <appender name="fileOut" class="org.apache.log4j.RollingFileAppender"> -->
      	<appender name="fileOut" class="com.viathink.sys.log.CustomDailyRollingFileAppender">
      		<!-- 自定义日志文件总数量,大于这个数量,自动删除 -->
      		<param name="maxBackupIndex" value="20"/>
      	
      		<param name="File" value="/work/mditip/logs/ssr/ssr.log" />
      		<param name="ImmediateFlush" value="true"/>
      		<param name="Threshold" value="info"></param>
      		<param name="Append" value="true"></param>
      		<param name="MaxFileSize" value="20MB"></param>
      		<!-- <param name="MaxBackupIndex" value="100"></param> -->
      		<layout class="org.apache.log4j.PatternLayout">
      			<param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] [%-5p]  %c {%F:%L} - %m%n"></param>
      		</layout>
      	</appender>
    
    	<logger name="org.springframework">
    		<level value="info" />
    	</logger>
    	<logger name="org.apache.commons">
    		<level value="info" />
    	</logger>
    	<logger name="net.sf.ehcache">
    		<level value="error" />
    	</logger>
    	<logger name="com.viathink.sys.freemarker">
    		<level value="info" />
    	</logger>
    
    	<logger name="com.viathink">
    		<level value="info" />
    	</logger>
    	<logger name="org.apache.ibatis">
    		<level value="info" />
    	</logger>
    	<logger name="org.mybatis.spring">
    		<level value="info" />
    	</logger>
    
    	<!-- 打印sql -->
    	<!-- 输出SQL语句 -->
    	<logger name="jdbc.sqlonly" additivity="false">
    		<level value="info"></level>
    		<appender-ref ref="stdout" />
    		<appender-ref ref="fileOut"/>
    	</logger>
    	<!-- 输出SQL执行时间 -->
    	<logger name="jdbc.sqltiming" additivity="false">
    		<level value="error"></level>
    		<appender-ref ref="stdout" />
    		<appender-ref ref="fileOut"/>
    	</logger>
    	<logger name="jdbc.audit" additivity="false">
    		<level value="error"></level>
    		<appender-ref ref="stdout" />
    		<appender-ref ref="fileOut"/>
    	</logger>
    	<!-- 输出SQL执行结果表格 -->
    	<logger name="jdbc.resultsettable" additivity="false">
    		<level value="error"></level>
    		<appender-ref ref="stdout" />
    		<appender-ref ref="fileOut"/>
    	</logger>
    	<!-- 输出SQL执行结果处理信息 -->
    	<logger name="jdbc.resultset" additivity="false">
    		<level value="error"></level>
    		<appender-ref ref="stdout" />
    		<appender-ref ref="fileOut"/>
    	</logger>
    	<!-- 输出数据库连接信息 -->
    	<logger name="jdbc.connection" additivity="false">
    		<level value="error"></level>
    		<appender-ref ref="stdout" />
    		<appender-ref ref="fileOut"/>
    	</logger>
    
    	<root>
    		<priority value="info" />
    		<appender-ref ref="stdout" />
    		<appender-ref ref="fileOut" />
    	</root>
    </log4j:configuration>

功能测试类:

    package com.viathink.sys.log;

    import java.io.File;
    import java.net.URL;
    
    import org.apache.log4j.Logger;
    import org.apache.log4j.PropertyConfigurator;
    
    /**
     * 自定义每天日志文件生成策略的测试类
     * @author WUHAOTIAN
     * @email nghsky@foxmail.com
     * @date 2019年3月11日下午4:16:57
     */
    public class CustomDailyRollingFileAppenderTest {
    	private static Logger logger = Logger.getLogger(CustomDailyRollingFileAppenderTest.class);
    
    	/*
    	 * 引用自定义的CustomDailyRollingFileAppender日志类
    	 * 
    	 * 自定义日志文件总数量,大于这个数量,自动删除
    	 * 
    	 */
    	public static void main(String[] args) {
    		String configFile = "log4j.xml";
    		final File file = new File(configFile);
    		if (!file.exists()) {
    			final URL url = CustomDailyRollingFileAppenderTest.class.getClassLoader().getResource("log4j.xml");
    			configFile = url.getPath();
    		}
    
    		PropertyConfigurator.configure(configFile);
    		logger.info("11111");
    	}
    
    }


Tags: log log4j

author

Authored By WUHAOTIAN

A gentleman should always strive to improve himself

We notice you're using an adblocker. If you like our webite please keep us running by whitelisting this site in your ad blocker. We’re serving quality, related ads only. Thank you!

I've whitelisted your website.

Not now
This website uses cookies to ensure you get the best experience on our website. Learn more Got it