Logging Exceptions


It is common knowledge that exceptions can occur anywhere ‚ web-tier, ejb-tier, database. Wherever they occur, they must be caught and logged with appropriate context. It makes more sense to handle a lot, if not all of the exceptions originating in the ejb tier and database tier on the client side in the web tier . Why should exception logging take place on the client side?

First, the control hasn't passed outside of the application server yet. (Assuming both the web tier and ejb tier do not belong to disparate entities). The so-called client tier, which is composed of JSP pages, servlets and their helper classes, runs on the J2EE application server itself. Second, the classes in a well-designed web tier have a hierarchy (for example, hierarchy in the Business Delegate classes, Intercepting Filter classes, JSP base class, or in the Struts Action classes) or single point of invocation in the form of a FrontController servlet (Business Delegate, Intercepting Filter and Front Controller are Core J2EE Patterns. Refer to Sun blueprints for more details). The base classes of these hierarchies or the central point in FrontController classes can contain the exception logging code. In the case of session EJB-based logging, each of the methods in the EJB component must have logging code. As the business logic grows, so will the number of session EJB methods , and so will the amount of logging code. A web tier system will require less logging code. You should consider this option if you have co-located web tier and EJB tiers and you don't have a requirement to support any other type of client.

To develop a full fledged exception handling strategy let us start with a simple class shown in Listing 9.7. This class, ExceptionCategory categorizes the exceptions into INFO , WARNING, ERROR and FATAL. This identification helps us when the notification of Support personnel depends on the severity of the exception.

Listing 9.7: Enumeration class for Exception Category
 public class ExceptionCategory implements java.io.Serializable {     public static final ExceptionCategory INFO =                                new ExceptionCategory(0);     public static final ExceptionCategory WARNING =                                new ExceptionCategory(1);     public static final ExceptionCategory GENERAL_PROBLEM =                                new ExceptionCategory(2);     public static final ExceptionCategory DATA_PROBLEM =                                new ExceptionCategory(3);     public static final ExceptionCategory CONFIG_PROBLEM =                                new ExceptionCategory(4);     public static final ExceptionCategory FATAL =                                new ExceptionCategory(5);     private int type;     private ExceptionCategory(int aType) {         this.type = aType;     } } 
 

The next class to look at is the ExceptionInfo class as shown in Listing 9.8 This class provides information about the Exception as the name indicates. Apart from the ExceptionCategory , this class also holds a unique id associated with the Exception and a boolean indicating if the exception has been already logged. The UniqueIDGeneratorFactory is a factory class that returns a UniqueIDGenerator. UniqueIDGenerator is represented by an interface IUniqueIDGenerator . This interface has just one method ‚ getUniqueID() . Listing 9.9 shows a simple Unique ID Generator implementation IP Address and time.

Listing 9.8: Exception Info class
 public class ExceptionInfo implements java.io.Serializable {    private ExceptionCategory exceptionCategory;    private String errorCode;    private String exceptionID;    private boolean logged;    public ExceptionInfo(ExceptionCategory aCategory,                           String aErrorCode) {     this.exceptionCategory = aCategory;     this.errorCode = aErrorCode;     this.logged = false;     this.exceptionID =        UniqueIDGeneratorFactory.               getUniqueIDGenerator().getUniqueID();    } } 
 
Listing 9.9: Simple Unique ID Generator
 public class UniqueIDGeneratorDefaultImpl               implements IUniqueIDGenerator   {    private static IUniqueIDGenerator instance =                         new UniqueIDGeneratorDefaultImpl();    private long counter = 0;    public String getUniqueID() throws UniqueIDGeneratorException     {       String exceptionID = null;       try {          exceptionID = InetAddress.getLocalHost().getHostName();       } catch(UnknownHostException ue) {          throw new UniqueIDGeneratorException(ue);       }       exceptionID = exceptionID +                      System.currentTimeMillis() +                      counter++;       return exceptionID;     } } 
 

And finally Listing 9.10 shows the actual Exception class. This is the base class for all the checked exceptions originating in MyBank . It is always better to have a base class for all exceptions originating in a system and then create new types as required. In this way, you can decide how much fine grained you want the catch exception blocks to be. Similarly you can have a base class for all unchecked exceptions thrown from system. Listing 9.11 shows such a class.

Listing 9.10: MybankException class
 public abstract class MybankException extends Exception {     private ExceptionInfo exceptionInfo;     public MybankException(ExceptionInfo aInfo) {        super();        this.exceptionInfo = aInfo;     } } 
 
