Using logging within an EJB

Logging is a useful technique for recording exceptions and significant events which occur during the execution of an application. While there are many different logging APIs available, we will focus on those classes found in the java.util.logging package. If you are interested you may find alternate logging technologies such as log4j (http://logging.apache.org/log4j/1.2/) or the Simple Logging Facade for Java (SLF4J) (http://www.slf4j.org/) to be useful.

Getting ready

The process of creating using a logger involves:

  1. Importing the java.util.logging package
  2. Using the Logger class' static method getLogger to obtain an instance of a logger
  3. Using methods of the Logger to record application data
  4. Optionally, various filters, handlers, and formatter can be added to control the logging process

    Logging involves writing messages to a data store. The logged messages can be controlled using levels and filters. The Logging API has seven predefined logging levels:

    Level

    Meaning

    SEVERE

    The highest level normally reserved for critical errors

    WARNING

    Used for warning type messages

    INFO

    This message does not necessarily indicate an error but rather conveys general information

    CONFIG

    Used for configuration-type information

    FINE

    First of three levels of detail

    FINER

    Intermediate level of detail

    FINEST

    Indicates the greatest level of detail

    The Logger's overloaded log method's first argument is a Level value. The value assigned to this argument denotes the level of the log. However, this does not mean the message will be logged. A logger will have a level specified for it and will log all messages at this or a higher level. The setLevel method determines the level of logging. Any request using levels below that value will be ignored. In addition, even if the message is accepted for logging, a filter may restrict those messages from being logged.

    When the message is logged, it can be processed and formatted to add additional content to the message. For example, a time stamp can be added to the message eliminating the need to include this information in each log method call.

    A java.util.logging.Handler derived class actually writes the message to a data store. A data store can be one of several different types of stores, though files are the most common type. There are two standard Handler classes available: MemoryHandler and StreamHandler. It is possible to create your own handler if needed.

    The following figure depicts the structure of the Java Logging API.

    Getting ready

How to do it...

Create a Java EE application called LoggingApplication. We will use this application to demonstrate various logging techniques. In the EJB module create a package called packt and a stateless session bean called PhoneNumber. This EJB consists of a default constructor and four methods. The purpose of the EJB is to validate and format a phone number. Using these elements of the EJB, we will demonstrate logging.

First add a private logger variable and a default constructor. Within the constructor, create an instance of a Logger using the getLogger method and use the instance to log the creation of the Logger.

@Stateless
public class PhoneNumber {
private Logger logger;
public PhoneNumber() {
logger = Logger.getLogger("phonenumber");
logger.log(Level.INFO, "Phone number logger created");
}
...
}

Next, add a format method which accepts three integers representing the three parts of a standard telephone number. Within this method we will call three different validation methods, each of which may throw an exception. If there are no errors, then a simple formatted string is returned and this event is logged at the level FINEST. If there are errors, then the exception is logged as a simple message at the level FINE. Using different levels allows us to record events in the application based on our interest.

public String format(int areaCode, int prefix, int lineNumber) {
try {
validateAreaCode(areaCode);
validatePrefix(prefix);
validateLineNumber(lineNumber);
logger.log(Level.FINEST, "Formatted phone number returned");
return "(" + areaCode + ")" + prefix + "-" + lineNumber;
}
catch(InvalidAreaCodeException e) {
logger.log(Level.FINE, "InvalidAreaCodeException");
}
catch(InvalidPrefixException e) {
logger.log(Level.FINE, "InvalidPrefixException");
}
catch(InvalidLineNumberException e) {
logger.log(Level.FINE, "InvalidLineNumberException");
}
return "";
}

Next, add the three methods used for validation. These methods perform a simple test on their arguments. A more sophisticated test would be needed in a production application.

private boolean validateAreaCode(int areaCode) throws InvalidAreaCodeException {
if (areaCode < 0 || areaCode > 999) {
throw new InvalidAreaCodeException();
}
return true;
}
private boolean validatePrefix(int prefix) throws InvalidPrefixException {
if (prefix < 0 || prefix > 999) {
throw new InvalidPrefixException();
}
return true;
}
private boolean validateLineNumber(int lineNumber) throws InvalidLineNumberException {
if (lineNumber < 0 || lineNumber > 9999) {
throw new InvalidLineNumberException();
}
return true;
}
}

Add three classes for the three different exceptions.

public class InvalidAreaCodeException extends java.lang.Exception {
public InvalidAreaCodeException() {}
public InvalidAreaCodeException(String message) {super(message);}
}
public class InvalidLineNumberException extends Exception {
public InvalidLineNumberException() {}
public InvalidLineNumberException(String message) {super(message);}
}
public class InvalidPrefixException extends Exception{
public InvalidPrefixException() {}
public InvalidPrefixException(String message) {super(message);}
}

Create a servlet called LoggingServlet in the servlet package of the Web module. Inject the PhoneNumber EJB and invoke its format method as shown below. Notice the line number is too large.

public class LoggingServlet extends HttpServlet {
@EJB
PhoneNumber phoneNumber;
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
try {
out.println("<html>");
out.println("<head>");
out.println("<title>Servlet LoggingServlet</title>");
out.println("</head>");
out.println("<body>");
out.println("<h1>Phone Number: " + phoneNumber.format(202, 555, 10003) + "</h1>");
out.println("</body>");
out.println("</html>");
} finally {
out.close();
}
}

Execute the servlet. The output in part shows the creation of the logger. The INFO: prefix is added automatically reflecting the level of the message.

...

INFO: Phone number logger created

The reason the InvalidLineNumberException is not displayed is because, by default, the Logger is set to the level INFO. It will only log those messages at that level or above. Since the exception is logged at the level FINE, it does not show up.

To rectify this situation, use the Logger's setLevel command after the creation of logger with an argument of Level.ALL.

logger.setLevel(Level.ALL);

Re-executing the servlet will produce the expected results.

...

INFO: Phone number logger created

FINE: InvalidLineNumberException

How it works...

The PhoneNumber EJB was created to illustrate the logging approach. In its constructor an instance of a Logger class was created. The string argument of the getLogger method was used to name the Logger instance. While it is not discussed here, the Java Logger API provides a sophisticated hierarchy of Loggers that can provide a richer set of logging capabilities than presented here.

The log method is overloaded. In this example, we used a simple variation of the method. The first argument specified the logging level and the second argument was the message we wanted to log. In the format method, exceptions that were caught were logged. The servlet used an invalid line number which resulted in the exception being logged once the appropriate logging level was set using the setLevel method.

There's more...

At the beginning of this recipe the creation of a Logger was accomplished as follows:

@Stateless
public class PhoneNumber {
private Logger logger;
public PhoneNumber() {
logger = Logger.getLogger("phonenumber");
logger.log(Level.INFO, "Phone number logger created");
}
...
}

However, this approach may not be suitable in all environments. The technique is not thread-safe and we cannot use the logger from a static method. An alternative approach declares the logger as follows:

@Stateless
public class PhoneNumber {
private static final Logger logger = Logger.getLogger("phonenumber");
public PhoneNumber() {
logger.log(Level.INFO, "Phone number logger created");
}
...
}

The static keyword means the logger can be used by static and instance methods. Making it final results in a single instance of the object and avoids many thread issues as the methods of the Logger class are thread-safe.

See also

The Using an interceptor for logging and exception handling recipe that follows incorporates logging in an interceptor.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.149.228.138