![](/img/trans.png)
[英]How can you combine log4j default initialization with configureAndWatch()?
[英]How to Re-Execute Log4j “Default Initialization Procedure”?
在運行時,我經常創建/修改log4j Loggers,Appenders,Levels,Layouts以及需要將所有內容重置為默認值的時間。
Log4j系統具有明確定義的默認初始化過程 ,該過程在將log4j類加載到內存時執行。 有沒有辦法在以后的運行時以編程方式重新執行整個過程?
我在log4j文檔中找到了幾個resetConfiguration()
方法,但不確定它們中的任何一個是否會執行默認初始化過程 :
BasicConfigurator.resetConfiguration();
Hierarchy.resetConfiguration();
LogManager.resetConfiguration();
關於重置log4j配置的任何其他建議都是受歡迎的! 謝謝。
這個問題與我今天早些時候回答的skiphoppy問題有關。 他在這里添加的賞金問題需要一個比Jan Zyka更棘手的解決方案:
因為默認初始化是在LogManager
類加載期間僅執行一次的硬編碼靜態塊,所以需要AOP(面向方面編程),更具體地說是AspectJ以攔截靜態初始化器。 我在回答 skiphoppy的另一個問題時解釋了如何做到這一點。
好的,現在我們可以攔截靜態初始化並誘騙LogManager告訴我們URL,但是為了重新執行整個代碼塊,我們需要另一種稱為工作對象模式的技巧。 以下是示例代碼,說明如下:
示例應用程序類:
import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Logger;
public class Log4jDemo {
public static Runnable log4jDefaultInitCmd;
private static Logger logger = Logger.getLogger("scrum-master.de");
public static void main(String[] args) throws InterruptedException {
BasicConfigurator.configure();
logger.info("Hello world!");
logger.info("Now sleeping for 2 sec...");
Thread.sleep(2000);
logger.info("I am awake again!");
if (log4jDefaultInitCmd != null) {
logger.info("Re-running log4j default initialisation");
log4jDefaultInitCmd.run();
}
logger.info("Done");
}
}
Aspect攔截LogManager
靜態初始化:
import org.apache.log4j.LogManager;
public aspect Log4jAspect {
Object around() : staticinitialization(LogManager) {
System.out.println("log4j static initialisation");
Log4jDemo.log4jDefaultInitCmd = new Runnable() {
@Override public void run() {
proceed();
}
};
Log4jDemo.log4jDefaultInitCmd.run();
return null;
}
}
這個怎么運作:
解釋AOP的一般概念超出了這個答案的范圍,所以我假設你知道它或者要讀一些東西以便理解它。
Log4jAspect
在around()
建議中攔截LogManager
的靜態初始化。 proceed()
調用(即靜態初始化的執行)被打包在由匿名Runnable
實例實現的worker對象中。 這有效地使用run()
方法將調用包裝到一個對象中,該方法可以隨意發出。 (啊哈,這是我們的伎倆!在像Scala這樣的更動態的語言中,你會稱之為詞法范圍。) Runnable
實例分配給另一個類的公共靜態成員,以便在方面之外可以訪問它。 run()
方法繼續靜態初始化。 到目前為止,非常好,現在LogManager
類已經被正確加載和初始化,就好像沒有方面確實存在一樣。 但現在看看Log4jDemo.main
:
run()
方法繼續並重新發出默認初始化 。 如果使用命令行參數-Dlog4j.debug=true
,您將看到如下內容:
log4j static initialisation
log4j: Trying to find [log4j.xml] using context classloader sun.misc.Launcher$AppClassLoader@17182c1.
log4j: Trying to find [log4j.xml] using sun.misc.Launcher$AppClassLoader@17182c1 class loader.
log4j: Trying to find [log4j.xml] using ClassLoader.getSystemResource().
log4j: Trying to find [log4j.properties] using context classloader sun.misc.Launcher$AppClassLoader@17182c1.
log4j: Using URL [file:/C:/Dokumente%20und%20Einstellungen/Robin/Eigene%20Dateien/java-src/dummy2/bin/log4j.properties] for automatic log4j configuration.
log4j: Reading configuration from URL file:/C:/Dokumente%20und%20Einstellungen/Robin/Eigene%20Dateien/java-src/dummy2/bin/log4j.properties
log4j: Parsing for [root] with value=[debug, stdout].
log4j: Level token is [debug].
log4j: Category root set to DEBUG
log4j: Parsing appender named "stdout".
log4j: Parsing layout options for "stdout".
log4j: Setting property [conversionPattern] to [%d{ABSOLUTE} %5p %c{1}:%L - %m%n].
log4j: End of parsing for "stdout".
log4j: Setting property [target] to [System.out].
log4j: Parsed "stdout" options.
log4j: Finished configuring.
12:41:22,647 INFO de:13 - Hello world!
0 [main] INFO scrum-master.de - Hello world!
12:41:22,663 INFO de:14 - Now sleeping for 2 sec...
16 [main] INFO scrum-master.de - Now sleeping for 2 sec...
12:41:24,663 INFO de:16 - I am awake again!
2016 [main] INFO scrum-master.de - I am awake again!
12:41:24,663 INFO de:18 - Re-running log4j default initialisation
2016 [main] INFO scrum-master.de - Re-running log4j default initialisation
log4j: Trying to find [log4j.xml] using context classloader sun.misc.Launcher$AppClassLoader@17182c1.
log4j: Trying to find [log4j.xml] using sun.misc.Launcher$AppClassLoader@17182c1 class loader.
log4j: Trying to find [log4j.xml] using ClassLoader.getSystemResource().
log4j: Trying to find [log4j.properties] using context classloader sun.misc.Launcher$AppClassLoader@17182c1.
log4j: Using URL [file:/C:/Dokumente%20und%20Einstellungen/Robin/Eigene%20Dateien/java-src/dummy2/bin/log4j.properties] for automatic log4j configuration.
log4j: Reading configuration from URL file:/C:/Dokumente%20und%20Einstellungen/Robin/Eigene%20Dateien/java-src/dummy2/bin/log4j.properties
log4j: Parsing for [root] with value=[debug, stdout].
log4j: Level token is [debug].
log4j: Category root set to DEBUG
log4j: Parsing appender named "stdout".
log4j: Parsing layout options for "stdout".
log4j: Setting property [conversionPattern] to [%d{ABSOLUTE} %5p %c{1}:%L - %m%n].
log4j: End of parsing for "stdout".
log4j: Setting property [target] to [System.out].
log4j: Parsed "stdout" options.
log4j: Finished configuring.
12:41:24,663 INFO de:21 - Done
2016 [main] INFO scrum-master.de - Done
Tadaa! 如您所見,默認初始化確實已執行兩次。 日志輸出證明了這一點。 例如,您在日志中看到兩次Using URL [file:/(...)]
。
結論:
雖然這不是一個特別好的方法來重新發布log4j默認初始化,而不是更加可取的情況,它不是硬編碼但是通過API調用暴露給用戶,事實就是這樣,我們需要這個技巧。 我懷疑在任何給定的情況下都應該重新運行完整的默認初始化塊,但是因為問了這個問題,我想准確地回答它,而不是建議一個解決方法。 請享用!
Jan Zyka的解決方案指出了我正確的方向,但我使用的是XML配置文件而不是屬性文件。 這是適合我的代碼:
LogManager.resetConfiguration(); // clear any existing config first
LoggerRepository loggerRepository = LogManager.getLoggerRepository();
DOMConfigurator domConfigurator = new DOMConfigurator();
try (
InputStream is = MyClassName.class.getResourceAsStream("/log4j.xml");
) {
domConfigurator.doConfigure(is, loggerRepository);
}
LOGGER.info("abc123");
我得到一個格式正確的log4j日志條目,其中“abc123”作為日志消息。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.