2011-10-02 75 views
18

我目前正在用java編寫一個大項目,有很多類,有些類很安靜,只是用很少的方法表示對象。 我在我的主類中設置了一個記錄器,它工作正常。 我想只能使用一個記錄器(帶有一個控制檯appender)與所有類。 我試圖將對記錄器的引用傳遞給不同的類,但它看起來不正確。另外,有時候我會在不運行main的情況下對這些類進行測試,因此記錄器沒有爲其他類進行初始化。如何使用log4j與多個類?

什麼是最好的方法來實現這一點,我的意思是,如何從不同的類登錄到一個日誌,類之間沒有硬依賴關係,並且能夠獨立使用每個類的日誌?

回答

16

如果我理解正確的,你有什麼在一分鐘是:

public class Main { 
    public static final Logger LOGGER = Logger.getLogger(Main.class); 
} 

public class AnotherClass { 
    public void doSomething() { 
     Main.LOGGER.debug("value=" + value); 
    } 
} 

,或者你引用傳遞一個記錄器到類的構造函數。

public class Main { 
    private static final Logger LOGGER = Logger.getLogger("GLOBAL"); 
} 

public class AnotherClass { 
    private final Logger LOGGER = Logger.getLogger("GLOBAL"); 

    public void doSomething() { 
     LOGGER.debug("value=" + value); 
    } 
} 

這將使用完全相同的記錄器,Logger.getLogger返回兩個調用相同的對象:

首先,你可以通過簡單的使用傳遞給Logger.getLogger,喜歡同樣的值使用一個全局記錄。你不再有類之間的依賴關係,這會起作用。

