1. 程式人生 > >log4j使用心得之一 -- 自定義日誌級別並分不同檔案儲存

log4j使用心得之一 -- 自定義日誌級別並分不同檔案儲存

我們組由.NET切換到JAVA,現有程式碼都需要重構,遇到的第一個問題就是缺少一個公共基礎類庫,網上找的或是其他找來的程式碼,往往不能很好的適應公司的需求,迫切的需要一個JAVA版本的公共基礎類庫,適應大夥現有的開發習慣,開發風格. 而開發這樣的基礎類庫,是我最喜歡乾的事情.

我們原有的日誌規範是,有兩個日誌級別,根據需要記錄不同的內容,每個日誌級別對應一個檔案.每個日誌級別每天歸檔一個日誌檔案.兩個日誌級別分別是:

Access Log:記錄訪問資訊,關鍵資訊輸出,api呼叫結果等

Error Log:記錄需要關注的問題,丟擲的異常,錯誤等.

轉成java後,記錄log的方法首選的是log4j, 為了不改變原有的日誌規範,做了如下定製

1 自定義log4j日誌級別

log4j原本有5個日誌級別,按級別從低到高一次是DEBUG < INFO < WARN < ERROR < FATAL.

一開始的時候,我想利用這幾個日誌級別,但很快發現了一些問題.

首先,程式使用的一些框架,類庫等會將本身輸出的資訊或是錯誤等輸出到上面幾個日誌級別中, 比如遇到錯誤時,會輸出資訊到ERROR或FATAL級別的log中,這基本還是滿足我們需求的, 但往往還會將一些除錯資訊等輸出到DEBUG或INFO級別中,造成輸出的log檔案內容較多,較亂,大夥剛剛上手就會看到log檔案裡記錄了很多沒見過的東西,肯定覺得很茫然.

瞭解到log4j可以自定義日誌級別,那麼接下來,工作開始了.

我們考慮到平時工作中遇到的問題,加上一定的擴充套件性需要,定義了4個日誌級別:

Access Log: 記錄關鍵資訊,api呼叫輸入,輸出等
Monitor Log:記錄資料庫訪問,api呼叫的耗時,後期會有其他作業來分析
Work Log:記錄作業的開始和結果或API的輸入和輸出
Error Log:記錄錯誤和異常

Error Log可以直接使用log4j的ERROR級別,由於log4j預設會把級別大於等於該級別的日誌都輸出到這個級別對應的日誌檔案中,所以日誌中能夠記錄ERROR和FATAL級別的log,而另外3個級別的log,為了不讓類庫和框架的除錯資訊干擾我們,就需要自定義log級別了.

定義一個自定義的日誌級別,非常容易. 首先,我們定義Access Log 的級別,AccessLevel,繼承自Level介面.

import org.apache.log4j.Level;

public class AccessLevel extends Level {
	public AccessLevel(int level, String name, int sysLogLevel) {
        super(level, name, sysLogLevel);
   }
}
MonitorLevel,WorkLevel也類似可以定義.

接下來,檢視log4j的原始檔org.apache.log4j.Priority,有如下定義

  public final static int FATAL_INT = 50000;
  public final static int ERROR_INT = 40000;
  public final static int WARN_INT  = 30000;
  public final static int INFO_INT  = 20000;
  public final static int DEBUG_INT = 10000;
可以看到每個級別對應一個數字,我們新增的log級別,定義在WARN和ERROR之間比較合適.我們建立一個MyLogLevel介面,對我們的自定義級別進行初始化
import org.apache.log4j.Level;
import org.apache.log4j.net.SyslogAppender;

public interface MyLogLevel {
	public static final Level ACCESS_LEVEL = new AccessLevel(35000, "ACCESS", SyslogAppender.LOG_LOCAL0);
	public static final Level MONITOR_LEVEL = new MonitorLevel(36000, "MONITOR", SyslogAppender.LOG_LOCAL0);
	public static final Level WORK_LEVEL = new WorkLevel(34000, "WORK", SyslogAppender.LOG_LOCAL0);
}
定義好我們的自定義級別後,我們就可以嘗試輸出log了,我們新建了一個LogWriter類來列印log.LogWriter類首先引用了Logger
private static Logger logger = Logger.getLogger(LogWriter.class);

輸出一個Error Log如下:
logger.error(msg.trim());

而其他3種log都是自定義級別,如下輸出一個access log:
logger.log(MyLogLevel.ACCESS_LEVEL, msg);

2 各個級別log分檔案輸出

log4j預設一個級別的log會輸出大於等於這個級別的所有log,比如WARN級別的log會打印出WARN,ERROR,FATAL級別的所有log,而為了讓我們自定義的3個日誌能分別只打印各自級別的log,我們需要做如下修改. 以Access Log為例,我們定義
public class MonitorLogAppender extends FileAppender {

	@Override
	public boolean isAsSevereAsThreshold(Priority priority) {
		// TODO Auto-generated method stub
		return MyLogLevel.MONITOR_LEVEL.equals(priority);
	}
}
這裡其實應該繼承DailyRollingFileAppender,這裡繼承FileAppender是由於log檔案命名的問題進行的改寫,這個稍後再說.

過載isAsSevereAsThreshold函式,只有等於該日誌級別,才會返回true,即只會記錄當前日誌級別的log

2 log4j.properties配置檔案

log4j.rootLogger=,error,access,monitor,work
log4j.appender.stdout=org.apache.log4j.ConsoleAppender  
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout  
log4j.appender.stdout.Threshold = DEBUG 
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} - %m%n
  
log4j.appender.error=com.xx.base.diagnostics.ErrorLogAppender
log4j.appender.error.layout=org.apache.log4j.PatternLayout  
log4j.appender.error.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} - %m%n
log4j.appender.error.datePattern='_'yyyyMMdd'.log'
log4j.appender.error.Threshold = ERROR  
log4j.appender.error.append=true
log4j.appender.error.File=${webapp.root}/log/error.log

log4j.appender.access=com.xx.base.diagnostics.AccessLogAppender
log4j.appender.access.layout=org.apache.log4j.PatternLayout  
log4j.appender.access.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} - %m%n
log4j.appender.access.datePattern='_'yyyyMMdd'.log'
log4j.appender.access.Threshold = ACCESS  
log4j.appender.access.append=true  
log4j.appender.access.File=${webapp.root}/log/access.log

log4j.appender.work=com.xx.base.diagnostics.WorkLogAppender
log4j.appender.work.layout=org.apache.log4j.PatternLayout  
log4j.appender.work.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %m%n
log4j.appender.work.datePattern='_'yyyyMMdd'.log'
log4j.appender.work.Threshold = WORK 
log4j.appender.work.append=true  
log4j.appender.work.File=${webapp.root}/log/work.log

log4j.appender.monitor=com.xx.base.diagnostics.MonitorLogAppender
log4j.appender.monitor.layout=org.apache.log4j.PatternLayout  
log4j.appender.monitor.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %m%n
log4j.appender.monitor.datePattern='_'yyyyMMdd'.log'
log4j.appender.monitor.Threshold = MONITOR
log4j.appender.monitor.append=true
log4j.appender.monitor.File=${webapp.root}/log/monitor.log


關於log4j的更多定製內容,可以關注我的下一篇博文~