Using Commons Logging from Ant

Posted on 04/23/2008 , 3 Comments ( Add )

Here is a common problem with custom Ant tasks. A typical task is implemented using multiple classes, so classes that don't extend org.apache.tools.ant.Task class don't necessarily have to have a dependency on Ant APIs. For example, it is pretty easy to pass Ant properties in a hashtable instead of passing the entire project object. This makes the custom task's code more generic and reusable.

One little issue that still remains is logging. Ant users are accustomed to running Ant with -verbose option which tells Ant tasks to print more detailed information. Oftentimes, -verbose is the only way to debug a build.

Unfortunately, using Ant logging requires access to Project or Task objects. As a result, the dependency on Ant APIs permeates the code that otherwise could have remained generic.

My solution for this is to use a simple class that implements org.apache.commons.logging.Log interface so that we can use jakarta commons logging (JCL) APIs instead of using Ant logging directly.
The class is called AntCommonsLogger.

To initialize AntCommonsLogger, users can either invoke "antCommonsLoggerInit" task in the beginning of a project or call AntCommonsLogger.init( getProject()) somewhere in their custom task class. After that, AntCommonsLogger becomes the default logger so that any class can use the the familiar commons logging pattern without any changes:


private static Log logger = LogFactory.getLog(CustomTask.class.getName());
...
logger.info("message");
logger.debug("message");
logger.trace("message");


"info" messages (i.e. calls to logger.info()) display during normal Ant execution, "debug" messages display if "-verbose" was specified and "trace" messages display if "-debug" was specified. This is a bit counterintuitive but this is the best we could do given that Ant's "verbose" does not have a direct counterpart in JCL.

To begin using AntCommonsLogger, download myarch-antutil and add antCommonsLoggerInit task definition to your project, e.g.:


    <taskdef resource="com/myarch/antutil/tasks.properties">
        <classpath>
            <pathelement path="${basedir}/myarch-antutil.jar"/>
            <!-- lib.dir must contain commons-logging -->
            <fileset dir="${lib.dir}" />
        </classpath>
    </taskdef>

You also need to add myarch-antutil.jar and commons-logging.jar to the classpath of your custom tasks.

Note that calling antCommonsLoggerInit makes AntCommonsLogger the default logger for this JVM instance. This means that all Java classes invoked by this Ant script (e.g., using "java" task) that use JCL, will use AntCommonsLogger instead of java.util logging or log4j. If this is not what you want, call AntCommonsLogger.init(getProject()) at the begging of your custom task and AntCommonsLogger.restorePreviousDefault() at the end.

Download myarch-antutil

3 Responses to “Using Commons Logging from Ant”

#26481 | April 24th, 2008 | SteveL

This works -but if you ever tell ant to log through commons-logging you are going to be in recursively deep trouble. I dont think Ant has a commons-logging logger (at least it is not documented), but there is nothing to stop anyone writing one.

Ant’s logging is limited. It was good at the time, but its inefficient, and suspected of causing build delays on multicore systems. Trouble is, how do we cut it out and stick in something new? Without adding extra dependencies? Use java.util.logging? Ouch.

#26482 | April 24th, 2008 | SteveL

…Actually, now that Ant is Java1.4+ only , we could provide a java.util.logging support, letting tasks log that way, and routing the input to our logs. Possibly.

#26492 | April 24th, 2008 | Alexander Ananiev

SteveL, the class that I posted here simply forwards logging requests to Ant and lets it handle it. I’m more concerned about having to use different logging abstractions, one for Ant and one for commons logging and that’s the problem that I’ve tried to solve.

Regards.