Как выполнить повторный запуск Log4j "Процедура инициализации по умолчанию"?
Во время выполнения я часто создаю/изменяю log4j Loggers, Appenders, Levels, Layouts и время от времени нужно reset вернуть все значения по умолчанию.
Система Log4j четко определила Процедура инициализации по умолчанию, которая выполняется, когда log4j > классы загружаются в память. Есть ли способ повторно выполнить всю процедуру программно позже во время выполнения?
Я нашел несколько resetConfiguration()
методов в документации log4j, но не уверен, что кто-нибудь из них сделает то, что Процедура инициализации по умолчанию делает:
-
BasicConfigurator.resetConfiguration();
-
Hierarchy.resetConfiguration();
-
LogManager.resetConfiguration();
Любые другие предложения по сбросу настроек log4j более приветствуются! Спасибо.
Ответы
Ответ 1
В соответствии с документацией для метода doConfigure
:
Прочитайте конфигурацию из файла. Существующая конфигурация не очищается, а reset. Если вам требуется другое поведение, то вызвать resetConfiguration
метод перед вызовом doConfigure.
Поэтому я верю, что вызов LogManager.resetConfiguration()
и вызов PropertyConfigurator.configure()
с теми же файлами, что и при запуске, будут делать то, что вы хотите.
Метод resetConfiguration()
описан в Hierarchy классе.
Ответ 2
Этот вопрос связан с вопросом skiphoppy, который я ответил ранее. Вопрос о щедрости, который он добавил к этому, требует более сложного решения, чем у Яна Зыки:
Поскольку инициализация по умолчанию представляет собой жестко закодированный статический блок, который выполняется только один раз во время загрузки класса LogManager
, вам необходимо AOP (аспектно-ориентированное программирование), а точнее AspectJ, чтобы перехватить статический инициализатор. Я объяснил, как сделать это в моем ответе, чтобы пропустить другой вопрос.
Итак, теперь мы можем перехватить статическую инициализацию и трюк 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;
}
}
Как это работает:
В основе этого ответа лежит объяснение общей концепции АОП, поэтому я предполагаю, что вы это знаете или собираетесь что-то прочитать, чтобы понять это.
-
Log4jAspect
перехватывает статическую инициализацию LogManager
в совете around()
.
- В рамках совета вызов
proceed()
(т.е. выполнение статической инициализации) упаковывается внутри рабочего объекта, реализованного анонимным экземпляром Runnable
. Это эффективно завершает вызов в объект с помощью метода run()
, который может быть выдан по желанию. (Ага, вот наш трюк! На более динамичных языках, таких как Scala, вы назвали бы это лексической областью.)
- После переноса статической инициализации мы присваиваем экземпляр
Runnable
другому публичному статическому члену класса, поэтому он становится доступным вне аспект.
- Внутри совета мы продолжаем статическую инициализацию, вызывая метод рабочего объекта
run()
.
До сих пор, так хорошо, теперь класс LogManager
был загружен и инициализирован правильно, как если бы никакого аспекта не было. Но посмотрите на Log4jDemo.main
:
- Мы инициализируем регистратор и регистрируем некоторые события.
- Мы ожидаем 2 секунды (достаточно времени, чтобы проверить вывод консоли на то, что произошло до сих пор).
- Мы продолжаем и переиздаем инициализацию по умолчанию, снова вызвав метод рабочего объекта
run()
.
Если вы используете аргумент командной строки -Dlog4j.debug=true
, вы увидите что-то вроде этого:
log4j static initialisation
log4j: Trying to find [log4j.xml] using context classloader [email protected]
log4j: Trying to find [log4j.xml] using [email protected] class loader.
log4j: Trying to find [log4j.xml] using ClassLoader.getSystemResource().
log4j: Trying to find [log4j.properties] using context classloader [email protected]
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 [email protected]
log4j: Trying to find [log4j.xml] using [email protected] class loader.
log4j: Trying to find [log4j.xml] using ClassLoader.getSystemResource().
log4j: Trying to find [log4j.properties] using context classloader [email protected]
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, факты такие, каковы они есть, и нам нужно этот трюк. Я сомневаюсь, что в любой заданной ситуации необходимо повторно запустить полный блок инициализации по умолчанию, но поскольку вопрос был задан, я хотел точно ответить на него, а не предлагать обходное решение. Наслаждайтесь!