`

Log4j RollingFileAppender和DailyRollingFileAppender杂交

 
阅读更多
package org.apache.log4j;

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.Writer;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;

import org.apache.log4j.helpers.CountingQuietWriter;
import org.apache.log4j.helpers.LogLog;
import org.apache.log4j.helpers.OptionConverter;
import org.apache.log4j.spi.LoggingEvent;

/**
 * 整合<code>RollingFileAppender</code>与<code>DailyRollingFileAppender</code>
 * 
 * <p>
 * 可追加属性如下
 * </p>
 * 
 * <table border="1">
 * <tr>
 * <th><b>属性名称</b></th>
 * <th><b>说明</b></th>
 * <th><b>默认值</b></th>
 * </tr>
 * <tr>
 * <td nowrap="true"><b>maxFileSize</b></td>
 * <td>日志文件大小</td>
 * <td nowrap="true">java.lang.Long#MAX_VALUE</td>
 * </tr>
 * <tr>
 * <td nowrap="true"><b>maxBackupIndex</b></td>
 * <td>每日产生日志文件的数量(个数)</td>
 * <td nowrap="true">java.lang.Integer#MAX_VALUE</td>
 * </tr>
 * <tr>
 * <td nowrap="true"><b>datePattern</b></td>
 * <td>日期格式</td>
 * <td nowrap="true">'.'yyyy-MM-dd</td>
 * </tr>
 * </table>
 * 
 * <p>
 * <b>maxBackupIndex</b> 设置为一个过小的数值时,有可能最老的日志文件被删除。 <br/>
 * <b>maxFileSize</b> 设置为0时,日志是不会输出到文件中的。这一点需要特别注意!
 * </p>
 * 
 * </ul>
 * 
 * <h4>配置列子(log4j.xml)</h4>
 * 
 * <pre>
 *   &lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; ?&gt;
 *   &lt;!DOCTYPE log4j:configuration SYSTEM &quot;log4j.dtd&quot;&gt;
 *   
 *   &lt;log4j:configuration xmlns:log4j=&quot;http://jakarta.apache.org/log4j/&quot;&gt;
 *   
 *    &lt;appender name=&quot;FILE&quot; class=&quot;org.apache.log4j.CompositeRollingFileAppender&quot;&gt;
 *      &lt;param name=&quot;File&quot;   value=&quot;log/sample.log&quot; /&gt;
 *      &lt;param name=&quot;maxFileSize&quot; value=&quot;15MB&quot; /&gt;
 *      &lt;param name=&quot;maxBackupIndex&quot; value=&quot;12&quot; /&gt;    
 *      
 *      &lt;layout class=&quot;org.apache.log4j.PatternLayout&quot;&gt;
 *        &lt;param name=&quot;ConversionPattern&quot; value=&quot;%d{ABSOLUTE} %5p %c:%L - %m%n&quot;/&gt;
 *      &lt;/layout&gt;
 *    &lt;/appender&gt;
 *  
 *   &lt;/log4j:configuration&gt;
 * </pre>
 * 
 * <h4>配置列子(log4j.properties)</h4>
 * 
 * <pre>
 *   log4j.rootCategory=DEBUG, file
 *   
 *   log4j.appender.file=org.apache.log4j.CompositeRollingFileAppender
 *   log4j.appender.file.File=${webapp.root}/WEB-INF/log/sample.log
 *   log4j.appender.file.maxBackupIndex=12
 *   log4j.appender.file.maxFileSize=15MB
 *   log4j.appender.file.layout=org.apache.log4j.PatternLayout
 *   log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c:%L - %m%n
 * </pre>
 */
public class CompositeRollingFileAppender extends FileAppender {

	private static final int TOP_OF_TROUBLE = -1;

	private static final int TOP_OF_MINUTE = 0;

	private static final int TOP_OF_HOUR = 1;

	private static final int HALF_DAY = 2;

	private static final int TOP_OF_DAY = 3;

	private static final int TOP_OF_WEEK = 4;

	private static final int TOP_OF_MONTH = 5;

	private static final TimeZone gmtTimeZone = TimeZone.getTimeZone("GMT");

	private static final int BY_SIZE = 0;

	private static final int BY_DATE = 1;

	protected long maxFileSize = Long.MAX_VALUE;

	protected int maxBackupIndex = Integer.MAX_VALUE;

	protected String datePattern = "'.'yyyy-MM-dd";

	protected String scheduledFilename;

	protected long nextCheck = System.currentTimeMillis() - 1;

	private Date now = new Date();

	private SimpleDateFormat sdf;

	private RollingCalendar rc = new RollingCalendar();

	/*----------------------------------------------------------------------------*/
	public CompositeRollingFileAppender() {
		super();
	}

	public CompositeRollingFileAppender(Layout layout, String filename,
			boolean append) throws IOException {
		super(layout, filename, append);
	}

	public CompositeRollingFileAppender(Layout layout, String filename)
			throws IOException {
		super(layout, filename);
	}

	public CompositeRollingFileAppender(Layout layout, String filename,
			String datePattern) throws IOException {
		super(layout, filename, true);
		this.datePattern = datePattern;
		activateOptions();
	}

	public CompositeRollingFileAppender(Layout layout, String filename,
			String datePattern, boolean append) throws IOException {
		super(layout, filename, append);
		this.datePattern = datePattern;
		activateOptions();
	}

	/*----------------------------------------------------------------------------*/

	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) qw).setCount(f.length());
		}
	}

	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("Either File 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("Unknown periodicity for appender [" + name + "].");
		}
	}

	int computeCheckPeriod() {
		RollingCalendar rollingCalendar = new RollingCalendar(gmtTimeZone,
				Locale.ENGLISH);
		// 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...
	}


	public void rollOverSize() {

		LogLog.debug("rolling over count="
				+ ((CountingQuietWriter) qw).getCount());
		LogLog.debug("maxBackupIndex=" + maxBackupIndex);

		if (maxBackupIndex > 0) {
			rotateLogFilesBy(BY_SIZE);
		}
		
		try {
			this.setFile(fileName, false, bufferedIO, bufferSize);
		} catch (IOException e) {
			LogLog.error("setFile(" + fileName + ", false) call failed.", e);
		}
	}

	public void rollOverTime() throws IOException {

		if (datePattern == null) {
			errorHandler.error("Missing DatePattern option in rollOver().");
			return;
		}

		String datedFilename = fileName + sdf.format(now);
		if (scheduledFilename.equals(datedFilename)) {
			return;
		}

		rotateLogFilesBy(BY_DATE);

		try {
			this.setFile(fileName, false, this.bufferedIO, this.bufferSize);
		} catch (IOException e) {
			errorHandler.error("setFile(" + fileName + ", false) call failed.");
		}
		scheduledFilename = datedFilename;
	}

	protected void subAppend(LoggingEvent event) {
		long n = System.currentTimeMillis();
		if (n >= nextCheck) {
			now.setTime(n);
			nextCheck = rc.getNextCheckMillis(now);
			try {
				rollOverTime();
			} catch (IOException ioe) {
				LogLog.error("rollOver() failed.", ioe);
			}
		}

		if ((fileName != null) && ((CountingQuietWriter) qw).getCount() >= maxFileSize) {
			rollOverSize();
		}

		super.subAppend(event);
	}

	private int getBackupLogFileNum() {
		File[] logs = getAllLogFiles();
		return logs.length - 1;
	}

	private File[] getAllLogFiles() {
		File outputPath = new File(fileName);
		String name = outputPath.getName();
		File logDir = outputPath.getParentFile();
		if (logDir == null) {
			logDir = new File(".");
		}
		return logDir.listFiles(new LogFileNameFilter(name));
	}

	private void deleteOldestFile() {
		File[] logs = getAllLogFiles();
		File oldest = null;
		long maxLastModified = 0;

		for (int i = 0; i < logs.length; i++) {
			long lastModified = logs[i].lastModified();
			if (oldest == null) {
				oldest = logs[i];
				maxLastModified = lastModified;
			} else {
				if (maxLastModified > lastModified) {
					oldest = logs[i];
					maxLastModified = lastModified;
				}
			}
		}

		if (oldest != null && oldest.exists()) {
			oldest.delete();
		}
	}

	private void rotateLogFilesBy(int mode) {

		List<File> notDailyLogs = new ArrayList<File>();
		File[] allLogs = getAllLogFiles();
		for (int i = 0; i < allLogs.length; i++) {
			if (!isDailyRotatedLog(allLogs[i])) {
				notDailyLogs.add(allLogs[i]);
			}
		}
		int notDailyLogNum = notDailyLogs.size();

		if (mode == BY_SIZE) {
			File file = null;
			File target = null;
			if (getBackupLogFileNum() >= maxBackupIndex) {
				deleteOldestFile();
			}
			
			for (int i = notDailyLogNum - 1; i >= 1; i--) {
				file = new File(fileName + "." + i);
				if (file.exists()) {
					target = new File(fileName + '.' + (i + 1));
					LogLog.debug("Renaming file " + file + " to " + target);
					file.renameTo(target);
				}
			}

			target = new File(fileName + "." + 1);

			this.closeFile();

			file = new File(fileName);
			LogLog.debug("Renaming file " + file + " to " + target);
			file.renameTo(target);
		} else if (mode == BY_DATE) {
			this.closeFile();

			if (getBackupLogFileNum() >= maxBackupIndex) {
				deleteOldestFile();
			}

			for (int i = 1; i <= notDailyLogNum - 1; i++) {
				String from = fileName + '.' + i;
				String to = scheduledFilename + '.' + i;
				renameFile(from, to);
			}

			renameFile(fileName, scheduledFilename);
		} else {
			LogLog.warn("invalid mode:" + mode);
		}
	}

	private boolean isDailyRotatedLog(File deletedFile) {
		String deletedName = deletedFile.getName();
		File outputPath = new File(fileName);
		String name = outputPath.getName();

		if (deletedName.equals(name)) {
			return false;
		} else {
			String ext = deletedName.substring(name.length() + 1);
			try {
				Integer.parseInt(ext);
			} catch (NumberFormatException ex) {
				return true;
			}
			return false;
		}
	}

	private void renameFile(String from, String to) {
		File toFile = new File(to);
		if (toFile.exists()) {
			toFile.delete();
		}

		File fromFile = new File(from);
		fromFile.renameTo(toFile);
	}

	/*----------------------------------------------------------------------------*/
	public int getMaxBackupIndex() {
		return maxBackupIndex;
	}
	
	public long getMaximumFileSize() {
		return maxFileSize;
	}

	public void setMaxBackupIndex(int maxBackups) {
		this.maxBackupIndex = maxBackups;
	}

	public void setMaximumFileSize(long maxFileSize) {
		this.maxFileSize = maxFileSize;
	}

	public void setMaxFileSize(String value) {
		maxFileSize = OptionConverter.toFileSize(value, maxFileSize + 1);
	}

	protected void setQWForFiles(Writer writer) {
		this.qw = new CountingQuietWriter(writer, errorHandler);
	}

	public void setDatePattern(String pattern) {
		datePattern = pattern;
	}

	public String getDatePattern() {
		return datePattern;
	}

	/*----------------------------------------------------------------------------*/

	static class LogFileNameFilter implements FilenameFilter {
		String fileName;

		LogFileNameFilter(String name) {
			this.fileName = name;
		}

		public boolean accept(File dir, String name) {
			return name.startsWith(fileName);
		}
	}

}


配置列子:
#------------------------------------------------------------------------------------
# Root-Categroy
#------------------------------------------------------------------------------------
log4j.rootCategory=DEBUG, stdout, file

#------------------------------------------------------------------------------------
# stdout
#------------------------------------------------------------------------------------
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c:%L - %m%n

#------------------------------------------------------------------------------------
# file (log)
#------------------------------------------------------------------------------------
log4j.appender.file=org.apache.log4j.CompositeRollingFileAppender
log4j.appender.file.File=D:/log4j-test/sample.log
log4j.appender.file.maxBackupIndex=12
log4j.appender.file.maxFileSize=15MB
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c:%L - %m%n
分享到:
评论

相关推荐

    Log4j日志包

    log4j.appender.A1=org.apache.log4j.DailyRollingFileAppender log4j.appender.A1.File=c\:\\SampleMessages.html log4j.appender.A1.DatePattern=yyyyMMdd-HH log4j.appender.A1.layout=org.apache.log4j....

    SSM整合中的Log4j日志的配置详情

    log4j.appender.file = org.apache.log4j.RollingFileAppender log4j.appender.file.File=log/tibet.log log4j.appender.file.MaxFileSize=10mb log4j.appender.file.Threshold=ERROR log4j.appender.file.layout=...

    log4j日志驱动包

    log4j.appender.A1=org.apache.log4j.DailyRollingFileAppender log4j.appender.A1.File=SampleMessages.log4j log4j.appender.A1.DatePattern=yyyyMMdd-HH'.log4j' log4j.appender.A1.layout=org.apache.log4j.xml....

    log4j配置和加载方法

    本文将深入解析log4j的配置与加载机制,帮助开发者更好地理解和应用log4j。 #### Log4j配置文件格式 Log4j支持多种配置文件格式,包括`.properties`和`.xml`。两种格式在功能上并无本质区别,选择哪种取决于个人或...

    Log4j详解,详细讲解log4j的使用,和原理

    Log4j 中将要输出的 Log 信息定义了 5 种级别,依次为 DEBUG、INFO、WARN、ERROR 和 FATAL。当输出时,只有级别高过配置中规定的级别的信息才能真正的输出。 Log4j 的 Appender Log4j 提供了多种 Appender,包括:...

    log4j与log4j.properties的配置.doc

    这些级别可以自定义,但 Log4j 建议只使用四个级别:ERROR、WARN、INFO 和 DEBUG。 四、Appenders Appenders 是 Log4j 的输出目的地,用于将日志信息输出到不同的目的地。Log4j 提供了多种 Appenders,包括: 1. ...

    log4j的配置及使用

    org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件) org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生新文件) 使用 Log4j 可以帮助开发人员调试和分析程序,了解程序的...

    Log4J完整说明和配置

    log4j.appender.ROLLING_FILE=org.apache.log4j.RollingFileAppender log4j.appender.ROLLING_FILE.Threshold=ERROR log4j.appender.ROLLING_FILE.File=rolling.log log4j.appender.ROLLING_FILE.Append=true log4j....

    log4j.properties配置详解

    - `org.apache.log4j.DailyRollingFileAppender`:根据日期滚动日志文件,每天生成一个新的日志文件。 - `org.apache.log4j.RollingFileAppender`:当文件大小达到指定值时,自动滚动日志文件,创建新文件。 - `...

    log4j日志详细讲解

    log4j.appender.DailyRollingFileAppender=org.apache.log4j.DailyRollingFileAppender log4j.appender.DailyRollingFileAppender.File=mylog.txt log4j.appender.DailyRollingFileAppender.DatePattern='.'yyyy-...

    log4j-1.2.16下载

    四、Log4j-1.2.16的优化与维护 1. **日志级别管理**:在生产环境中,通常会将日志级别设置为WARN或ERROR,以减少不必要的日志输出,提高性能。 2. **日志分割**:为了便于管理和分析,可以配置Log4j按日期分割日志...

    log4j日志配置以及配置文件详解

    接下来,`log4j配置说明.txt`文件通常会提供更详细的解释和示例,包括如何配置不同的appender(如FileAppender、RollingFileAppender等),如何定义不同logger的级别,以及如何使用自定义的error handler和filter。...

    Log4J_全能配置文件.pdf

    - `log4j.appender.ROLLING_FILE=org.apache.log4j.RollingFileAppender`:指定了名为`ROLLING_FILE`的appender用于回滚文件输出。 - `log4j.appender.ROLLING_FILE.Threshold=ERROR`:设置此appender只记录错误级别...

    tomcat下用Log4j 按文件大小,生成catalina.out日志文件

    log4j.appender.RFA=org.apache.log4j.DailyRollingFileAppender log4j.appender.RFA.File=${catalina.base}/logs/catalina.out log4j.appender.RFA.Append=true log4j.appender.RFA.Threshold=DEBUG log4j.appender...

    log4j 1.2.17版本jar包

    Log4j 1.2.17中包括ConsoleAppender(控制台)、FileAppender(文件)、RollingFileAppender(滚动文件)等多种类型的Appender。 3. **Layout**: 格式化器,定义日志信息的输出格式。例如,SimpleLayout只显示消息...

    log4j使用教程(详解)

    配置Log4j主要通过log4j.properties或log4j.xml文件进行,定义Logger的级别、Appender的目标和Layout的格式。例如: ```properties log4j.rootLogger=DEBUG, FILE log4j.appender.FILE=org.apache.log4j....

    log4j.properties的配置说明

    Log4j 提供了多种类型的 Appender,例如 ConsoleAppender、FileAppender、RollingFileAppender 等。 log4j.appender.stdout=org.apache.log4j.ConsoleAppender 这个语句定义了一个名为 stdout 的 ConsoleAppender...

    log4j配置文件说明(完整版)

    * org.apache.log4j.RollingFileAppender:文件大小到达指定尺寸的时候产生一个新的文件 * org.apache.log4j.WriterAppender:将日志信息以流格式发送到任意指定的地方 例如:log4j.appender.stdout=org.apache.log...

    log4j按功能保存日志

    在“log4j按功能保存日志”的场景中,我们通常会利用Log4j的配置灵活性,将不同功能的日志分别写入不同的文件,以便于后期分析、排查问题和监控系统状态。 1. **Log4j基本概念** - **Logger**: 日志记录器,是Log4...

    log4j使用笔记

    - `org.apache.log4j.RollingFileAppender`:根据文件大小自动滚动输出文件。 - `org.apache.log4j.WriteAppender`:将日志写入到其他输出流。 - `org.apache.log4j.jdbc.JDBCAppender`:通过 JDBC 将日志记录到...

Global site tag (gtag.js) - Google Analytics