Log4J to java.util.logging -- Jackpot!

A few days ago the JXTA JSE project announced that we had converted all of our logging code from using Apache Log4J to using java.util.logging. With nearly 6000 logging statements in the core JXTA platform and many thousands more in the JXTA Shell, CMS, and myJXTA2 the conversion process could have been a very daunting tasking. Luckily we had the perfect tool available, Jackpot. Jackpot is a rule based Java source reengineering tool. It's not just a refactoring tool, it can do lots of other amazing source transformations using either code or text based rules. For a task of this nature and size, Jackpot was the perfect tool.

I recommend performing Jackpot conversions only on a copy of your workspace or a clean checkout from your version control system. If something goes wrong it can be very difficult (if not impossible) to revert the changes made by Jackpot.

The first step in our conversion is to normalize the usage of Log4J statements. There are a few differences between the Log4J and java.util.logging APIs that are difficult to convert directly. By normalizing all of the Log4J output statements to use either the form "<Logger>.log( <Level>, <Message>)" or "<Logger>.log( <Level>, <Message>, <Throwable>)" the rest of the conversion goes more smoothly. We also convert a few convenience methods to their root form.

Log4J to java.util.logging Jackpot Rules : Normalization.rules
/\*\*
 \*  Convert Log4J to java.util.logging :: Normalize all of the logging statements.
 \*/

$L.trace($m,$e) => $L.log( org.apache.log4j.Level.TRACE,$m,$e) :: 
   $L instanceof org.apache.log4j.Logger && $m instanceof java.lang.String && $e instanceof java.lang.Throwable;
$L.debug($m,$e) => $L.log( org.apache.log4j.Level.DEBUG,$m,$e) :: 
   $L instanceof org.apache.log4j.Logger && $m instanceof java.lang.String && $e instanceof java.lang.Throwable;
$L.info($m,$e) => $L.log( org.apache.log4j.Level.INFO,$m,$e) :: 
   $L instanceof org.apache.log4j.Logger && $m instanceof java.lang.String && $e instanceof java.lang.Throwable;
$L.warn($m,$e) => $L.log( org.apache.log4j.Level.WARN,$m,$e) :: 
   $L instanceof org.apache.log4j.Logger && $m instanceof java.lang.String && $e instanceof java.lang.Throwable;
$L.error($m,$e) => $L.log( org.apache.log4j.Level.ERROR,$m,$e) :: 
   $L instanceof org.apache.log4j.Logger && $m instanceof java.lang.String && $e instanceof java.lang.Throwable;
$L.fatal($m,$e) => $L.log( org.apache.log4j.Level.FATAL,$m,$e) :: 
   $L instanceof org.apache.log4j.Logger && $m instanceof java.lang.String && $e instanceof java.lang.Throwable;

$L.trace($m) => $L.log( org.apache.log4j.Level.TRACE,$m) :: 
   $L instanceof org.apache.log4j.Logger && $m instanceof java.lang.String;
$L.debug($m) => $L.log( org.apache.log4j.Level.DEBUG,$m) :: 
   $L instanceof org.apache.log4j.Logger && $m instanceof java.lang.String;
$L.info($m) => $L.log( org.apache.log4j.Level.INFO,$m) :: 
   $L instanceof org.apache.log4j.Logger && $m instanceof java.lang.String;
$L.warn($m) => $L.log( org.apache.log4j.Level.WARN,$m) :: 
   $L instanceof org.apache.log4j.Logger && $m instanceof java.lang.String;
$L.error($m) => $L.log( org.apache.log4j.Level.ERROR,$m) :: 
   $L instanceof org.apache.log4j.Logger && $m instanceof java.lang.String;
$L.fatal($m) => $L.log( org.apache.log4j.Level.FATAL,$m) :: 
   $L instanceof org.apache.log4j.Logger && $m instanceof java.lang.String;

$L.trace($e,$e) => $L.log( org.apache.log4j.Level.TRACE, $e.getMessage(), $e) :: 
   $L instanceof org.apache.log4j.Logger && $e instanceof java.lang.Throwable;
$L.debug($e,$e) => $L.log( org.apache.log4j.Level.DEBUG, $e.getMessage(), $e) :: 
   $L instanceof org.apache.log4j.Logger && $e instanceof java.lang.Throwable;
$L.info($e,$e) => $L.log( org.apache.log4j.Level.INFO, $e.getMessage(), $e) :: 
   $L instanceof org.apache.log4j.Logger && $e instanceof java.lang.Throwable;
$L.warn($e,$e) => $L.log( org.apache.log4j.Level.WARN, $e.getMessage(), $e) :: 
   $L instanceof org.apache.log4j.Logger && $e instanceof java.lang.Throwable;
$L.error($e,$e) => $L.log( org.apache.log4j.Level.ERROR, $e.getMessage(), $e) :: 
   $L instanceof org.apache.log4j.Logger && $e instanceof java.lang.Throwable;
$L.fatal($e,$e) => $L.log( org.apache.log4j.Level.FATAL, $e.getMessage(), $e) :: 
   $L instanceof org.apache.log4j.Logger && $e instanceof java.lang.Throwable;