Listing 9.11: MybankRuntimeException class
 public abstract class MybankRuntimeException extends Exception {     private ExceptionInfo exceptionInfo;     private Throwable wrappedException;     public MybankException(ExceptionInfo aInfo,                             Throwable aWrappedException) {        super();        this.exceptionInfo = aInfo;        this.wrappedException = aWrappedException;     } } 
 

Notice that MybankRuntimeException has only one constructor that takes both ExceptionInfo and a Throwable . This is because if someone is explicitly throwing a runtime exception from his or her code, it is probably because a system error or serious unrecoverable problem has occurred. We want to get hold of the actual cause of the problem and log it. By enforcing development time disciplines like this, one can decrease the chances of exceptions in the system without a context.

Finally we also need to look at the actual Logging utility ‚ a stack trace printing utility shown in Listing 9.12. The default printStackTrace() method in java.lang.Throwable logs an error message to the System.err. Throwable also has an overloaded printStackTrace() method to log to a PrintWriter or a PrintStream . The above method in StackTraceUtil wraps the StringWriter within a PrintWriter . When the PrintWriter contains the stack trace, it simply calls toString() on the StringWriter to get a String representation of the stack trace.

Listing 9.12: Stack Trace printing utility.
 public final class StackTraceTool {   private StackTraceTool() {}   public static String getStackTraceAsString(                               MybankException exception)   {     String message = " Exception ID : " +                      exception.getExceptionInfo().getExceptionID()                   + "\n " + "Message :" + exception.getMessage();     return getStackMessage(message, exception);   }   public static String getStackTraceAsString(Throwable throwable)   {     String message = " Exception ID : " +      UniqueIDGeneratorFactory.getUniqueIDGenerator().getUniqueID()              + "\n " + "Message :" + exception.getMessage();     return getStackMessage(message, exception);   }   private static String getStackMessage(String message,                                          Throwable exception)   {         StringWriter sw = new StringWriter();         PrintWriter pw = new PrintWriter(sw);         pw.print(" [ " );         pw.print(exception.getClass().getName());         pw.print(" ] ");         pw.print(message);         exception.printStackTrace(pw);         return sw.toString();     } } 
 

The StackTraceUtil class has two overloaded methods ‚ getStackTraceAsString() ‚ One of them takes the MybankException as the parameter, the other takes Throwable as the parameter. All exceptions of type MybankException already have the unique id in-built. For other exceptions, to be logged the unique id has to be explicitly generated. MybankException also has the flag indicating whether the exception has been logged making it easier to prevent multiple logging, as you will see very soon. Other Exceptions don ‚ t have this capability and it is up to the caller program and called to collaborate and ensure that duplicate logging does not happen.

Armed with these knowledge let us look at a scenario that will lead to duplicate logging in the system when an exception occurs. Consider a case when a method, foo() , in an entity EJB component is accessed in a session EJB method, called bar() . A web-tier client invokes the method bar() on the session EJB component, and also logs the exceptions. If an exception occurs in the entity EJB method foo() when the session EJB method bar() is invoked from the web-tier, the exception will have been logged in three places: first in the entity EJB component, then in the session EJB component, and finally in the web tier.

Fortunately, addressing these problems is fairly easy to do in a generic way. All you need is a mechanism for the caller to:

  • Access the unique ID

  • Find out if the exception has already been logged

  • If the exception has been already logged don ‚ t log it again.

We have already developed the MybankException and ExceptionInfo class that let us check if the exception is already logged. If not logged already, log the exception and set the logged flag to be true. These classes also generate a unique id for every exception. Listing 9.13 shows a sample.

Listing 9.13: Sample Exception Logging
 try {     CustomerDAO cDao = CustomerDAOFactory.getDAO();     cDao.createCustomer(CustomerValue); } catch (CreateException ce) {     //Assume CreateException is a subclass of MybankException     if (! ce.isLogged() ) {       String traceStr = StackTraceTool.getStackTraceAsString(ce);       LogFactory.getLog(getClass().getName()).error(               ce.getUniqueID() + ":" + traceStr);       ce.setLogged(true);     }     throw ce; } 
 

Listing 9.13 shows the logging scenario when the exception caught is of type MybankException . It is very much a fact that not all of the exceptions thrown by your application are in this hierarchy. Under such conditions it is even more important that the logging is centralized in one place since there is no mechanism to prevent duplicate logging for exceptions outside the MybankException hierarchy. That brings us to the idea of centralized logging. In the beginning of this section we said that it is easy and convenient to log exceptions on web-tier since most of the web-tier classes have a hierarchy. Let us examine this claim in more detail.




Struts Survival Guide. Basics to Best Practices
Struts Survival Guide: Basics to Best Practices (J2ee Survival Series)
ISBN: 0974848808
EAN: 2147483647
Year: 2004
Pages: 96

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net