Как настроить log4j2 RollingFileAppender?

Мы используем log4j 1.2.x для входа в наш продукт и надеемся перейти на log4j 2.x в ближайшем будущем. Одна из реализованных нами функциональных возможностей заключается в том, чтобы регистрировать системную информацию и другие важные параметры в каждом новом генерируемом лог-файле пролонгации. В log4j 1.2.x мы реализовали то, что мы расширили RollingFileAppender класс log4j и переопределил rollOver() ниже приведен фрагмент кода реализации

@Override
public void rollOver() {

    super.rollOver(); //We are not modifying it's default functionality but as soon as rollOver happens we apply our logic 

    // 
    // Logic to log required system properties and important parameters.
    //

}

Теперь, когда мы хотим перейти на log4j2, мы ищем новое решение для достижения той же функциональности. Но, как я вижу исходный код для log4j2, он сильно отличается от старого исходного кода. RollingFileAppender класс не содержит rollover() метод, как он был перемещен в RollingManagerhelper и было установлено private также.

Разработка полного нового пакета и расширение / реализация некоторых абстрактных / вспомогательных классов из log4j2 является одним из возможных решений для нас, но это потребует большого количества кодирования / копирования, поскольку мы не изменяем то, что RollingFileAppender скорее нам нужно только небольшое расширение к нему. Есть ли простое решение для этого?

ОБНОВИТЬ

Я создал пользовательский поиск в соответствии с предложением в ответах и ​​ниже, как я его создал;

@Plugin(name = "property", category = StrLookup.CATEGORY)
public class CustomLookup extends AbstractLookup {

private static AtomicLong aLong = new AtomicLong(0);

@Override
public String lookup(LogEvent event, String key) {

    if (aLong.getAndIncrement() == 0) {
        return "this was first call";
    }
    if (key.equalsIgnoreCase("customKey")) {
        return getCustomHeader();
    } else {
        return "non existing key";
    }
}

private static String getCustomHeader() {

    // Implementation of custom header
    return "custom header string";

}}

Но это не сработало, как упоминалось; это всегда печатает this was first call в шапке. Я также попытался поставить перерыв на первом if и что я заметил, так это то, что он вызывается только один раз. Поэтому я боюсь, что класс customLookup инициализируется только при запуске, когда log4j2 инициализирует свои свойства из конфигурации xml. Я не знаю, как еще я мог бы реализовать этот пользовательский класс поиска.

ОБНОВЛЕНИЕ 2

После вышеупомянутой реализации я попробовал это немного по-другому, как показано ниже;

private static AtomicLong aLong = new AtomicLong(0);

@Override
public String lookup(LogEvent event, String key) {
    return getCustomHeader(key);
}

private static String getCustomHeader(final String key) {

    if (aLong.getAndIncrement() == 0) {
        return "this was first call";
    }
    if (key.equalsIgnoreCase("customKey")) {
        // Implementation for customKey
        return "This is custom header";
    } else {
        return "non existing key";
    }
}

Но это делает то же самое, что и хорошо. log4j2 создает заголовки при инициализации из своего конфигурационного файла xml, а затем использует заголовки из памяти. return значение переопределено lookup() метод не может быть изменен динамически, так как он вызывается только во время инициализации. Любая дополнительная помощь будет высоко ценится.

2 ответа

Решение

Альтернативой использованию встроенных поисков является создание пользовательского поиска. Это можно сделать в несколько строк кода с помощью плагина log4j2. Затем пользовательский поиск предоставляет точное значение, которое вы хотите показывать в заголовке файла при каждом пролонгации.

Код плагина будет выглядеть примерно так:

package com.mycompany;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.lookup.AbstractLookup;
import org.apache.logging.log4j.core.lookup.StrLookup;

/**
 * Looks up keys from a class SomeClass which has access to all
 * information you want to provide in the log file header at rollover.
 */
@Plugin(name = "setu", category = StrLookup.CATEGORY)
public class SetuLookup extends AbstractLookup {

    /**
     * Looks up the value of the specified key by invoking a
     * static method on SomeClass.
     *
     * @param event The current LogEvent (ignored by this StrLookup).
     * @param key  the key to be looked up, may be null
     * @return The value of the specified key.
     */
    @Override
    public String lookup(final LogEvent event, final String key) {
        return com.mycompany.SomeClass.getValue(key);
    }
}

Затем в вашей конфигурации вы можете использовать заголовок макета шаблона для вывода этого при каждом переходе:

<RollingFile name="RollingFile" fileName="logs/app.log"
             filePattern="logs/$${date:yyyy-MM}/app-%d{MM-dd-yyyy}.log.gz">

  <!-- use custom lookups to access arbitrary internal system info -->
  <PatternLayout header="${setu:key1} ${setu:key2}">
    <Pattern>%d %m%n</Pattern>
  </PatternLayout>
  <Policies>
    <TimeBasedTriggeringPolicy />
  </Policies>
</RollingFile>

Руководство log4j2 содержит подробную информацию о сборке / развертывании пользовательских плагинов. Краткое содержание:

Самый простой способ - построить свою банку с Maven; это приведет к тому, что процессор аннотаций log4j2 создаст двоичный индексный файл в jar, поэтому ваш плагин может быть быстро найден log4j2.

Альтернативой является указание имени пакета вашего класса плагина в вашей конфигурации log4j2.xml. packages атрибут:

<Configuration status="warn" packages="com.mycompany">
  ...

ОБНОВЛЕНИЕ: обратите внимание, что в вашей реализации поиска вы можете проявить себя настолько креативно, насколько это необходимо. Например:

package com.mycompany;

public class SomeClass {
    private static AtomicLong count = new AtomicLong(0);

    public static String getValue(final String key) {
        if (count.getAndIncrement() == 0) { // is this the first call?
            return ""; // don't output a value at system startup
        }
        if ("FULL".equals(key)) {
            // returns info to shown on rollover, nicely formatted
            return fullyFormattedHeader();
        }
        return singleValue(key);
    }
    ....
}

Это можно сделать с помощью конфигурации. Вы можете использовать заголовок макета шаблона для вывода информации. Это будет включено при каждом опрокидывании.

<RollingFile name="RollingFile" fileName="logs/app.log"
             filePattern="logs/$${date:yyyy-MM}/app-%d{MM-dd-yyyy}.log.gz">

  <!-- use built-in lookups for system info or system properties -->
  <PatternLayout header="${java:runtime} - ${java:vm} - ${java:os}">
    <Pattern>%d %m%n</Pattern>
  </PatternLayout>
  <Policies>
    <TimeBasedTriggeringPolicy />
  </Policies>
</RollingFile>
Другие вопросы по тегам