This post comes from my frustration with the following error messages when running tests:

log4j:WARN No appenders could be found for logger (<your class name goes here>).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.

Background

For logging purposes, I use log4j.

Most of the services I write, and that are started with a command line interface, are using the -Dlog4j.configuration=yyy configuration property to point to the proper log4j configuration. So it works.

The issue is that when you run tests for the same code that uses log4j, the tests are started differently, whether it is in your IDE (in my case IDEA) or using the build framework (in my case gradle): they do not have this property provided. So you end up with this log4j warning message. The big issue though is not really the warning message, it is the fact that for some odd reason, log4j just swallows any single output moving forward, which I am not entirely sure is the smartest way to handle it.

Solution

Here is what I did to resolve the issue (I am sure there are other ways, but this one works and is pretty simple):

Create a log4j project

In your multi-project build (gradle), create a brand new project:

log4j-test-config/
  src/
    main/
      resources/
        log4j.xml
  build.gradle

This is the content of log4j.xml:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
  <appender name="ConsoleAppender" class="org.apache.log4j.ConsoleAppender">
    <layout class="org.apache.log4j.PatternLayout">
      <param name="ConversionPattern" value="%d{yyyy/MM/dd HH:mm:ss.SSS} %p [%c{1}] %m%n" />
    </layout>
  </appender>
  <root>
    <priority value="warn"/>
    <appender-ref ref="ConsoleAppender"/>
  </root>
</log4j:configuration>

and this is the content of build.gradle

apply plugin: 'java'
dependencies {
  runtime spec.external.slf4jLog4j
  runtime spec.external.log4j
}

Add a test dependency in your other projects

Thanks to gradle support for multi-project build, in any project in which you have tests, simply add a (testRuntime) dependency to the log4j-test-config project:

apply plugin: 'groovy'

dependencies {
  compile spec.external.linkedinUtilsCore
  compile spec.external.slf4j
  compile spec.external.zookeeper

  testCompile spec.external.groovyTest
  testCompile spec.external.linkedinUtilsGroovy
  testCompile spec.external.junit
  testRuntime spec.external.slf4jLog4j
  testRuntime project(':log4j-test-config') // this is where the "magic" happens!
}

Running tests

Now when you run your tests with gradle, it will automatically pick up the jar file generated by the log4j-test-config project and add it to the classpath => the file log4j.xml will be on the classpath and log4j will be happy.

If you issue gradle cleanIdea idea then gradle will rebuild the IDEA project and add the proper dependency within the ide itself => when you run your test directly in IDEA, then the same happens and you get log4j output!

info_48x48.pngYou can simply change warn in the log4j.xml file with any other value during testing/debugging and it will automatically be picked up by IDEA or gradle…