最初想要在執行一段業務邏輯的時候調用一個外部接口記錄審計資訊,一直找不到一個比較優雅的方式,經過讨論覺得log4j自定義的appender或許可以實作此功能。後來就了解了一下log4j的這部分。
Apache Log4j 架構
Apache Log4j是目前在J2EE和J2SE開發中用得最多的日志架構(幾乎所有項目都用它),因為它具有出色的性能、靈活的配置以及豐富的功能,并且在業務有特殊的要求時,可以使用自定義元件來代替架構中已有的元件來滿足要求。
log4j元件介紹
Log4j主要有三個元件:
- Logger:負責供用戶端代碼調用,執行debug(Object msg)、info(Object msg)、warn(Object msg)、error(Object msg)等方法。
- Appender:負責日志的輸出,Log4j已經實作了多種不同目标的輸出方式,可以向檔案輸出日志、向控制台輸出日志、向Socket輸出日志等。
- Layout:負責日志資訊的格式化。
Logger 層級介紹
Logger的層級是logger名字指定的,如x.y 表示兩層,x層和y層,x是y的父層級,x.y所在層級是y層級
log4j.additivity.* = false : 表示目前logger不需要打到父層級所指定的appender,隻打到目前的appender;
預設true:表示目前logger将列印日志到目前的appender及所有的父層級所指定的appender
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiInBnaucTNxETYkVTYyQTZh1yY0MmYtEWYyMTLiVGN50SM1YzNwUTO58CXyYDMy8CX4UDMw8CX05WZth2YhRHdh9CXkF2bsBXdvwVbvNmLllXZ0lmLywGZvw1LcpDc0RHaiojIsJye.jpg)
Layout有多種
最常用且最靈活的輸出格式是: org.apache.log4j.PatternLayout
可以用以下的各項進行組合配置:
- %c logger名字空間的全稱,如果加上{<層數>}表示列出從最内層算起的指定層數的名字空間。
- %C 調用logger的類的全名(包含包路徑)。
- %d 日志記錄時間,{<日期格式>}使用ISO8601定義的日期格式。
- %F 調用logger的源檔案名。
- %l 日志事件的發生位置,包括類目名、發生的線程,以及在代碼中的行數。
- %L 調用logger的代碼行%m 輸出消息。
- %M 調用logger的方法名。
- %n 目前平台下的換行符。
- %p 該條日志的優先級。
- %r 從程式啟動時到記錄該條日志時已經經過的毫秒數。
- %t 産生該日志事件的線程名。
- %x 按NDC(Nested Diagnostic Context,線程堆棧)順序輸出日志。
- %X 按MDC(Mapped Diagnostic Context,線程映射表)輸出日志。通常用于多個用戶端連接配接同一台伺服器,友善伺服器區分是那個用戶端通路留下來的日志。
- %% 顯示一個百分号。)
執行順序及關系
調用Log4j輸出日志時,調用各個元件的順序:
- 1、日志資訊傳入 Logger。
- 2、将日志資訊封裝成 LoggingEvent 對象并傳入 Appender。
- 3、在 Appender 中調用 Filter 對日志資訊進行過濾,調用 Layout 對日志資訊進行格式化,然後輸出。
圖示:
實作自定義log4j Appender
明白了log4j的結構關系實作自定義的log4j appender就迎刃而解了
繼承log4j公共的基類:AppenderSkeleton
列印日志核心方法:abstract protected void append(LoggingEvent event);
初始化加載資源:public void activateOptions(),預設實作為空
釋放資源:public void close()
是否需要按格式輸出文本:public boolean requiresLayout()
正常情況下我們隻需要覆寫append方法即可。然後就可以在log4j中使用了
demo代碼:
Java代碼
- import org.apache.log4j.AppenderSkeleton;
- import org.apache.log4j.spi.LoggingEvent;
- public class HelloAppender extends AppenderSkeleton {
- private String account ;
- @Override
- protected void append(LoggingEvent event) {
- System.out.println("Hello, " + account + " : "+ event.getMessage());
- }
- public void close() {
- // TODO Auto-generated method stub
- public boolean requiresLayout() {
- return false;
- public String getAccount() {
- return account;
- public void setAccount(String account) {
- this.account = account;
- }
- public static void main(String[] args) {
- Log log = LogFactory.getLog("helloLog") ;
- log.info("I am ready.") ;
引用
log4j.properties 配置
log4j.logger.helloLog=INFO, hello
log4j.appender.hello=HelloAppender
log4j.appender.hello.account=World
執行main函數,輸出結果
Hello, World : I am ready.