Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Redirect slf4j to string

How can I configure slf4j to redirect all logged information to a Java string?

This is sometimes useful in unit tests, e.g. to test no warnings are printed when loading a servlet, or to make sure a forbidden SQL table is never used.

like image 485
Elazar Leibovich Avatar asked Aug 15 '11 13:08

Elazar Leibovich


People also ask

Is SLF4J better than log4j?

Unlike log4j, SLF4J (Simple Logging Facade for Java) is not an implementation of logging framework, it is an abstraction for all those logging frameworks in Java similar to log4J. Therefore, you cannot compare both. However, it is always difficult to prefer one between the two.

Can we use SLF4J without log4j?

SLF4J is the API for Logback just like Log4j-api is the API for Log4j-core. When you want to use Log4j with SLF4J logger calls go to log4j-slf4j-impl then to Log4j-api and then to log4j-core. If you use the Log4j-api with Logback then Log4j-api calls go to log4j-to-slf4j, slf4j and then to Logback.

What is JCL over SLF4J?

jcl-over-slf4j. 1. It implements the public API of JCL but using SLF4J underneath, hence the name "JCL over SLF4J." Our JCL over SLF4J implementation will allow you to migrate to SLF4J gradually, especially if some of the libraries your software depends on continue to use JCL for the foreseeable future.

Is SLF4J logger thread safe?

In fact, it is not possible to guarantee that a Logger will always be thread-safe. Someone could implement their own slf4j compatible logging classes. Such an implementation could be non-thread-safe, by accident or by design. If it was, then the Logger exposed via the slf4j facade would also be non-thread-safe.


2 Answers

A bit late, but still...

As logging configurations should be easy to replace when unit testing, you could just configure to log over stdout and then capture that prior to executing the logging subject. Then set the logger to be silent for all but the subject under test.

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

And if using slf4j over log4j in tests, a simple 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

Or if you loath configuration as an external dependencies in unit tests, then programmatically configure 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);
}
like image 160
earcam Avatar answered Sep 19 '22 07:09

earcam


As I see it you have two options.

First you could implement a custom Appender (depending on which slf4j implementation you're using) which simply appends each logged statement to a StringBuffer. In this case you probably have to hold a static reference to your StringBuffer so your test classes can access it.

Second you could write your own implementation of ILoggerFactory and Logger. Again your Logger would just append all the messages to internal StringBuffers, although in this case you'd probably have multiple buffers, one for each log level. If you did it this way you'd have an easy way of retrieving the Logger instances since you'd own the factory that was distributing them.

like image 43
Mike Deck Avatar answered Sep 19 '22 07:09

Mike Deck