重写log4j.jar中的DailyRollingFileAppender.java,实现按月保存日志,或者保留一定个数或者保留几个月的日志

       一般来说,log4j.jar中提供的日志都是按天保存,并且当天第一次启动项目,去生成前一天的日志,并且Info和error日志信息都保存在一起,对于日志都没有分类,不好管理与查看;
       现如今重写 log4j.jar中的DailyRollingFileAppender.java,让日志按照自己约定的方式,满足客户的需求;
       重写的类名更改为:CustomDailyRollingFileAppender.java


日志生成要求:

第一种方式: 日志按天生成,按月保存到一个文件夹中,日志文件保留期限为3-4个月,超过时间则删除以前的日志;
第二种方式:根据log.properties中配置的保存数量进行清除超出的日志;
文件下载地址:
https://download.csdn.net/download/su1573/10930807

log.properties:

log4j.rootLogger=INFO,CONSOLE,D,R
log4j.addivity.org.apache=false

####控制台######
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.Threshold=INFO
log4j.appender.CONSOLE.layout.ConversionPattern= %d{yyyy-MM-dd HH\:mm\:ss} %5p [%c] (%L) - %m%n
log4j.appender.CONSOLE.Target=System.out
log4j.appender.CONSOLE.Encoding=gbk
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout

####文件#####
#log4j.appender.A=org.apache.log4j.DailyRollingFileAppender  
#log4j.appender.A.File=${catalina.home}/logs/opension-back
#log4j.appender.A.DatePattern=yyyy-MM-dd'.log'
#log4j.appender.A.layout=org.apache.log4j.PatternLayout  
#log4j.appender.A.layout.ConversionPattern=%d{yyyy-MM-dd HH\:mm\:ss} %5p [%c] (%L) - %m%n

##日志信息测试按天生成,有效期最低3个月,最多4个月
log4j.appender.D = com.sinosoft.utils.CustomDailyRollingFileAppender
log4j.appender.D.File =${catalina.home}/logs/opension-back
log4j.appender.D.datePattern = yyyy-MM-dd'.log'
log4j.appender.D.Append = true
log4j.appender.D.Threshold = info
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern =%d{yyyy-MM-dd HH\:mm\:ss} %5p [%c] (%L) - %m%n

##错误日志文件
log4j.appender.R = com.sinosoft.utils.CustomDailyRollingFileAppender
log4j.appender.R.File =${catalina.home}/logs/opension-back-error
log4j.appender.R.datePattern = yyyy-MM-dd'.log'
log4j.appender.R.Append = true
log4j.appender.R.Threshold = error
log4j.appender.R.layout = org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern =%d{yyyy-MM-dd HH\:mm\:ss} %5p [%c] (%L) - %m%n

代码如下:

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.sinosoft.utils;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.Properties;
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;