$L.trace($m) => $L.log( org.apache.log4j.Level.TRACE,$m.toString()) :: $L instanceof org.apache.log4j.Logger;
$L.debug($m) => $L.log( org.apache.log4j.Level.DEBUG,$m.toString()) :: $L instanceof org.apache.log4j.Logger;
$L.info($m) => $L.log( org.apache.log4j.Level.INFO,$m.toString()) :: $L instanceof org.apache.log4j.Logger;
$L.warn($m) => $L.log( org.apache.log4j.Level.WARN,$m.toString()) :: $L instanceof org.apache.log4j.Logger;
$L.error($m) => $L.log( org.apache.log4j.Level.ERROR,$m.toString()) :: $L instanceof org.apache.log4j.Logger;
$L.fatal($m) => $L.log( org.apache.log4j.Level.FATAL,$m.toString()) :: $L instanceof org.apache.log4j.Logger;

$L.isDebugEnabled() =>  $L.isEnabledFor(org.apache.log4j.Level.DEBUG) :: $L instanceof org.apache.log4j.Logger;
$L.isInfoEnabled() =>  $L.isEnabledFor(org.apache.log4j.Level.INFO) :: $L instanceof org.apache.log4j.Logger;

$L.getLogger($C) => $L.getLogger($C.getName()) :: $L instanceof org.apache.log4j.Logger && $C instanceof java.lang.class;

The second step in the conversion is to actually convert from Log4 to java.util.logging. This step is the dangerous one because if any part of the conversion produces code which doesn't compile there is no easy way to revert the changes. (I had lots of fun debugging this step).

Log4J to java.util.logging Jackpot Rules : Conversion.rules
/\*\*
 \*  Convert Log4J to java.util.logging :: Convert from Log4J to java.util.logging
 \*/

/\*
$L.isEnabledFor(Level.TRACE) => Logging.SHOW_FINER && $L.isLoggable(Level.FINER) :: $L instanceof org.apache.log4j.Logger;
$L.isEnabledFor(Level.DEBUG) => Logging.SHOW_FINE && $L.isLoggable(Level.FINE) :: $L instanceof org.apache.log4j.Logger;
$L.isEnabledFor(Level.INFO) => Logging.SHOW_INFO && $L.isLoggable(Level.INFO) :: $L instanceof org.apache.log4j.Logger;
$L.isEnabledFor(Level.WARN) => Logging.SHOW_WARNING && $L.isLoggable(Level.WARNING) :: $L instanceof org.apache.log4j.Logger;
$L.isEnabledFor(Level.ERROR) => Logging.SHOW_SEVERE && $L.isLoggable(Level.SEVERE) :: $L instanceof org.apache.log4j.Logger;
$L.isEnabledFor(Level.FATAL) => Logging.SHOW_SEVERE && $L.isLoggable(Level.SEVERE) :: $L instanceof org.apache.log4j.Logger;
\*/

org.apache.log4j.Level.TRACE => java.util.logging.Level.FINER;
org.apache.log4j.Level.DEBUG => java.util.logging.Level.FINE;
org.apache.log4j.Level.INFO => java.util.logging.Level.INFO;
org.apache.log4j.Level.WARN => java.util.logging.Level.WARNING;
org.apache.log4j.Level.ERROR => java.util.logging.Level.SEVERE;
org.apache.log4j.Level.FATAL => java.util.logging.Level.SEVERE;

$L.toLevel($s) => $L.parse($s) :: $L instanceof org.apache.log4j.Level && $s instanceof java.lang.String;

mapclass org.apache.log4j.Level => java.util.logging.Level;
mapclass org.apache.log4j.Logger => java.util.logging.Logger;

It should now be possible to remove Log4J from your classpath. The final step in the conversion is to reverse some of the API normalization we did in the first steps and make use of java.util.logging convenience methods.

Log4J to java.util.logging Jackpot Rules : Simplification.rules
/\*\*
 \*  Convert Log4J to java.util.logging :: Simplify.
 \*/

$L.log(Level.FINER,$m) => $L.finer($m) :: $L instanceof java.util.logging.Logger && $m instanceof java.lang.String;
$L.log(Level.FINE,$m) => $L.fine($m) :: $L instanceof java.util.logging.Logger && $m instanceof java.lang.String;
$L.log(Level.INFO,$m) => $L.info($m) :: $L instanceof java.util.logging.Logger && $m instanceof java.lang.String;
$L.log(Level.CONFIG,$m) => $L.config($m) :: $L instanceof java.util.logging.Logger && $m instanceof java.lang.String;
$L.log(Level.WARNING,$m) => $L.warning($m) :: $L instanceof java.util.logging.Logger && $m instanceof java.lang.String;
$L.log(Level.SEVERE,$m) => $L.severe($m) :: $L instanceof java.util.logging.Logger && $m instanceof java.lang.String;

I would like to thank Tom Ball for assisting with the Jackpot scripts when we had problems with our initial conversion attempt back in June. Tom was very helpful in correcting our mistakes and adding additional flourishes that we likely wouldn't have figured out on our own. Jackpot is a very cool tool and was indispensable for this task. We expect that we will find many other uses for Jackpot within the JXTA project and will be keeping an eye out for other people's refactoring and reengineering scripts.

On the behalf of the JXTA community we would also like to thank the developers of Log4J and Chainsaw for their hard work and for producing such an excellent, well supported and well thought out product. Log4J has been a part of JXTA since early in 2001 and served us very well. I would also like to thank Nelson Minar for originally integrating Log4J into JXTA.

Comments:

Post a Comment:
Comments are closed for this entry.
About

mduigou

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today