LOG4J 存在一个问题,一段时间内没有打印日志,LOG4J日志归档会空缺一些日志文件。
如果有监控系统对日志文件进行监控,可能就会导致误判。
如果在程序中定时向LOG4J中打印日志或者空格(打一个空格也会存在一个换行)也可以,但是会破坏日志的完整性,监控程序可能会解析出错等。
经过研究,提供一种方法,修改LOG4J的源码,保证无日志打印,日志文件也会定时归档。
1、重写 DailyRollingFileAppende 类。
package com.pouyang.log4j;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
import java.util.Timer;
import java.util.TimerTask;
import org.apache.log4j.FileAppender;
import org.apache.log4j.Layout;
import org.apache.log4j.helpers.LogLog;
import org.apache.log4j.spi.LoggingEvent;
public class UmpDailyRollingFileAppender 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 long nextCheck = System.currentTimeMillis() - 1L;
private String categoryName = "MPSP";
Date now = new Date();
SimpleDateFormat sdf;
RollingCalendar rc = new RollingCalendar();
Timer timer;
org.slf4j.Logger _log ;
int checkPeriod = -1;
static final TimeZone gmtTimeZone = TimeZone.getTimeZone("GMT");
public UmpDailyRollingFileAppender()
{
System.out.println("UmpDailyRollingFileAppender()"+this);
}
public UmpDailyRollingFileAppender(Layout layout, String filename, String datePattern)
throws IOException
{
super(layout, filename, true);
this.datePattern = datePattern;
activateOptions();
System.out.println("UmpDailyRollingFileAppender()"+this);
}
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 = computeCheckPeriod();
printPeriodicity(type);
this.rc.setType(type);
File file = new File(this.fileName);
this.scheduledFilename =
(this.fileName +
this.sdf.format(new Date(file.lastModified())));
this.timer = new Timer();
Date next = this.rc.getNextCheckDate(this.now);
long perid = this.rc.getNextCheckMillis(next) - next.getTime();
Debug.log("timer.schedule(next,perid): "+next +","+perid+"ms");
_log = org.slf4j.LoggerFactory.getLogger(categoryName);
this.timer.schedule(new RolloverFileTask(), next, perid);
}
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;
}
/**
* old log LOGARCHIVE
* log4j locked http://blog.163.com/qiongling007@126/blog/static/214242962011102344916998/
* log4j time gen log file: http://www.iteye.com/topic/1006379
* @throws IOException
*/
void rollOver()
throws IOException
{
if (this.datePattern == null) {
this.errorHandler.error("Missing DatePattern option in rollOver().");
return;
}
String datedFilename = this.fileName + this.sdf.format(this.now);
//Debug.log("rollOver() datedFilename: "+datedFilename);
//Debug.log("rollOver() scheduledFilename: "+scheduledFilename);
if (this.scheduledFilename.equals(datedFilename)) {
return;
}
closeFile();
File target = new File(this.scheduledFilename);
if (target.exists()) {
System.out.println("scheduledFilename- 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 + "].");
}
try
{
//setFile(this.fileName, true, this.bufferedIO, this.bufferSize);
setFile(this.fileName, false, this.bufferedIO, this.bufferSize);
} catch (IOException e) {
this.errorHandler.error("setFile(" + this.fileName + ", false) call failed.");
}
this.scheduledFilename = datedFilename;
}
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 {
rollOver();
} catch (IOException ioe) {
// if ((ioe instanceof InterruptedIOException)) {
// Thread.currentThread().interrupt();
// }
LogLog.error("rollOver() failed.", ioe);
}
}
super.subAppend(event);
}
class RolloverFileTask extends TimerTask {
RolloverFileTask() {
}
public void run() {
/**
* 通过定时打印日志,触发 subAppend(LoggingEvent event) 方法,进行日切日志文件归档。
* mod by oyp 2014-12-04 18:22:30
* @@@Time log cutting@@@
*/
try {
//Debug.log("-"+categoryName+"@@@-LOGARCHIVE-@@@ timing output log to generate a log file");
/**
* log in system.log ,not in mpsp.log, because not the same layout
*/
_log.info(Debug.LOGARCHIVE+" timing output log to generate a log file");
} catch (Exception e) {
e.printStackTrace();
}
// System.out.println(System.currentTimeMillis()+"---------------------run---------------------");
// long n = System.currentTimeMillis();
// if (n >= UmpDailyRollingFileAppender.this.nextCheck) {
// UmpDailyRollingFileAppender.this.now.setTime(n);
// UmpDailyRollingFileAppender.this.nextCheck = UmpDailyRollingFileAppender.this.rc.getNextCheckMillis(UmpDailyRollingFileAppender.this.now);
// try {
// UmpDailyRollingFileAppender.this.rollOver();
// } catch (IOException ioe) {
// if ((ioe instanceof InterruptedIOException)) {
// Thread.currentThread().interrupt();
// }
// LogLog.error("rollOver() failed.", ioe);
// System.out.println(ioe);
// }
// }
}
}
public String getCategoryName() {
return categoryName;
}
public void setCategoryName(String categoryName) {
this.categoryName = categoryName;
}
}
2、重写 PatternLayout。
package com.pouyang.log4j;
/**
* @author ouyangping
* @date Dec 4, 2014
*/
import org.apache.log4j.Layout;
import org.apache.log4j.helpers.PatternConverter;
import org.apache.log4j.helpers.PatternParser;
import org.apache.log4j.spi.LoggingEvent;
public class UmpPatternLayout extends Layout
{
public static final String DEFAULT_CONVERSION_PATTERN = "%m%n";
public static final String TTCC_CONVERSION_PATTERN = "%r [%t] %p %c %x - %m%n";
protected final int BUF_SIZE = 256;
protected final int MAX_CAPACITY = 1024;
private StringBuffer sbuf = new StringBuffer(256);
private String pattern;
private PatternConverter head;
private String timezone;
public UmpPatternLayout()
{
this("%m%n");
}
public UmpPatternLayout(String pattern)
{
this.pattern = pattern;
this.head = createPatternParser(pattern == null ? "%m%n" : pattern).parse();
}
public void setConversionPattern(String conversionPattern)
{
this.pattern = conversionPattern;
this.head = createPatternParser(conversionPattern).parse();
}
public String getConversionPattern()
{
return this.pattern;
}
public void activateOptions()
{
}
public boolean ignoresThrowable()
{
return true;
}
protected PatternParser createPatternParser(String pattern)
{
return new PatternParser(pattern);
}
public String format(LoggingEvent event)
{
if (this.sbuf.capacity() > 1024)
this.sbuf = new StringBuffer(256);
else {
this.sbuf.setLength(0);
}
PatternConverter c = this.head;
while (c != null) {
c.format(this.sbuf, event);
c = c.next;
}
//System.out.println("layout2:"+this.sbuf.toString());
//System.out.println("layout2:"+ MyUtil.bcd(this.sbuf.toString().getBytes()));
/**
* 过滤掉定时任务执行生成对账文件的日志,只打一个空串且不包含换行操作
* add by oyp 2014-12-04 18:22:44
*/
// String mesg = this.sbuf.toString();
// Debug.log(mesg);
// String[] temp = mesg.split("-");
// if ( temp.length >= 2 && ( temp[1] == null || "".equals(temp[1].trim())) ) {
// return "";
// } else {
// return mesg;
// }
String mesg = this.sbuf.toString();
if (mesg.contains(Debug.LOGARCHIVE)) {
//Debug.log(mesg);
return "";
} else {
return mesg;
}
//return this.sbuf.toString();
}
}