Как настроить 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>