How do I add an appender to the gradle logger via a plugin?

Greetings! I’ve been working on a plugin for gradle and wanted to improve the logging experience for our users. I ran into an issue caused by this problem: ( http://issues.gradle.org/browse/GRADLE-1715 ) – Gradle pollutes the classpath with it’s own slf4j / log4j class definitions that only use a subset of the log4j functionality which prevents plugins from using their own log4j dependency, so some methods fail with: “java.lang.NoSuchMethodError: org.apache.log4j.Logger.getAppender(Ljava/lang/String;)Lorg/apache/log4j/Appender;”

Although I imagine this problem could have been solved by abstracting away the classloader behaviors (e.g., if gradle plugins used a module system such as OSGi, or if I specifically implemented my own classloader), in the interm, I’m hoping just to use the available logging configuration provided by gradle. So, I tried to add an appender to a mylog4j.properties file:

log4j.logger.myexamplelogger= debug, myfile log4j.appender.myfile=org.apache.log4j.RollingFileAppender log4j.appender.myfile.File=/Users/jouellette/myexample.log … (more properties)

and I then passed this file into the log4j logger:

PropertyConfigurator.configure(examplePropertiesLoadedIntoMemory); (I’ve checked, and examplePropertiesLoadedIntoMemory is correctly set)

And it didn’t work. Although myexamplelogger is correctly logging output, it’s not being appended to my file. So as far as I can tell, gradle is polluting the classpath with its own logger, and I’m not able to properly add my own appenders to its logger.

Do you guys have any thoughts on what might be causing this? Is it possible you could update your log4j implementation to support more of the API, or find a way to isolate it on the plugin classpath? Am I doing something dumb here?

Any assistance is greatly appreciated. Thanks much!

1 Like

Whilst this isnt the most elegant solution, I found that I could write the standard out/error to a log this way:

def tstamp = new Date().format('yyyy-MM-dd_HH-mm-ss')
def buildLogDir = "${rootDir}/build/logs"
mkdir("${buildLogDir}")
def buildLog = new File("${buildLogDir}/${tstamp}_buildLog.log")
  import org.gradle.logging.internal.*
System.setProperty('org.gradle.color.error', 'RED')
  gradle.services.get(LoggingOutputInternal).addStandardOutputListener (new StandardOutputListener () {
    void onOutput(CharSequence output) {
        buildLog << output
    }
})
  gradle.services.get(LoggingOutputInternal).addStandardErrorListener (new StandardOutputListener () {
    void onOutput(CharSequence output) {
        buildLog << output
    }
})

I usually save this as “$rootDir/gradle/logging.gradle” and then use:

apply from: "$rootDir/gradle/logging.gradle"

from within the master build file. Only issue I’ve really had is it captures EVERYTHING!

1 Like