로그 된 모든 정보를 Java 문자열로 리디렉션하도록 slf4j
을 어떻게 구성 할 수 있습니까?slf4j를 문자열로 리디렉션
이것은 단위 테스트에 유용합니다. 서블릿을로드 할 때 아무런 경고도 출력되지 않거나 금단의 SQL 테이블이 절대로 사용되지 않도록 테스트합니다.
로그 된 모든 정보를 Java 문자열로 리디렉션하도록 slf4j
을 어떻게 구성 할 수 있습니까?slf4j를 문자열로 리디렉션
이것은 단위 테스트에 유용합니다. 서블릿을로드 할 때 아무런 경고도 출력되지 않거나 금단의 SQL 테이블이 절대로 사용되지 않도록 테스트합니다.
나는 그것을 볼 수있는 두 가지 옵션이 있습니다.
먼저 각 기록 된 문을 StringBuffer에 추가하는 사용자 정의 Appender (사용중인 slf4j 구현에 따라 다름)를 구현할 수 있습니다. 이 경우에는 테스트 클래스가 액세스 할 수 있도록 StringBuffer에 대한 정적 참조를 보유해야합니다.
둘째, ILoggerFactory 및 Logger의 고유 한 구현을 작성할 수 있습니다. 다시 말하지만 로거는 모든 메시지를 내부 StringBuffers에 추가합니다.이 경우 로그 수준마다 하나씩 여러 개의 버퍼가있을 수 있습니다. 이 방법을 사용하면 Logger 인스턴스를 배포하는 공장을 소유하고 있기 때문에 Logger 인스턴스를 쉽게 검색 할 수 있습니다. 단위 테스트, 당신은 단지 표준 출력을 통해 로그를 구성하고 로깅 대상을 실행에 그 전에 캡처 할 수있을 때 로깅 구성으로
모든 로그를 별도의 로그 파일로 리디렉션하는 것이 좋지 않습니까? 그렇게하면 로깅의 이점을 잃지 않고 원하는 컨트롤 (원하는 경우 테스트를 실행하기 전에 로그 파일을 삭제하고 파일이 언제든지 만들어 졌는지 확인할 수 있음) (출력을 문자열로 리디렉션하면 메모리 누수가 발생할 수 있음) 덜 성과)
단위 테스트로만 실행됩니다. 그리고 디스크를 만지지 않는 단위 테스트가 훨씬 빠릅니다. –
조금 늦게,하지만 여전히 ...
교체가 용이해야한다. 그런 다음 테스트중인 주제를 제외한 모든 항목에 대해 로거를 자동으로 설정하십시오.
@Test
public void test()
{
String log = captureStdOut(() -> {
// ... invoke method that shouldn't log
});
assertThat(log, is(emptyString()));
}
public static String captureStdOut(Runnable r)
{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintStream out = System.out;
try {
System.setOut(new PrintStream(baos, true, StandardCharsets.UTF_8.name()));
r.run();
return new String(baos.toByteArray(), StandardCharsets.UTF_8);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("End of the world, Java doesn't recognise UTF-8");
} finally {
System.setOut(out);
}
}
그리고 테스트에서의 log4j 이상 SLF4J를 사용하는 경우, 간단한
log4j.properties :
log4j.rootLogger=OFF, out
log4j.category.com.acme.YourServlet=INFO, out
log4j.appender.out=org.apache.log4j.ConsoleAppender
log4j.appender.out.layout=org.apache.log4j.PatternLayout
log4j.appender.out.layout.ConversionPattern=%-5p %c{1}:%L - %m%n
아니면 단위 테스트에서 외부 의존성으로 싫어 구성, 다음 프로그램의 log4j를 구성하는 경우 :
//...
static final String CONSOLE_APPENDER_NAME = "console.appender";
private String pattern = "%d [%p|%c|%C{1}] %m%n";
private Level threshold = Level.ALL;
private Level defaultLevel = Level.OFF;
//...
public void configure()
{
configureRootLogger();
configureConsoleAppender();
configureCustomLevels();
}
private void configureConsoleAppender()
{
ConsoleAppender console = new ConsoleAppender();
console.setName(CONSOLE_APPENDER_NAME);
console.setLayout(new PatternLayout(pattern));
console.setThreshold(threshold);
console.activateOptions();
Logger.getRootLogger().addAppender(console);
}
private void configureRootLogger()
{
Logger.getRootLogger().getLoggerRepository().resetConfiguration();
Logger.getRootLogger().setLevel(defaultLevel);
}
나는 이것을하기위한 간단한 모듈을 썼다. [io.earcam.utilitarian.log.slf4j] (http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22io. earcam.utilitarian % 22 % 20and % 20a % 3A % 22io.earcam.utilitarian.log.slf4j % 22) 여기에 간략히 설명되어 있습니다. https://utilitarian.earcam.io/log/slf4j/ – earcam
콘솔에 로그인하는 간단한 방법은 다음과 같습니다.
import org.slf4j.LoggerFactory;
import ch.qos.logback.classic.BasicConfigurator;
import ch.qos.logback.classic.LoggerContext;
private void LogToConsole() {
BasicConfigurator bc = new BasicConfigurator();
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
lc.reset();
bc.configure(lc);
}
정확히 무엇을하고 있는지 알지 못하지만 특정 로그 문을 주장 할 수있는 LogInterceptingTestHarness
을 작성했습니다. 당신은 비슷한 것을 사용하여 (또는 이와 비슷한) 특정 레벨에서 아무 것도 기록되지 않았다는 것을 주장 할 수 있습니다.
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import java.util.List;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.Appender;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.Logger;
import org.junit.After;
import org.junit.Before;
import org.mockito.ArgumentCaptor;
import lombok.Getter;
/**
* Use this class to intercept logs for the purposes of unit testing log output.
* <p>
* On {@link Before} of the unit test, call {@link #initHarness(Class, Level)} or {@link #initHarness(Class, Level, String)} to get a new harness and hold onto reference to it in a class-level
* variable of your unit test
* <p>
* On {@link After} of the unit test, you MUST call {@link #teardown()} in order to remove the mocked {@link #appender}
*
* @author jeff.nelson
*
*/
@Getter
public class LogInterceptingTestHarness {
private final Appender appender;
private final ArgumentCaptor<LogEvent> logEventCaptor;
private final Logger itsLogger;
private LogInterceptingTestHarness(Class<?> classInterceptLogsFor, Level logLevel, String appenderName) {
logEventCaptor = ArgumentCaptor.forClass(LogEvent.class);
appender = mock(Appender.class);
doReturn("testAppender").when(appender).getName();
doReturn(true).when(appender).isStarted();
itsLogger = (Logger) LogManager.getLogger(classInterceptLogsFor);
itsLogger.addAppender(appender);
itsLogger.setLevel(logLevel);
}
public void teardown() {
itsLogger.removeAppender(appender);
}
public List<LogEvent> verifyNumLogEvents(int numEvents) {
verify(appender, times(numEvents)).append(logEventCaptor.capture());
return logEventCaptor.getAllValues();
}
public LogEvent verifyOneLogEvent() {
return verifyNumLogEvents(1).get(0);
}
public void assertLoggedMessage(String message) {
assertLogMessage(message, logEventCaptor.getValue());
}
public void assertLoggedMessage(String message, int messageIndex) {
assertLogMessage(message, logEventCaptor.getAllValues().get(messageIndex));
}
public static void assertLogMessage(String message, LogEvent event) {
assertEquals(message, event.getMessage().getFormattedMessage());
}
public static LogInterceptingTestHarness initHarness(Class<?> classInterceptLogsFor, Level logLevel) {
return initHarness(classInterceptLogsFor, logLevel, "testAppender");
}
public static LogInterceptingTestHarness initHarness(Class<?> classInterceptLogsFor, Level logLevel, String appenderName) {
return new LogInterceptingTestHarness(classInterceptLogsFor, logLevel, appenderName);
}
}
단위 테스트에서 로그 리디렉션을했는데 다른 방법은 형식이 지정된 메시지를 String/StringBuffer에 추가하는 대신 단순히'레코드'자체를'List'에 추가하는 것입니다. –
@AlistairIsrael, 좋은 생각, 고마워. 그것들을'List'로 추론하는 것이 훨씬 쉬울 것입니다. –