簡體   English   中英

Log4J:創建Logger實例的策略

[英]Log4J: Strategies for creating Logger instances

我決定將Log4J日志框架用於新的Java項目。 我想知道我應該使用什么策略來創建/管理Logger實例以及為什么?

  • 每個類的一個Logger實例,例如

     class Foo { private static final Logger log = Logger.getLogger(Foo.class); } 
  • 每個線程一個Logger實例
  • 每個應用程序的一個Logger實例
  • 水平切片:應用程序每層中的一個Logger實例(例如視圖層,控制器層和持久層)
  • 垂直切片:應用程序功能分區中的一個Logger實例

注意:在這些文章中已經在某種程度上考慮了這個問題:

什么是創建Log4j記錄器的開銷

通常,您需要為每個類設置記錄器,因為這是一個很好的邏輯組件。 線程已經是日志消息的一部分(如果您的過濾器顯示它們),因此切片記錄器可能是多余的。

關於應用程序或基於圖層的記錄器,問題是您必須找到一個地方來粘貼該Logger對象。 不是什么大不了的事。 更大的問題是某些類可能會在多個應用程序的多個級別使用...可能很難讓您的記錄器正確。 或者至少是棘手的。

...而你想要的最后一件事是你的日志記錄設置中的錯誤假設。

如果您關心應用程序和圖層並擁有簡單的分離點,那么NDC就是您的選擇。 代碼有時可能有點過分,但我不知道有多少次我被一個准確的上下文堆棧保存,顯示我從層Y中的應用程序X調用了Foo.bar()。

最常用的策略是為每個類創建一個記錄器。 如果您創建新線程,則為它們提供有用的名稱,以便可以輕松區分它們的日志記錄。

每個類創建記錄器的好處是能夠在類的包結構中打開/關閉日志記錄:

log4j.logger.org.apache = INFO
log4j.logger.com.example = DEBUG
log4j.logger.com.example.verbose = ERROR

上面將所有apache庫代碼設置為INFO級別,將日志記錄從您自己的代碼切換到DEBUG級別,但詳細軟件包除外。

我確定這不是最佳實踐,但我在應用程序之前已經解除了一些啟動時間以保存代碼行。 特別是,粘貼時:

Logger logger = Logger.getLogger(MyClass.class);

...開發人員經常忘記將“MyClass”更改為當前的類名,並且幾個記錄器總是指向錯誤的位置。 這是不好的。

我偶爾會寫:

static Logger logger = LogUtil.getInstance(); 

和:

class LogUtil {
   public Logger getInstance() {
      String callingClassName = 
         Thread.currentThread().getStackTrace()[2].getClass().getCanonicalName();
      return Logger.getLogger(callingClassName);
   }
}

該代碼中的“2”可能是錯誤的,但要點就在那里; 將性能命中(在類加載時,作為靜態變量)找到類名,以便開發人員實際上沒有辦法輸入錯誤或引入任何錯誤。

我一般不會因為失去性能而在運行時防止開發人員錯誤而感到興奮,但如果它以單例形式出現,那么一次? 通常聽起來對我來說是個好交易。

正如其他人所說,我會為每個類創建一個Logger:

private final static Logger LOGGER = Logger.getLogger(Foo.class);

要么

private final Logger logger = Logger.getLogger(this.getClass());

但是,我發現過去在記錄器中包含其他信息很有用。 例如,如果您有一個網站,則可以在每條日志消息中包含用戶ID。 這樣,您可以跟蹤用戶正在做的所有事情(對調試問題非常有用等)。

最簡單的方法是使用MDC,但您可以使用為類的每個實例創建的Logger,其名稱包括用戶ID。

使用MDC的另一個好處是,如果使用SL4J,則可以根據MDC中的值更改設置。 因此,如果您希望在DEBUG級別記錄特定用戶的所有活動,並將所有其他用戶留在ERROR,則可以。 您還可以根據MDC將不同的輸出重定向到不同的位置。

一些有用的鏈接:

http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/MDC.html

