Как повторно выполнить Log4j «Процедура инициализации по умолчанию»? - PullRequest
21 голосов
/ 14 января 2011

Во время выполнения я часто создаю / изменяю log4j Регистраторам, приборам, уровням, макетам и время от времени необходимо сбрасывать все обратно на значения по умолчанию.

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

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

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

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

Ответы [ 3 ]

11 голосов
/ 15 февраля 2011

В соответствии с документацией для метода doConfigure:

Чтение конфигурации из файла. Существующая конфигурация не очищается и не сбрасывается .Если вам требуется другое поведение, тогда вызовите resetConfiguration метод перед вызовом doConfigure .

Поэтому я верю, что вызов LogManager.resetConfiguration() и вызов PropertyConfigurator.configure() с теми же файлами, что и при запускеделай что хочешь.

Метод resetConfiguration() документирован в Иерархия класс.

7 голосов
/ 31 августа 2012

Этот вопрос связан с вопросом Скипоппи , на который я ответил сегодня ранее. Щедрый вопрос, который он добавил к этому, требует более хитрого решения, чем вопрос Яна Зики:

Поскольку инициализация по умолчанию представляет собой жестко закодированный статический блок, выполняемый только один раз во время загрузки класса 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");
    }
}

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

0 голосов
/ 04 июня 2018

Решение Яна Зики направило меня в правильном направлении, но я использую файл конфигурации 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" в качестве сообщения журнала.

...