How to set the log level to DEBUG during Junit tests?

I am using SLF4J with LOG4J, and the configurations are usually in the log4j.properties, and it sets the log level to INFO.

However during the tests I would like to set the logs to DEBUG.

I can't see a way to automate this, neither to have something like log4j.tests.properties that would be loaded only during tests.

So I've tried doing this programmatically in the test setup (the @BeforeClass):

LogManager.getRootLogger().setLevel(Level.ALL);

With no success...

I am using these versions:

    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.5</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>1.7.5</version>
    </dependency>

How can I achieve this result?

EDIT: I think I wasn't clear enough. This question is not about setting the correct log level... It is about setting the DEBUG log level when running Junit tests, and setting INFO log level in any other situation. I want to automate this.


Solution 1:

You do not need to give the JVM a different log implementation.

The logging code searches for the log4j.properties file using the classpath. So all you need to do is ensure that your test log4j.properties file is in a location that it will find before the release file.

I use Maven, which lays out files in directories to make that easy. My release log4j.properties goes in the directory src/main/resources. My test version goes in src/test/resources. The Eclipse build path (classpath) is set up to search src/test/resources before src/main/resources, so your unit tests use the test file. The JAR (or WAR) build instructions use the files from src/main/resources.

Solution 2:

Below ROOT log level change will work for junit to set logging to desired level.

import org.slf4j.LoggerFactory;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;

@Before
public void setUp() {
    final Logger logger = (Logger)LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
    logger.setLevel(Level.ALL);
}

Solution 3:

You can use the org.apache.logging.log4j.core.config.Configurator which has a setAllLevels method.

In your test you can use it in a @Before method:

  @Before
  public void changeLogLevel() {
    Configurator.setAllLevels("", Level.ALL);
  }

NOTE: tested with binding org.slf4j:slf4j-log4j12:1.7.26

Solution 4:

maybe not entirely related to the question in the first place, however a google query will lead most developers to this topic when they search for a way on

how to change the log level for certain junit test methods.

The way to go is to use a custom junit MethodRule that accesses the loggers and re-configures the log level per package.

With below classes, you can achieve this. It sets the log level for packages and classes as defines in the annotation of a test method and when the test has finished, sets the loggers back to their initial state. We assume the default log level is set to INFO currently.

@Test
@LogLevel(packageToLevel = { "my.java.package=DEBUG", "my.other.java.package.ClassNeedsTracing=TRACE" })
public void allLogsOfThatPackageAreInDebugNow() {
   ...
}

@Test
@LogLevel(packageToLevel = { "my.java.package=TRACE", "my.java.package.ClassNoTracing=TRACE" })
public void allLogsOfThatPackageAreInTraceNowExceptOneClass() {
   ...
}

To achieve this, you need so specify the test rule in your test class:

@Rule
LogLevelRule logLevelRule = new LogLevelRule();

Classes needed found below:

import java.util.HashMap;
import java.util.Map;

import org.apache.log4j.Level;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.junit.rules.MethodRule;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.Statement;

/**
 * a Junit Rule that check for LogLevel annotation on methods and activates the configured log level per package. After
 * the test was executed, restores the previous log level.
 */
public class LogLevelRule implements MethodRule {

    @Override
    public Statement apply(Statement base, FrameworkMethod method, Object target) {

        return new Statement() {
            @Override
            public void evaluate() throws Throwable {

                // activate log level desired, remember what they were
                Map<String, Level> existingPackageLogLevel = new HashMap<>();
                LogLevel logLevelAnnotation = method.getAnnotation(LogLevel.class);
                if (logLevelAnnotation != null) {
                    activate(logLevelAnnotation.packageToLevel(), existingPackageLogLevel);
                }

                // run the test
                Throwable testFailure = evaluateSafely(base);

                // revert the log level back to what it was
                if (!existingPackageLogLevel.isEmpty()) {
                    deactivate(existingPackageLogLevel);
                }

                if (testFailure != null) {
                    throw testFailure;
                }
            }

            /**
             * execute the test safely so that if it fails, we can still revert the log level
             */
            private Throwable evaluateSafely(Statement base) {
                try {
                    base.evaluate();
                    return null;
                } catch (Throwable throwable) {
                    return throwable;
                }
            }
        };
    }

    /**
     * activates the log level per package and remember the current setup
     *
     * @param packageToLevel
     *            the configuration of the annotation
     * @param existingPackageLogLevel
     *            where to store the current information
     */
    protected void activate(String[] packageToLevel, Map<String, Level> existingPackageLogLevel) {
        for (String pkgToLevel : packageToLevel) {
            String[] split = pkgToLevel.split("=");
            String pkg = split[0];
            String levelString = split[1];
            Logger logger = LogManager.getLogger(pkg);
            Level level = logger.getLevel();
            existingPackageLogLevel.put(pkg, level);
            logger.setLevel(Level.toLevel(levelString));
        }
    }

    /**
     * resets the log level of the changes packages back to what it was before
     *
     * @param existingPackageLogLevel
     */
    protected void deactivate(Map<String, Level> existingPackageLogLevel) {
        for (Map.Entry<String, Level> e : existingPackageLogLevel.entrySet()) {
            LogManager.getLogger(e.getKey()).setLevel(e.getValue());
        }
    }

}




import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * marks a method to use a different log level for the execution phase
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface LogLevel {
    String[] packageToLevel();
}

Solution 5:

Usually LEVEL.FINEST should do it... but take a look to http://saltnlight5.blogspot.mx/2013/08/how-to-configure-slf4j-with-different.html to see logging frameworks implementations considereation.