Динамически добавлять appender с slf4j и log4j2
Я хочу динамически создать приложение и добавить его в журнал. Однако это не представляется возможным с slf4j. Я могу добавить свой appender в log4j logger, но затем я не могу получить регистратор с помощью slf4j LoggerFactoy.
Что я хочу сделать: создаю тестовый класс (а не тест jUnit) и передаю регистратор в конструкторе для используемого тестового класса. Каждому экземпляру тестового класса нужен его собственный регистратор и приложение, которое сохраняет журнал, чтобы впоследствии его можно было использовать в отчете HTML.
Что я пробовал (для простоты я создал тест jUnit):
import static org.junit.Assert.assertEquals;
import java.util.LinkedList;
import java.util.List;
import org.apache.logging.log4j.core.LogEvent;
import org.junit.Test;
import org.slf4j.helpers.Log4jLoggerFactory;
import ch.fides.fusion.logging.ListAppender;
public class ListAppenderTest {
@Test
public void test() {
String testName = "test1";
// the log messages are to be inserted in this list
List<LogEvent> testLog = new LinkedList<>();
// create log4j logger
org.apache.logging.log4j.core.Logger log4jlogger = (org.apache.logging.log4j.core.Logger) org.apache.logging.log4j.LogManager
.getLogger("Test:" + testName);
// create appender and add it to the logger
ListAppender listAppender = new ListAppender("Test:" + testName + ":MemoryAppender", testLog);
log4jlogger.addAppender(listAppender);
// get the slf4j logger
org.slf4j.helpers.Log4jLoggerFactory loggerFactory = new Log4jLoggerFactory();
org.slf4j.Logger testLogger = loggerFactory.getLogger("Test:" + testName);
// test it
final String TEST_MESSAGE = "test message";
testLogger.info(TEST_MESSAGE);
assertEquals(1, testLog.size());
LogEvent logEvent = testLog.get(0);
assertEquals(TEST_MESSAGE, logEvent.getMessage().getFormattedMessage() );
}
}
и это мое самое основное приложение:
package ch.fides.fusion.logging;
import java.util.List;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.appender.AbstractAppender;
public class ListAppender extends AbstractAppender {
private final List<LogEvent> log;
public ListAppender(String name, List<LogEvent> testLog) {
super(name, null, null);
this.log = testLog;
}
@Override
public void append(LogEvent logEvent) {
log.add(new TestLogEvent(logEvent));
}
}
Что я могу сделать, чтобы заставить это работать? Возможно, я приближаюсь к этому с неправильного угла, но я бы хотел, чтобы не создавать свой собственный класс журнала. Любая помощь приветствуется.
Ответы
Ответ 1
Доступ и управление log4j2 по slf4j по коду/во время выполнения:
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.LoggerConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Log4j2OverSlf4jConfigurator {
final private static Logger LOGGER = LoggerFactory.getLogger(Log4j2OverSlf4jConfigurator.class);
public static void main(final String[] args) {
LOGGER.info("Starting");
LoggerContext loggerContext = (LoggerContext) LogManager.getContext();
Configuration configuration = loggerContext.getConfiguration();
LOGGER.info("Filepath: {}", configuration.getConfigurationSource().getLocation());
// Log4j root logger has no name attribute -> name == ""
LoggerConfig rootLoggerConfig = configuration.getLoggerConfig("");
rootLoggerConfig.getAppenders().forEach((name, appender) -> {
LOGGER.info("Appender {}: {}", name, appender.getLayout().toString());
// rootLoggerConfig.removeAppender(a.getName());
});
rootLoggerConfig.getAppenderRefs().forEach(ar -> {
System.out.println("AppenderReference: " + ar.getRef());
});
// adding appenders
configuration.addAppender(null);
}
}
Ссылка: https://logging.apache.org/log4j/2.x/manual/customconfig.html
Ответ 2
Daniele, ListAppender существует в Log4J-2.0 (пакет org.apache.logging.log4j.test.appender
). Он является частью дистрибутива, но он находится в банке log4j-core-tests. В основном он используется для тестов JUnit. В тестовом источнике JUnit также есть примеры конфигураций, показывающих, как настроить этот ListAppender.
Пример конфигурации выглядит примерно так:
<Configuration status="warn" packages="org.apache.logging.log4j.test">
<Appenders>
<List name="MyList">
</List>
</Appenders>
<Loggers>
<Root level="error">
<AppenderRef ref="MyList"/>
</Root>
</Loggers>
</Configuration>
Ответ 3
Я думаю, у вас такой же сценарий, как у нас. Более сложное ведение журнала в производстве, но более простое при тестировании JUnit, поэтому мы можем утверждать, что ошибок не было.
Существуют более чистые решения с использованием сборщиков, если вы используете log4j2 > 2.4 (но тогда не поддерживаете Java6), но это тот, который я получил с log4j2 2.3:
@Test
public void testClass() {
LoggerContext loggerContext = (LoggerContext) LogManager.getContext(false);
Configuration configuration = loggerContext.getConfiguration();
LoggerConfig rootLoggerConfig = configuration.getLoggerConfig("");
ListAppender listAppender = new ListAppender("testAppender");
rootLoggerConfig.addAppender(listAppender, Level.ALL, null);
new TestClass(); //this is doing writing an error like org.slf4j.LoggerFactory.getLogger(TestClass.class).error("testing this");
assertEquals(1, listAppender.getEvents().size());
}
Важно отметить, что при вызове getContext нам нужно передать "false", поскольку в противном случае он, похоже, не будет иметь тот же контекст, что и slf4j.