Dealing with logging of exceptions in a Servlets/Jsp environment.

Ideally an exception should not be caught unless it can be handled gracefully. One must use checked exceptions for all errors the application can recover from, and unchecked exceptions for the errors the application cannot recover from. But, due to lack of proper understanding(and/or due to the confusion) of Java’s Checked Exception mechanism, we find most of the code with too many catch blocks or too many throws clauses besides method declaration there by leading to exception declaration aggregation.

My intention here is not to start another war pro or against checked exceptions. FYI, C# doesn’t have the concept of checked exceptions. Personally I prefer having very few checked exceptions which can be handled or recovered from(with no empty catch blocks or re-throws by wrapping or just logging) and the rest all as unchecked exceptions.

So, while throwing unchecked exceptions, it is useful to have a centralized block of code which logs what is happening for debugging purposes. Of course, you should be pragmatic and apply it according to your condition.

Whenever there is a request for a particular Servlet, before a particular Servlet is handed over the request, we can configure a Servlet Filter to intercept and log the requests and exceptions, this way you can avoid unnecessary catch blocks through out the code base.

In this blog I will show you a code sample of how we can do it. The code also deals with preventing memory leaks(during the undeployment of the web application) regarding Jdbc drivers which are registered and shutting down the loggers.

If you are not very clear about the best practices of exception handling, here are some very good posts, which you might find useful.

Java Checked and Unchecked Exceptions

Exception Management Best Practices

Which is better? Checked or Unchecked exceptions

With a quick StackOverflow or Google search you will find many other good articles. The mentioned posts give a good understanding of the best practices. I am not showing the Servlet API specific code(Filter and Listener) as the following global methods can be called from anywhere in the code. So, you can use the following class according to your needs.

 


/**
	Utility class with methods to call from javax.servlet.Filter and javax.servlet.ServletContextListener
	for unchecked exception logging and memory clean up.
	@author Rajasekhar
*/

public class WebAppUtils {
	
    //slf4j-log4j dependency
    private static final Logger logger = LoggerFactory.getLogger(WebAppUtils.class);

    //Called from the ExceptionLogger Filter's doFilter() method, which is mapped to /*
    public static void log(ServletRequest request, ServletResponse response, FilterChain chain) {
        try {
            chain.doFilter(request, response);
        } catch (Throwable exception) {
            //Something went wrong. log the whole stacktrace for debugging. 
            //Also get the root cause
            String rootCauseMessage = ExceptionUtils.getRootCauseMessage(exception);
            //Apache-commons-lang dependency
            request.setAttribute("rootCauseMessage", rootCauseMessage);
            if (exception instanceof RuntimeException) {
                logger.error("A Runtime Exception has occurred. The cause is " + rootCauseMessage, exception);
            	//Signal Tomcat or any other server to serve the user a user friendly error page
                throw new RuntimeException();
            }
            if (exception instanceof Exception) {
                logger.error("An Exception has occurred. The cause is " + rootCauseMessage, exception);
            } else {
                //Ideally errors should not be cought, but for debugging purposes at filter level it is useful
                logger.error("An Error has occurred. The cause is " + rootCauseMessage, exception);
            }
            //Signal Tomcat or any other server to serve the user a user friendly error page
            throw new RuntimeException();
        }
    }

    //Called when context is being destoyed from a listener. 
    //I am not showing the listener configuration in web.xml
    public static void cleanUp() {
    	    //De-Register the Jdbc drivers to prevent memory leaks. 
        //Quite useful (during the hot deploys) at the time of development and testing.
        deRegisterJdbcDrivers();

        if (logger.isInfoEnabled()) {
            logger.info("Jdbc drivers have been " +
                    "de-registered to prevent memory leak. Shutting down loggers.");
        }
        LogManager.shutdown();
    }

    private static void deRegisterJdbcDrivers() {
        Enumeration<Driver> drivers = DriverManager.getDrivers();
        while (drivers.hasMoreElements()) {
            Driver driver = drivers.nextElement();
            try {
                DriverManager.deregisterDriver(driver);
                logger.info(String.format("De-registering jdbc driver: %s", driver));
            } catch (SQLException e) {
                logger.info(String.format("Error de-registering driver %s", driver), e);
            }
        }
    }
}

The corresponding portion of web.xml:



	<filter>
	    <filter-name>logger</filter-name>
	    <filter-class>com.helical.ExceptionLogger</filter-class>
	</filter>

	<filter-mapping>
	    <filter-name>logger</filter-name>
	    <url-pattern>/*</url-pattern>
	</filter-mapping>

	//Configure Tomcat to serve the user with a user friendly error page
	<error-page>
	    <exception-type>java.lang.Throwable</exception-type>
	    <location>/WEB-INF/error.jsp</location>
	</error-page>

The error.jsp:



<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

    <div>
        Oops! Something went wrong. The cause is 
        <c:if test="${!empty requestScope.rootCauseMessage}">
            <c:out value="${requestScope.rootCauseMessage}"/>
        </c:if>
    </div>

 

Hope this helps and let me about know your opinions on this article.

B. Rajasekhar

Helical IT Solutions

Leave a Reply