JWare/AntXtras Logging for Ant (Log4Ant) is a full bridge to the SLF4J library which is a simple façade for various logging frameworks like Log4J, Jakarta Commons Logging, LOGBack, and the JRE′s own java.util.logging module. By using Log4Ant components you can easily capture, transform, and send Ant messages, AntXtras messages, and standard output and error messages to your real logging and monitoring system. This means you can capture detailed information about your build, test, and deploy processes for live monitoring or subsequent post-run inspection.
Log4Ant has three main objectives: first it wants to get all Ant sourced messages into your actual logging or monitoring system; second, it wants to provide a rich set of feedback components for you to use instead of the basic <echo> task that Ant provides; and third, it wants to accomplish the previous two objectives without forcing you to rewrite your existing scripts or to do a lot of post-capture massaging to get the outputs to show only what you need.
We use SLF4J as our logging service interface because it matches perfectly with the logging features already within Ant and with the enhanced logging capabilities we want to provide as Ant components. With SLF4J we got a simple but strong interface that when translated into Ant components and their attributes, just about any one would understand very quickly. We could also avoid creating a tight coupling of Log4Ant to a particular logging system such as Log4J or the JRE’s own logging feature.
To meet its objectives, Log4Ant makes very few assumptions about what information you want logged or about how you want your logged information presented; instead, it presents as much of your Ant runtime environment as possible to your selected logging system and depends on you to configure both Log4Ant and your logging system to select, filter, transform, and propagate the information however you want.
To work with existing scripts, Log4Ant provides two mechanisms to capture and emit or broadcast information. First, you can use Log4Ant as an implementation of a standard Ant listener. This mechanism is the less tunable method, but it allows you to capture information without changing anything in the source Ant script. To use this method you have to supply Ant with the name of the Log4Ant listener as shown in snippet below. For additional information read “How do I setup a Log4Ant Ant listener?”
ant -listener org.jwaresoftware.log4ant.listener.AntToSlf4jConduit ...
The second mechanism requires that you use the Log4Ant <emit:capturelogs> taskset as a scoped listener that only listens for messages generated from its enclosed tasks. While you have to change your Ant script for this method, the amount of change is minimal and straightforward to implement. Below is a very simple example of how you might use Log4Ant’s capturelogs taskset to capture and emit all Ant messages related to generating Javadocs for a project “MyProject”. All of the script within the <emit:capturelogs> taskset is the current script; the change is the addition of the <emit:capturelogs> wrapper that will forward all logged messages (including unseen diagnostic messages) to your logging system.
<target name="apidocs"> <emit:capturelogs> <!-- addition --> <mkdir dir="${apis.d}"/> <property name="wintitle" value="MyProject APIs, ${DSTAMP}"/> <echo message="Javadocs for '${wintitle}' to ${apis.d}" level="info"/> <javadoc destdir="${apis.d}" windowtitle="${wintitle}"...> ... </javadoc> </emit:capturelogs> <!-- addition --> </target>
Without additional configuration for either the listener or the <emit:capturelogs> taskset, Log4Ant will automatically redirect outgoing messages to one of three top-level loggers also known as indicators: problems, status, and diagnostics. All messages marked as errors and warnings are sent to the ‘problems’ logger, all messages marked as info are sent to the ‘status’ logger, and all other messages are sent to the ‘diagnostics’ logger. Additionally, Log4Ant will further order messages by source project and source target under these top-level categories. This means that without lifting a pinky, you’ll receive your messages organized in a minimal but sane fashion in your logging system.
There are a variety of ways to get Log4Ant to do a better job distributing your script’s messages– these are the new or Log4Ant ways of sending feedback from Ant scripts and they are reviewed in the next section.
Log4Ant also provides new feedback components that greatly expand the range of information that you can emit to your logging system. If you combine these items with the core AntXtras feedback and error handling functionality, you can generate tailor-made feedback for your builds, deployments, testing, and whatever other processes for which you use Ant scripts.
First, Log4Ant provides a wholesale replacement for the standard <echo> task, <emit:show>, that extends the core AntXtras <show> task to send messages and other fixture information directly to your logging system. Here is an example that sends a message to the logging system directly in a manner similar to what you might do with <echo> and the Ant console except you also get: access to locale-aware external messages including template argument replacement, a default level of ‘info’ not ‘warning’, the option to not echo the same message to Ant console, and other goodies supported by both <show> and <emit:show>.
<emit:show messageid="LF.tests.start" arg0="${testsuite.name}" arg1="${datafactory.name}"/>
The snippet below combines the AntXtras <protect> taskset with <emit:show> to pass through both an error message and the associated exception information to the logging system.
<target name="main-libraries"> <protect> ...[tasks that can fail]... <iferror capturethrown="last.error"> <emit:show messageid="E.something.failed" level="error"> <include thrown="last.error"/> </emit:show> </iferror> </protect> </target>
If this reaction is something you’d like done for many targets, you can leverage Ant’s built-in <presetdef> mechanism to declare a shared custom <protect> wrapper that you can reuse with simplier Ant script. Read “Emit Errors For All Targets” for additional information.
If you use Ant for purposes other than the building your application, for instance for the controlling of test harnesses or deployment, you can use the <emit:checkpoint> task to report “heart beats” or “health beats” out to your logging system. Unlike the <emit:show>, the checkpoint task is designed specifically for this purpose and the format of the emitted message is predefined to make the reading of logged statements easy (on the console, in a text file, posted to a chat, or broadcast on an RSS feed). Below are some examples of how you might use Log4Ant’s checkpoint:
<emit:checkpoint/> <emit:checkpoint status="G"/> <emit:checkpoint status="G" messageid="SMOKE.started"/> <emit:checkpoint status="A" message="${svr.id} ping too slow: ${svr.time}"/> <emit:checkpoint status="R" message="Failed customer tests: ${tests.errors}"/>
Output might look like:
20071021T093319-0400 [TICK] [G] [20071021T093319-0400] - [TICK] [G] [20071021T094125-0400] - SMOKE TESTING started! [TICK] [A] [20071021T102725-0400] - 'gphc02n4' ping too slow: 23s481ms [TICK] [R] [20071021T102725-0400] - Failed customer tests: 5
While Log4Ant makes very few assumptions about what to log or where to log it, it provides you with a trio of components that let you define that information in great detail. Log4Ant can then use that information to do most of the grunt work of transforming and sorting messages so you receive the right messages in the right format in the right place in your logging system.
For the “New Way” components, you can use the <emit:configuration> and <emit:includes> data types to tell Log4Ant about logger names, logger hierarchies, whether to echo back messages to the Ant console, and what fixture information to make available to your logging system. For “Old Way” components you primarily use the <emit:mappings> component to define the sorting of Ant messages.
In addition to the logged message, Log4Ant lets you expose elements of your Ant runtime fixture other than simple properties. You can include variables, references, exceptions, location information (target, file, line, etc.), among other things.
There are several “How To” articles written about Log4Ant configuration and about exposing fixture information in particular.
The recommended namespace URI and prefix for Log4Ant is “jwaresoftware.log4ant” and “emit:” respectively. Read the Log4Ant Properties page for the list of OS and Java properties that control Log4Ant. If you’re a developer looking to extend or use Log4Ant in your own Ant components, you will find additional implementation details in the Log4Ant Javadoc API reference.
| Task/Type | General Description |
|---|---|
| emit:configuration | Lets you define shareable Log4Ant configuration like logger hierarchies, logging levels, etc. |
| emit:manage | Lets you install a fall-back or default configuration for all Log4Ant components. |
| emit:show | Lets you emit a message and other information directly to your logging system. Replacement for standard <echo> and AntXtra’s own <show> component. |
| emit:checkpoint | Lets you emit a shorthand “health beat” message to your logging system. |
| emit:capturelogs | Lets you capture and redirect logged messages for any Ant-based component. Meant as a bridge to capture output of existing Ant script. |
| emit:mappings | Lets you organize captured messages from Ant-based components before sending to your logging system. |
| emit:overlay | Lets you install a Log4Ant configuration for the duration of a set of enclosed tasks. |
| emit:includes | Lets you define a shareable set of fixture information that you want captured and sent to your logging system. Supports properties, variables, references, resource collections, etc. |
| emit:libcheck | Gives you the version information for the active Log4Ant antlib including information about the SLF4J implementation being used. |
| AntToSlf4jConduit | Lets you install a Log4Ant based build listener for the duration of your entire Ant execution cycle. (Note this is not a task or type per-se.) |
• You can install the Log4Ant extension directly into your <ANT_HOME>/lib directory but more typically, you will install it into its own location. This example assumes the latter case; it loads the Log4Ant antlib into the Ant runtime from a location defined by the ‘LOG4ANT_HOME’ property (you would fill-in the property value) and then links its components to the “emit:” namespace prefix. Note that this example does not cover the configuration you need for your specific logging system!
1: <project name="example" xmlns:emit="jwaresoftware.log4ant"> 2: <property name="LOG4ANT_HOME" value="..."/> 3: 4: <taskdef uri="jwaresoftware.log4ant" 5: resource="org/jwaresoftware/log4ant/antlib.xml"> 6: <classpath> 7: <fileset dir="${LOG4ANT_HOME}/dep"> 8: <include name="*/lib/*.jar"/> 9: </fileset> 10: <fileset dir="${LOG4ANT_HOME}/lib"> 11: <include name="*.jar"/> 12: </fileset> 13: </classpath> 14: </taskdef> 15: …
• Example: define a Log4Ant configuration that includes a custom top level logger and no echo-back to the Ant console. Install this configuration as the default for Log4Ant. Log4Ant will use this configuration for any attributes that are not explicitly defined by a component.
1: <emit:configuration id="my.log.conf" 2: to="MyApp.NightlyBuilds" 3: echo="no" 4: isdefault="yes"/>
• Example: install a another predefined configuration ‘my.other.log.conf’ as the default for all Log4Ant-aware components. This replaces the existing default configuration.
1: <emit:manage action="install-fallback"> 2: <parameter name="my.other.log.conf"/> 3: </emit:manage>
• Example: send a simple message directly to your logging system. This message will go to the logger defined in the fallback configuration at the SLF4J “info” level.
1: <emit:show message="Build blastoff!"/>
• Example: fill-in a parametrized message and then send to your logging system. The final message will also echo-back to the Ant console (explicitly requested). Assume you have already installed a valid messages resource bundle.
1: <emit:show messageid="build.begin" arg0="${package}" echo="yes"/>
• Example: send a simple message directly to a specific logger in your logging system different from the default logger. In this case a top level logger named “FINETRACE” at the SLF4J “debug” level.
1: <emit:show message="A debug message" to="FINETRACE" level="debug"/>
• Example: install a custom Log4Ant configuration for a small set of tasks. Log4Ant will first read all default configuration from this configuration then from the default if needed. Note that this local configuration’s logger name is defined relative to (aka “wrt”) the default logger’s name.
1: <emit:configuration id="smoke.log.conf" 2: to="SmokeTest" wrt="my.log.conf" 3: echo="yes"/> 4: 5: <emit:overlay with="smoke.log.conf"> 6: <emit:show message="Smoke tests blastoff!"/> 7: [other tasks...] 8: <emit:show message="Smoke tests complete!"/> 9: </emit:overlay>
• Example: define a set of mappings “my.loggers” that will bucket standard Ant log messages by level (errors vs. info vs. debug, etc.) as well as target name.
1: <property name="TOP" value="MyApp.Builds"/> 2: 3: <emit:mappings id="my.loggers"> 4: <mapping type="indicator" name="problems" logger="${TOP}.Problems"/> 5: <mapping type="indicator" name="status" logger="${TOP}.Log"/> 6: <mapping type="indicator" name="diagnostics" logger="${TOP}.Debug"/> 7: <mapping type="target" like=".*workspace$" logger="SetUp"/> 8: <mapping type="target" like="(.*)tests$" loggermatch="Tests.\1"/> 9: ... 10: </emit:mappings>
• Example: capture all Ant console and debug messages and redirect them to your logging system, using a pre-defined mapping “my.loggers” to bucket the messages by level and source target name. We define a preset of the standard <echo> that logs at the “info” level– not the default “warning” level!
1: <presetdef name="inform"> 2: <echo level="info"/> 3: </presetdef> 4: 5: <presetdef name="body"> 6: <emit:capturelogs includes="target" mappings="my.loggers"> 7: <inform message="Started ${$x:targetname}"/> 8: </emit:capturelogs> 9: </presetdef> 10: … 11: <target name="blankworkspace"> 12: <body> 13: [other tasks...] 14: </body> 15: </target> 16: … 17: <target name="programmertests"> 18: <body> 19: [other tasks...] 20: </body> 21: </target> 22: …