最近做项目的时候,遇到了日志的输出问题。我们想按小时输出日志,同时最多保存7天的日志。log4j本身自带的appender如下:
org.apache.log4j.ConsoleAppender 输出到控制台
org.apache.log4j.FileAppender 输出到文件
org.apache.log4j.DailyRollingFileAppender
org.apache.log4j.RollingFileAppender 文件大小到达指定尺寸的时候产生一个新的文件
按小时输出可以使用DailRollingFileAppender,但是这个类没法限制文件的数量。如果继承该类,该类输出日志的方法rollover是default,无法重写。参考了其他博主的思路,DailRollingFileAppender继承了FileAppender,我们可以自己写一个类,继承FileAppender,代码还是用DailRollingFileAppender的代码,在其中添加限制文件保存天数的方法。
CustomLogAppender 类代码如下:
import org.apache.log4j.FileAppender;
import java.io.File;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.text.SimpleDateFormat;
import java.util.*;
import org.apache.log4j.Layout;
import org.apache.log4j.helpers.LogLog;
import org.apache.log4j.spi.LoggingEvent;
/**
* @author
* @date 2018/10/30 16:41
* 按小时滚动日志,指定保存的时间,删除超出时间的日志
*/
public class CustomLogAppender 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 int maxBackupIndex = 7;
private long nextCheck = System.currentTimeMillis() - 1L;
Date now = new Date();
SimpleDateFormat sdf;
SimpleDateFormat newSdf = new SimpleDateFormat("'.'yyyy-MM-dd");
RollingCalendar rc = new RollingCalendar();
int checkPeriod = -1;
static final TimeZone gmtTimeZone = TimeZone.getTimeZone("GMT");
public CustomLogAppender() {
}
public CustomLogAppender(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.fileName + this.sdf.format(new Date(file.lastModified()));
} else {
LogLog.error("Either File or DatePattern options are not set for appender [" + this.name + "].");
}
}
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() {
RollingCalendar rollingCalendar = new RollingCalendar(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;
}
void rollOver() throws IOException {
if (this.datePattern == null) {
this.errorHandler.error("Missing DatePattern option in rollOver().");
} else {
String datedFilename = this.fileName + this.sdf.format(this.now);
if (!this.scheduledFilename.equals(datedFilename)) {
this.closeFile();
File target = new File(this.scheduledFilename);
if (target.exists()) {
target.delete();
}
File file = new File(this.fileName);
boolean result = file.renameTo(target);
if (result) {
LogLog.debug(this.fileName + " -> " + this.scheduledFilename);
} else {
LogLog.error("Failed to rename [" + this.fileName + "] to [" + this.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 marked = ff.getName().substring( file.getName().length());
String markedDate = marked.substring(0,marked.lastIndexOf("-"));
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.setFile(this.fileName, true, this.bufferedIO, this.bufferSize);
} catch (IOException var6) {
this.errorHandler.error("setFile(" + this.fileName + ", true) call failed.");
}
this.scheduledFilename = datedFilename;
}
}
}
/**
* 根据maxBackupIndex配置的保存天数,获取要保留log文件的日期范围集合
* @return list<'fileName+yyyy-MM-dd'>
*/
List<String> getMaxBackupIndexDates() {
List<String> result = new ArrayList<String>();
if (maxBackupIndex > 0) {
for (int i = 0; i < maxBackupIndex; i++) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(new Date(System.currentTimeMillis()));
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(newSdf.format(calendar.getTime()));
}
}
return result;
}
protected void subAppend(LoggingEvent event) {
long n = System.currentTimeMillis();
if (n >= this.nextCheck) {
this.now.setTime(n);
this.nextCheck = this.rc.getNextCheckMillis(this.now);
try {
this.rollOver();
} catch (IOException var5) {
if (var5 instanceof InterruptedIOException) {
Thread.currentThread().interrupt();
}
LogLog.error("rollOver() failed.", var5);
}
}
super.subAppend(event);
}
public int getMaxBackupIndex() {
return maxBackupIndex;
}
public void setMaxBackupIndex(int maxBackupIndex) {
this.maxBackupIndex = maxBackupIndex;
}
}
//这个类是org.apache.log4j里的私有类,外类无法引用,所以我在这里完全copy了出来
class RollingCalendar extends GregorianCalendar {
//private static final long serialVersionUID = -3560331770601814177L;
int type = -1;
RollingCalendar() {
}
RollingCalendar(TimeZone tz, Locale locale) {
super(tz, locale);
}
void setType(int type) {
this.type = type;
}
public long getNextCheckMillis(Date now) {
return this.getNextCheckDate(now).getTime();
}
public Date getNextCheckDate(Date now) {
this.setTime(now);
switch(this.type) {
case 0:
this.set(13, 0);
this.set(14, 0);
this.add(12, 1);
break;
case 1:
this.set(12, 0);
this.set(13, 0);
this.set(14, 0);
this.add(11, 1);
break;
case 2:
this.set(12, 0);
this.set(13, 0);
this.set(14, 0);
int hour = this.get(11);
if (hour < 12) {
this.set(11, 12);
} else {
this.set(11, 0);
this.add(5, 1);
}
break;
case 3:
this.set(11, 0);
this.set(12, 0);
this.set(13, 0);
this.set(14, 0);
this.add(5, 1);
break;
case 4:
this.set(7, this.getFirstDayOfWeek());
this.set(11, 0);
this.set(12, 0);
this.set(13, 0);
this.set(14, 0);
this.add(3, 1);
break;
case 5:
this.set(5, 1);
this.set(11, 0);
this.set(12, 0);
this.set(13, 0);
this.set(14, 0);
this.add(2, 1);
break;
default:
throw new IllegalStateException("Unknown periodicity type.");
}
return this.getTime();
}
}
然后在log4j.properties里使用这个appender。
log4j.rootLogger=INFO,CONSOLE,day
log4j.appender.day=改类所在路径.CustomLogAppender
log4j.appender.day.File=日志输出路径/日志名.log
log4j.appender.day.Threshold=INFO
#日志的后缀名格式,按小时
log4j.appender.day.DatePattern='.'yyyy-MM-dd-HH
#最多保存7天
log4j.appender.day.MaxBackupIndex=7
log4j.appender.day.Append=true
log4j.appender.day.layout=org.apache.log4j.PatternLayout
log4j.appender.day.layout.ConversionPattern=[%p][%d{yyyy-MM-dd HH:mm:ss}][%l] %m%n
原文地址:http://www.shanhh.com/set_max_count_of_log_files_for_log4j/
---------------------
作者:传杯
来源:CSDN
原文:https://blog.csdn.net/weixin_42142125/article/details/83547316
版权声明:本文为博主原创文章,转载请附上博文链接!