,我從你的意見收集的另一件事是,你是用手工配置(使用BasicConfigurator.configure,這其中大部分是沒有必要的時候,你應該通過簡單地增加一個log4j.properties或log4j的做你的配置。 xml添加到你的類路徑中,在Eclipse中,這是通過將它添加到src /(或者如果你使用maven的情況下是src/main/resources)來實現的,如果你使用的是junit,那麼把它添加到test/source目錄(或者src/test/resources with maven)。這是一種更好的配置log4j的長期方式,因爲您不必在類之間傳遞信息。

另外,推薦使用記錄器的方式是將類傳遞給Logger.getLogger()。通過這種方式,你可以篩選基於類名的輸出,這通常比只是有一個全球記錄有用得多:

public class Main { 
    private static final Logger LOGGER = Logger.getLogger(Main.class); 
    public static final main(String[] args) { 
     LOGGER.debug("started"); 
    } 
} 

public class AnotherClass { 
    private final Logger LOGGER = Logger.getLogger(this.getClass()); 

    public void doSomething() { 
     LOGGER.debug("value=" + value); 
    } 
} 
在log4j.properties

然後,您可以配置一個附加器到一個文件。

# Set root logger level to DEBUG and its only appender to A1. 
log4j.rootLogger=DEBUG, A1 

# A1 is set to be a ConsoleAppender. 
log4j.appender.A1=org.apache.log4j.ConsoleAppender 

# A1 uses PatternLayout. 
log4j.appender.A1.layout=org.apache.log4j.PatternLayout 
log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n 

最後,沒有必要將所有的記錄器聲明爲靜態的。如果你正在做對象創建的批次 [*],這隻會產生顯着的差異。將記錄器聲明爲非靜態字段允許您使用Logger.getLogger(this.getClass());,在這種情況下,將記錄器添加到類將成爲單行的剪切和粘貼。請參閱Should I declare Log references static or not?(不幸的是,鏈接到wiki頁面已損壞),但slf4j page也包含一個很好的解釋。因此,除非你有一個非常好的理由,否則使用非靜態字段。

卡梅隆說得對,他應該儘量使用slf4j,如果可能的話,它有一個殺手級功能,可以使用多個日誌記錄框架。

[*]我的意思是很多。

+0

我的確如你所提到的那樣,但只有Main類中的日誌被打印,其他方法中的日誌不會被打印?如何解決它 –

5

Your logger instances should typically be private, static and final。通過這樣做,每個類都將擁有它自己的記錄器實例(該類會在加載類時創建),以便您可以識別創建日誌記錄的類,並且不再需要跨類傳遞記錄器實例。

+0

我在哪裏放「BasicConfigurator.configure();」 在沒有主要方法的類中? – stdcall

+0

@Mellowcandle,你不需要在每個類中調用'BasicConfigurator.configure()'。做到這一點,只有在初始化您的應用的類的主要方法中,並且僅在必須時。編輯:['BasicConfigurator.configure'](http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/BasicConfigurator.html#configure%28%29)用於創建log4j設置,當你缺少一個配置了appender和佈局的功能性log4j.properties文件時。 –

+0

你的意思是什麼?我需要在幾個類上運行單元測試。我什麼時候需要運行它? – stdcall

4

最好的辦法是讓每個類都有它自己的記錄器(以該類的名字命名),然後設置你的配置,使它們都追加到同一個appender。

例如:

class A { 
    private static final Logger log = Logger.getLogger(A.class); 
} 

class B { 
    private static final Logger log = Logger.getLogger(B.class); 
} 

然後你可以log4j.properties看起來像log4j的文檔中的例子:

# Set root logger level to DEBUG and its only appender to A1. 
log4j.rootLogger=DEBUG, A1 

# A1 is set to be a ConsoleAppender. 
log4j.appender.A1=org.apache.log4j.ConsoleAppender 

# A1 uses PatternLayout. 
log4j.appender.A1.layout=org.apache.log4j.PatternLayout 
log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n 

兩個AB將記錄到根logger,因此到相同的appender(在這種情況下是控制檯)。

這會給你你想要的:每個類是獨立的,但他們都寫入相同的日誌。您還可以獲得獎金功能,您可以更改log4j配置中每個類的日誌記錄級別。

另外,如果項目仍處於早期開發階段,您可能需要考慮轉向slf4j。 slf4j比log4j有一些增強功能,使它更易於使用。

+0

我在哪裏放置「BasicConfigurator.configure();」在沒有主要方法的類中? – stdcall

+2

@Mellowcandle:不要。使用'log4j.properties'文件。你只需要把它放在類路徑上,log4j就可以使用它。 –

+0

@CameronSkinner我的確如你所說,但是隻有Main類中的日誌被打印在控制檯中,其他方法的日誌不會被打印,如何修復它? –

1

您有多個記錄器實例的原因是因爲您希望它們在記錄時表現不同(通常通過輸出它們配置的類名稱)。如果你不關心這一點,你可以在一個類中創建一個靜態記錄器實例,並在整個地方使用它。

要創建單個記錄器,您可以簡單地創建一個靜態實用程序日誌記錄類爲單點記錄器,因此如果我們需要更改記錄器包,則只會更新此類。

final public class Logger { 
    private static final org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger("Log"); 

    enum Level {Error, Warn, Fatal, Info, Debug} 

    private Logger() {/* do nothing */}; 

    public static void logError(Class clazz, String msg) { 
     log(Level.Error, clazz, msg, null); 
    } 

    public static void logWarn(Class clazz, String msg) { 
     log(Level.Warn, clazz, msg, null); 
    } 

    public static void logFatal(Class clazz, String msg) { 
     log(Level.Fatal, clazz, msg, null); 
    } 

    public static void logInfo(Class clazz, String msg) { 
     log(Level.Info, clazz, msg, null); 
    } 

    public static void logDebug(Class clazz, String msg) { 
     log(Level.Debug, clazz, msg, null); 
    } 


    public static void logError(Class clazz, String msg, Throwable throwable) { 
     log(Level.Error, clazz, msg, throwable); 
    } 


    public static void logWarn(Class clazz, String msg, Throwable throwable) { 
     log(Level.Warn, clazz, msg, throwable); 
    } 

    public static void logFatal(Class clazz, String msg, Throwable throwable) { 
     log(Level.Fatal, clazz, msg, throwable); 
    } 

    public static void logInfo(Class clazz, String msg, Throwable throwable) { 
     log(Level.Info, clazz, msg, throwable); 
    } 

    public static void logDebug(Class clazz, String msg, Throwable throwable) { 
     log(Level.Debug, clazz, msg, throwable); 
    } 

    private static void log(Level level, Class clazz, String msg, Throwable throwable) { 
     String message = String.format("[%s] : %s", clazz, msg); 
     switch (level) { 
      case Info: 
       logger.info(message, throwable); 
       break; 
      case Warn: 
       logger.warn(message, throwable); 
       break; 
      case Error: 
       logger.error(message, throwable); 
       break; 
      case Fatal: 
       logger.fatal(message, throwable); 
       break; 
      default: 
      case Debug: 
       logger.debug(message, throwable); 
     } 
    } 

}