Tuesday, March 3, 2009

AspectJ for Java logging: Serviceability without the clutter - part 1

I have grown fond of AspectJ in the past years, probably at the same rate I have grown tired of writing or referencing the same cross-cutting snippets of code all over the code-base of a project.

In the way of background, AspectJ is a Java extension for "Aspect Oriented Programming" (PDF download), or AOP for short.

You can read the entire background in the previous link, but in a nutshell, AOP allows you to abstract common code snippets (advices, in AspectJ lingo) that are executed on common points of the code-base (join points.)

As a concrete example, I'll reference an Aspect that I have been perfecting on my last three projects: adding debugging trace statements to the entry/exit of all methods in a project.

I won't lie to you, it is not a trivial technique and the average Java programmer who eventually inherits your code base will hate your guts.

Setup your environment

Using the Eclipse environment and the AJDT (AspectJ Development Tools) is almost mandatory when dealing with aspects in Java. If you already have one of the Rational Software Development Platform siblings installed on your machine, such as Rational Software Architect, you are in good shape too, just pay attention to their underlying Eclipse version in order to select the right level of AJDT.

Since this is not an Eclipse primer, I assume you know how to deal with installation of plugins from update sites.

Trace statements

If you want somewhat decent traceability, you should look into three types of statements:

Method entry:

    if (trace.isLoggable(Level.FINER)) {
        trace.entering(<CLASS_NAME>, <METHOD_NAME>, <args> ) ;
    }


Method exit:

    if (trace.isLoggable(Level.FINER)) {
        trace.exiting(<CLASS_NAME>, <METHOD_NAME>, <args> ) ;
    }


Exception handling:

    catch (<EXCEPTION_CLASS> e) {
        trace.logp(Level.SEVERE,
<CLASS_NAME>, <METHOD_NAME>,
                   "Caught exception: " + e.getClass().toString(),
                   e);
    }


The Logging aspect

This is also not a primer on AspectJ, so that I assume you know that aspects go inside an "Aspect" file sitting inside any regular folder corresponding to a Java package. Here is what I have for trace.


package my.project.aspects;

import java.util.logging.Level;
import java.util.logging.Logger;

/**
* Aspect for Java logging throughout the code base.
*/
public aspect Logging {

    private static Logger trace = Logger.getLogger("trace.my.project");

   /*
     * Pointcuts
     */
   
 
    /**
     * Execution body for all methods.
     */
    pointcut method():
        execution(* my.project..*.*(..)) &&
        !within(my.project.D.*) &&
        !within(Logging);

    /**
     * Exception handling blocks inside all methods.
     */
    pointcut methodError(Throwable t):
args(t) &&
        handler(Throwable+) &&
        within(my.project..*.*) &&
        !within(my.project.D.*) &&
        !within(Logging);

    /*
     * Join points
     */

    /**
     * Entry trace statements for all methods.
     */
    before(): method() {
        if (trace.isLoggable(Level.FINER)) {
            trace.entering(
thisJoinPointStaticPart.getSignature().getDeclaringTypeName(),

                    thisJoinPointStaticPart.getSignature().toString(),
                    thisJoinPoint.getArgs());
        }
    }

    /**
     * Exit trace statements for all methods.
     */
    after() returning (Object result): method() {
        if (trace.isLoggable(Level.FINER)) {
            trace.exiting(
thisJoinPointStaticPart
.getSignature().getDeclaringTypeName(),
                    thisJoinPointStaticPart.getSignature().toString(),
                    result);
        }
    }

    /**
     * Exception handling blocks inside all methods.
     */
    before(Throwable t): methodError(t) {
        if (trace.isLoggable(Level.SEVERE)) {
            trace.logp(Level.SEVERE,
thisJoinPointStaticPart.getSignature().getDeclaringTypeName(),
                    thisJoinPointStaticPart.getSignature().toLongString(),
                    "Caught exception: " + t.getClass().toString(),
t);
        }
    }
 
}







Advanced reading





In the way of advanced reading, one can browse through developerWorks excellent AOP@Work series.





I particularly like "Next Steps with Aspects" and "AOP Myths and Realities".

No comments:

Post a Comment