/**
 * method:
 * Description:日志生成规则
 * author:sph
 * return
 */
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";
	static  String logName   = "";
	static  String logTime  = "";

	private int  maxBackupIndex  = 1;

	private String scheduledFilename;
	private String logFilename;
	
	private static Date lastmodfiyTimeDate;  //文件上一次修改时间
	private static Date CreateTimeDate;      //文件创建时间
	private  String filePath = "";      //文件创建时间
	

	/**
	 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;

	/**
	 * 获取当前环境所处的时区
	 * 仅供computeCheckPeriod方法使用
	 */
	static final TimeZone gmtTimeZone = TimeZone.getTimeZone("GMT");

	public CustomDailyRollingFileAppender() {}

	public CustomDailyRollingFileAppender(Layout layout, String filename, String datePattern) throws IOException {
		super(layout, filename, true);

		this.datePattern = datePattern;
		activateOptions();
	}

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

	public String getDatePattern() {
		return datePattern;
	}
	public void setFilePath(String filePath) {
		this.filePath = filePath;
	}
	
	public String getFilePath() {
		return filePath;
	}

	public int getMaxBackupIndex() {
		return maxBackupIndex;
	}

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

	public static Date getLastmodfiyTimeDate() {
		return lastmodfiyTimeDate;
	}

	public static void setLastmodfiyTimeDate(Date lastmodfiyTimeDate) {
		CustomDailyRollingFileAppender.lastmodfiyTimeDate = lastmodfiyTimeDate;
	}

	public static Date getCreateTimeDate() {
		return CreateTimeDate;
	}

	public static void setCreateTimeDate(Date createTimeDate) {
		CreateTimeDate = createTimeDate;
	}

	/**
	 * activateOptions译名为激活操作
	 * 意思是按照配置的参数进行初始化
	 * scheduledFilename为log的最后一次修改时间
	 */
	@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);
			Properties getProperties = PropertyUitls.getProperties("config.properties");
			String logFileDirExt =getProperties.getProperty("logFileDirExt");
			String openBack =getProperties.getProperty("openBack");  //opension-back
			String openBackError =getProperties.getProperty("openBackError");  //opension-back-error
			
			if(fileName.contains(openBack)) {
				logName = openBack;
			}
			if(fileName.contains(openBackError)) {
				logName = openBackError;
			}
			filePath = fileName.substring(0,fileName.indexOf(logName));
			removeOutTimeDir(filePath);  //删除过期的目录
			
			File file = new File(fileName);
			
			String lastDate = new SimpleDateFormat("yyyyMM").format(new Date(file.lastModified()));
			String nowDate = new SimpleDateFormat("yyyyMM").format(new Date());
			try {
				Date lastD = new SimpleDateFormat("yyyyMM").parse(lastDate);
				Date nowD = new SimpleDateFormat("yyyyMM").parse(nowDate);
				if(nowD.after(lastD)) {  //判断当前月是否在上一次文件修改月后面
					logTime = lastDate;//是的话,文件日期去上个月
				}else {
					logTime = nowDate; //不是的话,文件日期取当月
				}
			} catch (ParseException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
			
			String front = logTime+"-"+logFileDirExt;
			String logDir = filePath+front;  //到当前日期
			File fileDir = new File(logDir);
			if(!fileDir.exists()) {
				fileDir.mkdir();
			}
			
//			//新方法,取出创建日期
//			Path path = Paths.get(fileName);
//			BasicFileAttributeView basicview = Files.getFileAttributeView(path, BasicFileAttributeView.class,
//					LinkOption.NOFOLLOW_LINKS);
//			BasicFileAttributes attr;
//			try {
//				attr = basicview.readAttributes();
//				lastmodfiyTimeDate=new Date(attr.lastModifiedTime().toMillis());
//				CreateTimeDate= new Date(attr.creationTime().toMillis());  //文件创建日期
//			} catch (Exception e) {
//				e.printStackTrace();
//			}
			
			scheduledFilename = fileName+sdf.format(new Date(file.lastModified()));
//			scheduledFilename = logDir+File.separator+logName+sdf.format(new Date(file.lastModified()));
			logFilename = logDir+File.separator+logName+sdf.format(new Date(file.lastModified()));   //重新命名
		} else {
			LogLog.error("Either File or DatePattern options are not set for appender ["+name+"].");
		}
	}
	
	/**
	 * 先根遍历序递归删除文件夹
	 *
	 * @param dirFile 要被删除的文件或者目录
	 * @return 删除成功返回true, 否则返回false
	 */
	public  boolean deleteFile(File dirFile) {
	    // 如果dir对应的文件不存在,则退出
	    if (!dirFile.exists()) {
	        return false;
	    }
	    if (dirFile.isFile()) {
	        return dirFile.delete();
	    } else {
	        for (File file : dirFile.listFiles()) {
	            deleteFile(file);
	        }
	    }
	    return dirFile.delete();
	}
	
	/*
	 * 删除过期的日志目录
	 */
	public void removeOutTimeDir(String filePath) {
		try {
			File path = new File(filePath);
			String[] parr = path.list();
			SimpleDateFormat sdf = new SimpleDateFormat("yyyyMM");
			Date today = new SimpleDateFormat("yyyyMM").parse(sdf.format(new Date()));
			for (String pf : parr) {
				if (pf.endsWith("logmes")) {
					Date ffd = new SimpleDateFormat("yyyyMM").parse(pf.substring(0, 6));
					Calendar calendar = Calendar.getInstance();
					calendar.setTime(ffd);
					calendar.add(Calendar.MONTH, 4);// 增加4个月
					Date addThrDate = calendar.getTime();
					if (addThrDate.equals(today)) {
						File reFile = new File(filePath + pf);
						deleteFile(reFile); // 删除需要过期的目录
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}


	/**
	 * 根据type打印做出日志打印
	 * @param type
	 */
	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+"].");
		}
	}


//   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());
		//设置初始时间为格林尼治时间:1970-01-01 00:00:00 GMT
		Date epoch = new Date(0L);
		if(datePattern != null) {
			for(int i = TOP_OF_MINUTE; i <= TOP_OF_MONTH; i++) {
				//将所示的时间格式化为当前时区
				SimpleDateFormat simpleDateFormat = new SimpleDateFormat(datePattern);
				simpleDateFormat.setTimeZone(gmtTimeZone);

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

	/**
	 * 按照周期将当前日志文件转存为日期文件
	 *
	 * @throws IOException
	 */
	void rollOver() throws IOException {

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

		/*//文件创建日期 加三个月后与当期日期比较 ------------
		Calendar calendar = Calendar.getInstance();

		calendar.setTime(CreateTimeDate);

		calendar.add(Calendar.MONTH, 3);//增加3个月
		
	    Date addThrDate = calendar.getTime();  //三个月后的日期  比如:Thu Jan 17 15:03:37 CST 2019  -> Wed Apr 17 15:03:37 CST 2019
	    File target= new File(scheduledFilename);  //D:/logs/opension-back.log2019-01-14
	    if(now.before(addThrDate)) {  //日志未到3月期限,直接返回
	    	return;
	    }else {   //日志刚到三月   比如 3月1 - 6月1  ,则关闭此文件
	    	// 关闭当前文件,重命名为日期文件
			this.closeFile();
			File file = new File(fileName);  //D:\logs\opension-back.log
			boolean result = file.renameTo(target);
			
			//-------------------------------------------------------
			if(result) {
				target.delete();  //删除目标文件
				LogLog.debug(fileName +" -> "+ scheduledFilename);
			} else {
				LogLog.error("Failed to rename ["+fileName+"] to ["+scheduledFilename+"].");
			}
	    }*/
		
		

		String datedFilename = fileName+sdf.format(now);  //原先的 通过时区取时间
		
		//通过测试取时间
//		Date dd = new Date();
//		SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd'.log'");
//		String datedFilename = fileName+sdf1.format(dd);  //sph新加的  可以修改电脑时间  测试

		//如果最后一次的修改时间为当前时间 ,则不做任何任何操作
		if (scheduledFilename.equals(datedFilename)) {
			return;
		}

		// 关闭当前文件,重命名为日期文件
		this.closeFile();

		File target= new File(logFilename);  //更改后的日期文件夹里面的文件名  全路径D:\logs\201904-logmes\opension-back201904.log
		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 (file.getParentFile().exists()){
			File[] files = file.getParentFile().listFiles(new LogFileFilter(file.getName()));
			Long[] dateArray = new Long[files.length];
			for (int i = 0; i < files.length; i++) {
				File fileItem = files[i];
				String fileDateStr = fileItem.getName().replace(file.getName(), "");
				Date filedate = null;
				try {
					filedate = sdf.parse(fileDateStr);
					long fileDateLong = filedate.getTime();
					dateArray[i] = fileDateLong;
				} catch (ParseException e) {
					LogLog.error("Parse File Date Throw Exception : " + e.getMessage());
				}
			}
			Arrays.sort(dateArray);
			if (dateArray.length > maxBackupIndex) {
				for (int i = 0; i < dateArray.length - maxBackupIndex; i++) {
					String dateFileName = file.getPath() + sdf.format(dateArray[i]);
					File dateFile = new File(dateFileName);
					if (dateFile.exists()) {
						dateFile.delete();
					}
				}
			}
		}*/

		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;
	}

	/**
	 * 写入日志之前判断是否需要新起一个日志来记录
	 */
	@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);
	}
}

