`
sw1982
  • 浏览: 511411 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

扩展log4j系列[一]为DailyRollingFileAppender加上maxBackupIndex属性

阅读更多

在log4j的大多数appender中,都有maxBackupIndex属性,但是这个DailyRollingFileAppender没有,也就是说它会每天滚一个文件,却没有办法控制文件总个数。这绝对是系统的一个“着火点”,下面就开始动手改造了:

 

一。研究整个log4j的appender结构:

    对框架的一个模块进行扩展,并非总是直接继承某个类就好了,如果不进一步深入研究就有可能掉入某些陷阱。(比如扩展log4j的Logger类,直接继承它并不能得到任何好处,具体解释清参考官方文档。),还好log4j对level,appender,layerout都扩展有很好支持的。


 

然后就是看log4j的配置文件了。 配置文件是可以直接配置扩展appender属性的,这样就替我们节省了一堆定义、解析、处理的过程

 

# 给自己的类取个对应的名


log4j.appender.appenderName=fully.qualified.name.of.appender.class 
 
#还可以给自己的类property设置值,也就是说扩展的maxBackupIndex属性可以配置


log4j.appender.appenderName.option1=value1   
... 
log4j.appender.appenderName.optionN=valueN  
   

 

二。大致胸有成竹后,可以开始看DailyRollingFileAppender的源码了。

直接看属性跟方法结构

 

大致可以猜出这个类做了如下几个事情:继承了根类appender、支持DatePattern解析并针对DatePattern设置的滚动条件组装filename、实现“监听”方法,到时间点切换logfile。。。 大部分的工作都给我们做好了:)

 

现在唯一需要改动的就是,“切换文件”方法,在切换新文件的同时,删除掉最老的n个log。

 

  /**
     Rollover the current file to a new file.
  */
  void rollOver() throws IOException {

    /* Compute filename, but only if datePattern is specified */
    if (datePattern == null) {
      errorHandler.error("Missing DatePattern option in rollOver().");
      return;
    }

    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("Failed to rename ["+fileName+"] to ["+scheduledFilename+"].");
    }

    try {
      // This will also close the file. This is OK since multiple
      // close operations are safe.
      this.setFile(fileName, false, this.bufferedIO, this.bufferSize);
    }
    catch(IOException e) {
      errorHandler.error("setFile("+fileName+", false) call failed.");
    }
    scheduledFilename = datedFilename;
  }
 

      看到这里就发现问题了,由于DatePattern格式可配置,那么产生的滚动的文件名也是不同的,也没有什么规律可循。

      比如".yyyy-ww",是按周滚动,当配置改成".yyyy-MM "按月滚动之后,通过文件名匹配删除旧文件将会导致错误。    

       另外,日志文件的切换不是定时轮询而是事件促发机制,只有在进行写操作的时候才会去判断是否需要滚动文件!那么写操作在跨过一个滚动周期执行的时候,文件名会产生空缺而不保证连续性。

       也许这就是log4j本身没有对这个appender做文件个数限制的原因吧。

 

三。妥协吧。

    框架的功能总是尽量强大的,但使用总是最简单的功能!在IDC环境中通常是不允许按时间滚动记log的,主要是防止日志文件撑爆硬盘成为着火点。 这里考虑启用按时间滚动,主要是性能日志的统计脚本需要日志文件以日期为名按天存储,并且只需要备份前一天的即可.

 

    那么我的需求就简单了:简化功能!

   仿造DailyRollingFileAppender实现1.仅支持按天滚动的 、2.格式写死的DatePattern ,3.最大备份文件个数为n的appender 。(备份数可配考虑灵活性,但一定要有参数检查预防万一!)

    限制datepattern,一方面可以防止配错,弄成按月滚动肯定死翘翘;另一方面也容易处理MaxBackupIndex删除历史文件。 more,既然知道是按天滚动,check的方法当然可以简化了:

 

 

最终修改版的按天滚动appender如下:

 

package cxxxxxxxj;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import org.apache.log4j.FileAppender;
import org.apache.log4j.Layout;
import org.apache.log4j.helpers.LogLog;
import org.apache.log4j.spi.LoggingEvent;

/**
 * 扩展的一个按天滚动的appender类
 * 暂时不支持datePattern设置,但是可以配置maxBackupIndex
 * @author weisong
 *
 */
public class DayRollingFileAppender extends FileAppender {


  /**不允许改写的datepattern */
  private final String datePattern = "'.'yyyy-MM-dd";
  
  /**最多文件增长个数*/
  private int  maxBackupIndex = 2;
  
  /**"文件名+上次最后更新时间"*/
  private String scheduledFilename;

  /**
     The next time we estimate a rollover should occur. */
  private long nextCheck = System.currentTimeMillis () - 1;

  Date now = new Date();

  SimpleDateFormat sdf;

  /**
     The default constructor does nothing. */
  public DayRollingFileAppender() {
  }

  /**
		改造过的构造器
    */
  public DayRollingFileAppender (Layout layout, String filename,
		  int  maxBackupIndex) throws IOException {
    super(layout, filename, true);
    this.maxBackupIndex = maxBackupIndex;
    activateOptions();
  }


  /**
   * 初始化本Appender对象的时候调用一次
   */
  public void activateOptions() {
    super.activateOptions();
    if(fileName != null) { //perf.log
      now.setTime(System.currentTimeMillis());
      sdf = new SimpleDateFormat(datePattern);
      File file = new File(fileName);
      //获取最后更新时间拼成的文件名
      scheduledFilename = fileName+sdf.format(new Date(file.lastModified()));
    } else {
      LogLog.error("File is not set for appender ["+name+"].");
    }
    if(maxBackupIndex<=0) {
    	LogLog.error("maxBackupIndex reset to default value[2],orignal value is:" + maxBackupIndex);
    	maxBackupIndex=2;
    }
  }


  /**
         滚动文件的函数:
         1.对文件名带的时间戳进行比较,确定是否更新
         2.if需要更新,当前文件rename到文件名+日期, 重新开始写文件
         3. 针对配置的maxBackupIndex,删除过期的文件
  */
	void rollOver() throws IOException {

		String datedFilename = fileName + sdf.format(now);
		// 如果上次写的日期跟当前日期相同,不需要换文件
		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("Failed to rename [" + fileName + "] to ["
					+ scheduledFilename + "].");
		}

		// 删除过期文件
		if (maxBackupIndex > 0) {
			File folder = new File(file.getParent());
			List<String> maxBackupIndexDates = getMaxBackupIndexDates();
			for (File ff : folder.listFiles()) { //遍历目录,将日期不在备份范围内的日志删掉
				if (ff.getName().startsWith(file.getName()) && !ff.getName().equals(file.getName())) {
					//获取文件名带的日期时间戳
					String markedDate = ff.getName().substring(file.getName().length());
					if (!maxBackupIndexDates.contains(markedDate)) {
						result = ff.delete();
					}
					if (result) {
						LogLog.debug(ff.getName() + " ->deleted ");
					} else {
						LogLog.error("Failed to deleted old DayRollingFileAppender file :" + ff.getName());
					}
				}
			}
		}

		try {
			// This will also close the file. This is OK since multiple
			// close operations are safe.
			this.setFile(fileName, false, this.bufferedIO, this.bufferSize);
		} catch (IOException e) {
			errorHandler.error("setFile(" + fileName + ", false) call failed.");
		}
		scheduledFilename = datedFilename; // 更新最后更新日期戳
	}

  /**
   * Actual writing occurs here. 这个方法是写操作真正的执行过程!
   * */
  protected void subAppend(LoggingEvent event) {
		long n = System.currentTimeMillis();
		if (n >= nextCheck) { //在每次写操作前判断一下是否需要滚动文件
			now.setTime(n);
			nextCheck = getNextDayCheckPoint(now);
			try {
				rollOver();
			} catch (IOException ioe) {
				LogLog.error("rollOver() failed.", ioe);
			}
		}
		super.subAppend(event);
	}

  /**
   * 获取下一天的时间变更点
   * @param now
   * @return
   */
  long getNextDayCheckPoint(Date now) {
	  Calendar calendar = Calendar.getInstance();
	  calendar.setTime(now);
	  calendar.set(Calendar.HOUR_OF_DAY, 0);
	  calendar.set(Calendar.MINUTE, 0);
	  calendar.set(Calendar.SECOND, 0);
	  calendar.set(Calendar.MILLISECOND, 0);//注意MILLISECOND,毫秒也要置0.。。否则错了也找不出来的
	  calendar.add(Calendar.DATE, 1);
	  return calendar.getTimeInMillis();
  }
  
  /**
   * 根据maxBackupIndex配置的备份文件个数,获取要保留log文件的日期范围集合
   * @return list<'fileName+yyyy-MM-dd'>
   */
  List<String> getMaxBackupIndexDates() {
	  List<String> result = new ArrayList<String>();
	  if(maxBackupIndex>0) {
		  for (int i = 1; i <= maxBackupIndex; i++) {
			Calendar calendar = Calendar.getInstance();
			calendar.setTime(now);
			calendar.set(Calendar.HOUR_OF_DAY, 0);
			calendar.set(Calendar.MINUTE, 0);
			calendar.set(Calendar.SECOND, 0);
			calendar.set(Calendar.MILLISECOND, 0);//注意MILLISECOND,毫秒也要置0.。。否则错了也找不出来的
			calendar.add(Calendar.DATE, -i);
			result.add(sdf.format(calendar.getTime()));
		}
	  }
	  return result;
  }
  
	public int getMaxBackupIndex() {
		return maxBackupIndex;
	}

	public void setMaxBackupIndex(int maxBackupIndex) {
		this.maxBackupIndex = maxBackupIndex;
	}
    
	public String getDatePattern() {
		return datePattern;
	}

//	public static void main(String[] args) {
//		DayRollingFileAppender da = new DayRollingFileAppender();
//		da.setMaxBackupIndex(2);
//		da.sdf = new SimpleDateFormat(da.getDatePattern());
//		System.out.println(da.getMaxBackupIndexDates());
//		
//		File f = new File("e:/log/b2c/perf.log");
//		System.out.println("f.name=" + f.getName());
//		File p = new File(f.getParent());
//		for(File ff : p.listFiles()) {
//			System.out.println(ff);
//		}
//	}
}


  

 

  • 大小: 91.7 KB
  • 大小: 66.7 KB
分享到:
评论
3 楼 ustclz 2012-12-27  
你好,这个很好用,我在纯java项目里面配置properti文件用这个没问题,但是在jboss 7.1的standalone.xml里面想配置文件生成方式用DailyRollingFileAppender似乎不支持啊,求解jboss中的standalone.xml可以应用外部的文件处理方法吗~~小白提问
2 楼 sw1982 2011-08-03  
wengsibo 写道
这并不能用....

debug之咯,呵呵。代码都写的比较清楚了。  泛泛的讲不能用,本身就不是程序员应有的精神
1 楼 wengsibo 2011-08-03  
这并不能用....

相关推荐

    log4j_appender:带有MaxBackupIndex的DailyRollingFileAppender

    在Java日志处理领域,Log4j是一个广泛使用的开源库,它为应用程序提供灵活的日志记录功能。在Log4j中,`DailyRollingFileAppender`是一个非常实用的组件,它能按照日期滚动生成日志文件,确保日志文件不会无限增长。...

    配置好log4j的eclipse简单工程,每天输出到一个日志文件中,该文件名为:“XXX_年月日时分.log”的形式

    在IT行业中,日志记录是系统调试、性能分析和故障排查的重要工具,而Log4j则是Java编程语言中广泛使用的日志框架之一。本工程基于Eclipse IDE,配置了一个简单的Log4j设置,实现了每天自动将日志输出到一个特定命名...

    log4j实用配置扩展

    ### log4j实用配置扩展 #### 一、log4j简介与重要性 Log4j是一种广泛使用的开源日志框架,由Apache软件基金会维护。它主要用于记录Java应用程序的运行时信息,帮助开发者追踪应用程序的行为、调试问题以及进行性能...

    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....

    log4j删除前N天日志

    首先,DailyRollingFileAppender是Log4j中的一个Appender实现,它按照日期滚动日志文件。例如,每天生成一个新的日志文件,这样可以避免单个日志文件过大。这个特性对于长时间运行的应用尤其有用,因为它能够保持...

    log4j日志详细讲解

    首先,要在项目中启用Log4j,需要创建一个名为`log4j.properties`的配置文件,将其放在`classes`目录下。这个文件用于定义Log4j的行为,包括日志的级别、输出目的地、格式等。 1. **配置根Logger** 根Logger是Log4...

    log4j与log4j.properties的配置.doc

    log4j.appender.R.MaxBackupIndex=1 log4j.appender.R.layout=org.apache.log4j.PatternLayout log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n log4j.logger.com.foo=WARN ``` 这个配置文件定义了 Log...

    log4j.properties(完整版) log4j.properties(精简版)

    - **定义日志文件Appender**:如`log4j.appender.FILE=org.apache.log4j.DailyRollingFileAppender`,每天创建新的日志文件。 - **文件Appender属性**:`log4j.appender.FILE.File=logs/app.log`,指定日志文件的...

    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....

    SpringBoot框架配置log4j和log4j2的配置代码

    log4j.appender.file=org.apache.log4j.DailyRollingFileAppender log4j.appender.file.File=app.log log4j.appender.file.Append=true log4j.appender.file.Threshold=DEBUG log4j.appender.file.layout=org.apache...

    log4j示例项目

    Log4j是一款广泛使用的Java日志框架,它为应用程序提供了灵活的日志记录功能。这个“log4j示例项目”旨在帮助开发者理解和使用Log4j,通过该项目,我们可以深入学习Log4j的配置、使用方法以及其在实际开发中的应用。...

    tomcat下的log4j日志配置

    2. **配置全局 Log4j 属性文件**:在任意位置创建 `log4j.properties` 文件,并设置全局的日志级别、输出格式及存储路径等。通常会将该文件放置在 Tomcat 的 `conf` 目录下。 ```properties log4j.rootLogger=...

    log4j的配置及使用

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

    java log4j统一打印在user.dir目录下(windows、linux通用、不用考虑不同操作系统分隔符不一致的情况)

    首先,创建一个名为`log4j.properties`的文件,并将其放置在项目的类路径下。 ```properties # log4j.properties # 设置root logger级别为INFO,所有未指定级别的类都将使用此级别 log4j.rootLogger=INFO, FILE #...

    log4j jarjar包

    在Java项目中,引入log4j-1.2.16.jar这个jar包后,我们首先需要配置log4j的属性文件(通常为log4j.properties或log4j.xml)。配置文件中,我们可以设定日志器的级别、Appender类型及输出格式等。 例如,以下是一个...

    log4j-1.2.17.jar下载

    log4j.appender.File=org.apache.log4j.DailyRollingFileAppender log4j.appender.File.File=/var/log/app.log log4j.appender.File.layout=org.apache.log4j.PatternLayout log4j.appender.File.layout....

    log4j-1.2.17.jar及配置文件

    Log4j是一个灵活且高效的日志记录库,为Java应用程序提供了丰富的日志功能。它的设计目标是提供一个能够进行调试、性能监测、系统管理和安全控制的工具。Log4j的核心组件包括Logger(日志器)、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使用jar包和log4j.properties配置文件

    Log4j是Apache组织提供的一款广泛使用的Java日志框架,因其灵活性、高效性和可扩展性而备受青睐。本文将详细探讨Log4j的使用,主要关注1.2.17版本的jar包以及配置文件`log4j.properties`。 ### 1. Log4j简介 Log4j...

    log4j_jar包跟教程说明

    log4j.appender.FILE=org.apache.log4j.DailyRollingFileAppender log4j.appender.FILE.File=./logs/app.log log4j.appender.FILE.Append=true log4j.appender.FILE.Threshold=DEBUG log4j.appender.FILE....

Global site tag (gtag.js) - Google Analytics