http://www.slf4j.org/api/index.html?org/slf4j/MDC.html

  • 每個類創建一個記錄器。
  • 如果您有依賴項需要Commons Logging (很可能)使用slf4j的橋接器進行Commons Logging。 使用Commons Logging接口實例化您的記錄器(每個類): private static final Log log = LogFactory.getLog(MyClass.class);
  • 使用快捷方式在IDE中顯示此模式。 我為此目的使用IDEA的實時模板
  • 使用NDC (線程本地字符串堆棧)或MDC (String→?的線程本地映射)向線程提供上下文信息。

模板示例:

private static final Log log = LogFactory.getLog($class$.class); // live template 'log'

if (log.isDebugEnabled())
    log.debug(String.format("$string$", $vars$)); // live template 'ld', 'lw', 'le' ...

另一個選項:您可以在日志記錄上嘗試AspectJ橫切。 點擊此處: 簡化您的日志記錄 (如果你不想使用AOP ,你可以看看slf4j

//Without AOP

    Class A{
       methodx(){
        logger.info("INFO");
       }
    }

    Class B{
       methody(){
        logger.info("INFO");
       }
    }

//With AOP

    Class A{
       methodx(){
         ......
       }
    }

    Class B{
       methody(){
         ......
       }
    }

    Class LoggingInterceptor{

       //Catched defined method before process
       public void before(...xyz){
         logger.info("INFO" + ...xyz);
       }

       //Catched defined method after processed          
       public void after(...xyz){
         logger.info("INFO" + ...xyz);
       }
       .....

    }

PS: AOP會更好,它是DRY(不要重復自己)的方式。

創建自定義記錄器的最佳和最簡單的方法,未鏈接到任何類名是:

// create logger
Logger customLogger = Logger.getLogger("myCustomLogName");

// create log file, where messages will be sent, 
// you can also use console appender
FileAppender fileAppender = new FileAppender(new PatternLayout(), 
                                             "/home/user/some.log");

// sometimes you can call this if you reuse this logger 
// to avoid useless traces
customLogger.removeAllAppenders();

// tell to logger where to write
customLogger.addAppender(fileAppender);

 // send message (of type :: info, you can use also error, warn, etc)
customLogger.info("Hello! message from custom logger");

現在,如果你需要同一個類中的另一個記錄器,沒問題:)只需創建一個新的

// create logger
Logger otherCustomLogger = Logger.getLogger("myOtherCustomLogName");

現在看到上面的代碼並創建新的fileappender,以便您的輸出將在其他文件中發送

這對於(至少)2種情況是有用的

  • 當你想從信息中發出單獨的錯誤並發出警告時

  • 當您管理多個流程並且需要每個流程的輸出時

PS。 有問題嗎? 隨便問! :)

常見的約定是“記錄器pr類,並使用類名作為其名稱”。 這是個好建議。

我個人的經驗是,這個記錄器變量不應該被聲明為靜態,而是一個為每個新變量檢索的實例變量。 這允許日志記錄框架根據它們的來源不同地處理兩個調用。 對於該類的所有實例(在該類加載器中),靜態變量是相同的。

此外,您應該了解所選日志后端的所有可能性。 您可能有可能沒有想到的可能性。

在部署多個EAR / WAR時,最好將log4j.jar打包到類加載器層次結構中更高的位置。
即不在WAR或EAR中,而是在容器的System-classloader中,否則多個Log4J實例將同時寫入同一文件,從而導致奇怪的行為。

如果您的應用程序遵循SOA原則,那么對於每個服務A,您將擁有以下組件:

  1. 控制器
  2. 服務實施
  3. 執行人
  4. 堅持不懈

因此,使aController.log aService.log aExecutor.log和aPersistance.log變得更加容易。

這是一個基於層的分離,因此所有Remoting / REST / SOAP類都將寫入aController.log

您的所有調度機制,后端服務等都將寫入aService.log

並且所有任務執行都寫入aExecutor.log等。

如果您有多線程執行程序,則可能必須使用日志累加器或其他技術來正確對齊多個線程的日志消息。

通過這種方式,你總會有4個日志文件,這些文件不是很多而且不會太少,我從經驗告訴你,這讓生活變得更加輕松。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM