将slf4j重定向到字符串

时间:2011-08-15 13:54:57

标签: java logging slf4j

如何配置slf4j将所有记录的信息重定向到Java字符串?

这有时在单元测试中很有用,例如测试加载servlet时不打印任何警告,或确保永远不会使用禁用的SQL表。

5 个答案:

答案 0 :(得分:4)

有点晚了,但还是......

由于日志配置在单元测试时应该很容易替换,您可以配置为登录stdout然后在执行日志主题之前捕获它。 然后将记录器设置为对所有测试对象以外的所有记录器保持沉默。

errno

如果在测试中使用slf4j而不是log4j,那么一个简单的 log4j.properties

@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:

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

答案 1 :(得分:3)

我认为你有两种选择。

首先,您可以实现自定义Appender(取决于您正在使用的slf4j实现),它只是将每个记录的语句附加到StringBuffer。在这种情况下,您可能必须持有对StringBuffer的静态引用,以便您的测试类可以访问它。

其次,您可以编写自己的ILoggerFactory和Logger实现。同样,您的Logger会将所有消息附加到内部StringBuffers,尽管在这种情况下您可能有多个缓冲区,每个日志级别一个缓冲区。如果你这样做,你就可以轻松地检索Logger实例,因为你拥有分发它们的工厂。

答案 2 :(得分:0)

重定向所有日志以查看单独的日志文件应该没有意义吗?这样你就拥有了你想要的控件(你可以在运行测试之前删除日志文件并检查文件是否在任何时刻创建)而不会失去日志记录的好处(将输出重定向到String会导致内存泄漏,并且性能较差)

答案 3 :(得分:0)

这是一种登录控制台的简单方法:

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);
}

答案 4 :(得分:0)

不完全正是您正在做的事情,但我已经编写了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);
}
}