Подтвердить что ты не робот

Как выполнить повторный запуск Log4j "Процедура инициализации по умолчанию"?

Во время выполнения я часто создаю/изменяю log4j Loggers, Appenders, Levels, Layouts и время от времени нужно reset вернуть все значения по умолчанию.

Система Log4j четко определила Процедура инициализации по умолчанию, которая выполняется, когда log4j > классы загружаются в память. Есть ли способ повторно выполнить всю процедуру программно позже во время выполнения?

Я нашел несколько resetConfiguration() методов в документации log4j, но не уверен, что кто-нибудь из них сделает то, что Процедура инициализации по умолчанию делает:

  • BasicConfigurator.resetConfiguration();
  • Hierarchy.resetConfiguration();
  • LogManager.resetConfiguration();

Любые другие предложения по сбросу настроек log4j более приветствуются! Спасибо.

4b9b3361

Ответ 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, факты такие, каковы они есть, и нам нужно этот трюк. Я сомневаюсь, что в любой заданной ситуации необходимо повторно запустить полный блок инициализации по умолчанию, но поскольку вопрос был задан, я хотел точно ответить на него, а не предлагать обходное решение. Наслаждайтесь!