/**
 * 文件过滤器
 *
 */
class LogFileFilter implements FileFilter {
	private String logName;

	public LogFileFilter(String logName) {
		this.logName = logName;
	}

	@Override
	public boolean accept(File file) {
		if (logName == null || file.isDirectory()) {
			return false;
		} else {
			LogLog.debug(file.getName());
			return file.getName().startsWith(logName);
		}
	}
}

/**
 * CustomDailyRollingFileAppender的内部类
 * 提供周期类型和当前时间 ,计算并返回下一个周期的开始时间
 *
 */
class RollingCalendar extends GregorianCalendar {
	private static final long serialVersionUID = -3560331770601814177L;

	int type = CustomDailyRollingFileAppender.TOP_OF_TROUBLE;

	/**
	 * RollingCalendar默认构造器
	 */
	RollingCalendar() {
		super();
	}

	/**
	 * RollingCalendar构造器
	 * 根据地点时区 ,获取对应的日历Calendar
	 * @param tz
	 * @param locale
	 */
	RollingCalendar(TimeZone tz, Locale locale) {
		super(tz, locale);
	}

	void setType(int type) {
		this.type = type;
	}

	public long getNextCheckMillis(Date now) {
		return getNextCheckDate(now).getTime();
	}

	/**
	 * 根据所传入的时间以及时间类型获取下一个时间
	 * @param now
	 * @return
	 */
	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();
	}
	
}
	

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ssy03092919

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值