ONJava.com    
 Published on ONJava.com (http://www.onjava.com/)
 See this if you're having trouble printing code examples


Reporting Application Errors by Email Reporting Application Errors by Email

by Sean C. Sullivan
09/29/2004

It is common practice for server-side applications to log messages to files on the server's file system. These logs are a vital source of information for system administrators and the application development team. If a user calls to report an error in the application, the developers can review the log files to determine what is happening in the application.

When your team has to support a mission-critical application, you can't afford to wait for users to tell you that the application is having problems. You need to be able to detect problems as soon as they occur. A simple and inexpensive solution is to implement an email notification system. The notification system will generate an email message whenever the application detects an error. If you are already using a standard logging framework like log4j, you can easily configure the logging system to broadcast error messages via email. In this article, we describe how to set up email notification for the two most widely used Java logging frameworks: log4j and java.util.logging.

log4j and java.util.logging

Let's start by reviewing the features and capabilities of log4j and java.util.logging. These logging frameworks provide the ability to categorize log messages, control over which messages will be logged, configurable message formatting, and configurable output destinations. The following table summarizes the core features of each logging framework.

  log4j java.util.logging
Manager class org.apache.log4j.LogManager java.util.logging.LogManager
Logger objects org.apache.log4j.Logger java.util.logging.Logger
Named loggers Supported Supported
Logging levels

Levels are declared in the org.apache.log4j.Level class. log4j has five pre-defined levels:

  1. FATAL
  2. ERROR
  3. WARN
  4. INFO
  5. DEBUG

Levels are declared in the java.util.logging.Level class. There are seven pre-defined levels:

  1. SEVERE
  2. WARNING
  3. INFO
  4. CONFIG
  5. FINE
  6. FINER
  7. FINEST
Log event object org.apache.log4j.spi.LoggingEvent java.util.logging.LogRecord
Output destinations Appender classes. These classes implement the org.apache.log4j.Appender interface. Handler classes. These classes extend java.util.logging.Handler.
Output message filtering Filter classes. These classes extend org.apache.log4j.spi.Filter. Filter classes. These classes must implement the java.util.logging.Filter interface.
Output message formatting Layout classes extend org.apache.log4j.Layout. Formatter classes extend java.util.logging.Formatter.
Programmatic configuration Supported Supported
Property file configuration Supported Supported
XML configuration Supported. The XML configuration file must comply with log4j.dtd. Not supported
Email notification SMTPAppender class SMTPHandler class (third-party extension)

If you are using java.util.logging, your application primarily uses the java.util.logging.Logger class to create log messages. This simple program demonstrates how to create a FINE-level message and a SEVERE-level message with the java.util.logging.Logger class:

import java.util.logging.*;
public class JDKLoggingExample
{
    private static final Logger log =
        Logger.getLogger(JDKLoggingExample.class.getName());
    public static void main(String[] args)
    {
        try
        {
            log.fine("This is a fine message.");
            int i = Integer.parseInt("Hello world");
        }
        catch (java.lang.Exception ex)
        {
            log.log(Level.SEVERE, "Caught an exception", ex);
        }
    }
}

log4j has a similar approach. An application uses log4j's org.apache.log4j.Logger class to produce log messages. The following program demonstrates how to produce a DEBUG-level message and an ERROR-level message via log4j's Logger class.

import org.apache.log4j.*;

public class Log4jExample
{

    private static final Logger log = 
        Logger.getLogger(Log4jExample.class);

    public static void main(String[] args)
    {
        try
        {
            log.debug("This is a debug message.");
            int i = Integer.parseInt("Hello world");
        }
        catch (java.lang.Exception ex)
        {
            log.error("Caught an exception", ex);
        }
    }
}

Appenders and Handlers

Both log4j and java.util.logging are capable of dispatching log messages to multiple destinations. log4j accomplishes this task by routing log messages through appender objects. Similarly, java.util.logging routes messages through handler objects. log4j users will typically configure a list of appenders in the log4j.xml file; java.util.logging users will typically configure a list of handlers in the logging.properties file.

log4j provides the SMTPAppender class so that applications can broadcast errors to an SMTP mail server. java.util.logging applications accomplish the same task with the SMTPHandler class.

Let's take a closer look at the SMTPAppender and the SMTPHandler.

log4j's SMTPAppender

The log4j JAR includes the org.apache.log4j.net.SMTPAppender class. With proper configuration, log4j will route error messages to the SMTPAppender. The SMTPAppender is then responsible for constructing an email message. The appender employs the JavaMail API to send the email message to an SMTP server.

Figure 1 illustrates the relationship between the log4j Logger, the SMTPAppender, and the SMTP server.

Figure 1
Figure 1. log4j Logger and SMTPAppender

The SMTPAppender makes it easy to broadcast application errors to your entire support team. Figure 2 shows an example email message from the SMTPAppender class.

Figure 2
Figure 2. An email message from the SMTPAppender

Next, we'll discuss how to configure log4j and how to enable the SMTPAppender.

Configuring log4j's SMTPAppender

You can configure log4j with a property file (typically, log4j.properties) or with an XML file (typically, log4j.xml). Since log4j XML configuration files allow for more flexibility than log4j property files, we'll focus our attention on XML.

The log4j.xml file has a top-level element, <log4j:configuration>. Inside of the configuration element, you will typically declare <appender> elements and a <root> element. The following example declares three appenders and attaches the appenders to the root logger:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration>
    <appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
        <param name="Target" value="System.out" />
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern"
              value="[%d{ISO8601}] %-5p %c %m %n" />
        </layout>
        <filter class="org.apache.log4j.varia.LevelRangeFilter">
            <param name="LevelMin" value="DEBUG"/>
            <param name="LevelMax" value="INFO"/>
        </filter>
    </appender>
    <appender name="STDERR"  class="org.apache.log4j.ConsoleAppender">
        <param name="Target" value="System.err" />
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern"
              value="[%d{ISO8601}] %-5p %c %m %n" />
        </layout>
        <filter class="org.apache.log4j.varia.LevelRangeFilter">
            <param name="LevelMin" value="WARN"/>
            <param name="LevelMax" value="FATAL"/>
        </filter>
    </appender>

    <!--                          -->
    <!-- Declare the SMTPAppender -->
    <!--                          -->
    <appender name="EMAIL"  class="org.apache.log4j.net.SMTPAppender">
        <param name="BufferSize" value="512" />
        <param name="SMTPHost" value="smtp.foobar.com" />
        <param name="From" value="log4j@server5" />
        <param name="To" value="neo@foobar.com" />
        <param name="Subject" value="[SMTPAppender] Application message" />
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern"
              value="[%d{ISO8601}]%n%n%-5p%n%n%c%n%n%m%n%n" />
        </layout>
        <filter class="org.apache.log4j.varia.LevelRangeFilter">
            <param name="LevelMin" value="ERROR"/>
            <param name="LevelMax" value="FATAL"/>
        </filter>
    </appender>

    <!--                           -->
    <!-- setup log4j's root logger -->
    <!--                           -->
    <root>
        <level value="all" />
        <appender-ref ref="STDOUT"/>
        <appender-ref ref="STDERR"/>
        <appender-ref ref="EMAIL" />
    </root>
</log4j:configuration>

Each appender has a logical name. In this example, the appender names are STDOUT, STDERR, and EMAIL. We associate the appenders to log4j's root logger by adding three <appender-ref> declarations inside of the <root> element. The ref attribute in the <appender-ref> must match one of the name attributes in the <appender> declarations.

Using nested <param> elements, you can tailor the SMTPAppender for your environment. The SMTPAppender supports the following parameters: From, To, Subject, SMTPHost, and BufferSize. Also, the <layout> element gives you control over the format of the body of the email message. The previous example uses org.apache.log4j.PatternLayout to display a date, the logging level, the logger name, and the log message.

For more information about log4j configuration, consult the Apache log4j web site.

SMTPHandler for java.util.logging

Unlike log4j, java.util.logging does not include any code for sending messages via SMTP. Fortunately, java.util.logging allows you to install custom handler classes. The SMTPHandler project provides a handler class named smtphandler.SMTPHandler. By installing this class, you can achieve functionality that is similar to log4j's SMTPAppender. The SMTPHandler class is responsible for creating the email message and sending it to the SMTP server. Figure 3 shows its control flow.

Figure 3
Figure 3. java.util.logging.Logger and the SMTPHandler

Like the SMTPAppender, the SMTPHandler uses the JavaMail API to communicate with the SMTP server. The SMTP server holds the message until it is time to deliver the message to a mail client. Figure 4 shows an example email message that was generated by the SMTPHandler class.

Figure 4
Figure 4. An email message from the SMTPHandler

Next, we'll discuss how to configure java.util.logging and how to enable the SMTPHandler.

Configuring SMTPHandler for java.util.logging

By default, java.util.logging reads the lib/logging.properties file in the JRE directory. You can adjust logging levels and add new handlers by editing this property file. It is important to know that java.util.logging requires all handler classes to be on the JVM system classpath. In order to use the SMTPHandler class, you will need to place three JAR files on your system classpath: the SMTPHandler JAR, the JavaMail JAR, and the JavaBeans Activation Framework JAR.

The logging.properties file contains a handlers property. The handlers property specifies a comma-separated list of handler classes. The following example declares two handlers, a ConsoleHandler and a SMTPHandler:

handlers=java.util.logging.ConsoleHandler,smtphandler.SMTPHandler
.level= FINEST
java.util.logging.ConsoleHandler.level=FINEST
java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter
smtphandler.SMTPHandler.level=WARNING
smtphandler.SMTPHandler.smtpHost=smtp.foobar.com
smtphandler.SMTPHandler.to=neo@foobar.com
smtphandler.SMTPHandler.from=appserver@server3
smtphandler.SMTPHandler.subject=[SMTPHandler] Application message
smtphandler.SMTPHandler.bufferSize=512
smtphandler.SMTPHandler.formatter=java.util.logging.SimpleFormatter

The SMTPHandler has seven customizable parameters: level, smtpHost, to, from, subject, bufferSize, and formatter. You should tailor their values as needed for your application environment.

For more information about java.util.logging configuration, consult Sun's J2SE platform documentation.

Common Pitfalls

To get the most out of the SMTPAppender and the SMTPHandler, your application code needs to correctly and consistently invoke the logging framework's API. Let's suppose that your web application unexpectedly throws a runtime exception. In a perfect world, the exception will be caught, logged using the logging framework's API, and reported via email to all interested parties.

However, you may encounter a situation where some of the application exceptions do not trigger an email message. This usually occurs when the application fails to invoke the logging API appropriately.

Let's examine three exception-handling examples:

Example Code Discussion
A
catch (Exception ex)
{
    System.err.println(ex.getMessage());
    displayErrorPage();
}

This code prints the exception message to stderr.

This type of code should be avoided because the logging framework is unaware that the application encountered an error. In this scenario, the logging framework never has an opportunity to generate an email message.

B
catch (Exception ex)
{
    ex.printStackTrace();
    displayErrorPage();
}

This code prints the exception stack trace to stderr.

The logging framework never receives notification about the error. The logging framework never has the opportunity to report the error.

C
catch (Exception ex)
{
    log.error("unexpected exception", ex);
    displayErrorPage();
}

This code logs an error message using the logging framework API.

Example C is preferable to examples A and B.

Your development team will need to be disciplined whenever they are writing exception-handling code. With proper exception-handling code in place, the email notification system can notify you about application errors.

Wrap-Up

We have learned about log4j's SMTPAppender and the SMTPHandler for java.util.logging. We discussed how to configure these features in their respective logging frameworks. With only a small amount of configuration work, your logging framework can notify you when there is a problem in your application.

Resources

Sean C. Sullivan has been developing Internet applications with Java since 1996. His recent work includes B2B web applications, various open source projects, and the development of an Internet e-commerce payment system at Intel.


Return to ONJava.com.

Copyright © 2009 O'Reilly Media, Inc.