Logging in Java


Java supplies complete logging facilities in the package java.util.logging.[6] For an exercise, you will learn to use the logging package to capture exception events. The Student constructor throws an exception. Let's log it. Here is a slightly modified Student constructor with a placeholder for logging.

[6] While Java's logging facilities are adequate for most needs, many developers prefer to use the freely available Log4J package (http://logging.apache.org/log4j/docs/).

 public Student(String fullName) {    this.name = fullName;    credits = 0;    List<String> nameParts = split(fullName);    if (nameParts.size() > MAX_NAME_PARTS) {       String message =          String.format(Student.TOO_MANY_NAME_PARTS_MSG,                        fullName, MAX_NAME_PARTS);       // log the message here       throw new StudentNameFormatException(message);    }    setName(nameParts); } 

The first question is how to test that an appropriate message gets logged when you throw the exception. Or should you bother with such a test?

Remember the first rule of testing: Test everything that can possibly break. Logging can definitely break. Take a stab at a test:

 public void testBadlyFormattedName() {    final String studentName = "a b c d";    try {       new Student(studentName);       fail("expected exception from 4-part name");    }    catch (StudentNameFormatException expectedException) {       String message =          String.format(Student.TOO_MANY_NAME_PARTS_MSG,                        studentName, Student.MAX_NAME_PARTS);       assertEquals(message, expectedException.getMessage());       assertTrue(wasLogged(message));    } } private boolean wasLogged(String message) {    // ??? } 

How do you determined whether or not a message was logged? The method name wasLogged represents the intent of what you need to determine. The contents of wasLogged will represent how you determine it.

Learning how to test an unfamiliar API such as logging, sometimes involves playing around with the API until you understand how it works. Without this more complete understanding of the API, you may not be able to figure out how to test it. In this exercise, you'll write a bit of "spike" code that provides you with a better understanding of the logging API. Then you'll throw away the spike code, write a test, and write the production code to meet the test specification.

In Student, create an "intention message" in the constructor:

 public Student(String fullName) {    this.name = fullName;    credits = 0;    List<String> nameParts = split(fullName);    if (nameParts.size() > MAX_NAME_PARTS) {       String message =          String.format(Student.TOO_MANY_NAME_PARTS_MSG,                        fullName, MAX_NAME_PARTS);       log(message);       throw new StudentNameFormatException(message);    }    setName(nameParts); } 

Then code the log method.

 private void log(String message) {    Logger logger = Logger.getLogger(getClass().getName());    logger.info(message); } 

To log a message, you need a Logger object. To obtain a Logger object, you call the Logger factory method getLogger. As a parameter to getLogger, you pass the name of the subsystem within which you are logging. Later in this lesson (in the section entitled Logging Hierarchies), you'll learn more about the relevance of the subsystem name. Passing in the name of the class is typical and will suffice.

If a Logger object with that name already exists, getLogger returns that logger. Otherwise, getLogger creates and returns a new Logger object.

The second line in log calls the Logger method info, passing the message parameter. By using the info method, you request that the message be logged at an informational level. The logging API supports several levels of message log. The Logger class provides a method that corresponds to each level. From highest to lowest, the levels are severe, warning, info, config, fine, finer, and finest.

Each logger object maintains its own logging level. If you attempt to log a message at a lower level than the logger's level, the logger discards the message. For example, if a logger is set to warning, it logs only severe and warning messages.

You can obtain and set the logging level on a logger using getLevel and setLevel. You represent the logging level with a Level object. The Level class defines a set of Level class constants, one to represent each logging level. The Level class also supplies two additional class constants: Level.ALL and Level.OFF, to designate either logging all messages or logging no messages.

Modify the wasLogged method in StudentTest to return false. Run all your tests. By returning false, you cause testBadlyFormattedName to fail. This failure reminds you that you will need to flesh out the test.

On the console, you should see something like:

 Apr 14, 2005 2:45:04 AM sis.studentinfo.Student log INFO: Student name 'a b c d' contains more than 3 parts 

You have successfully logged a message!

The UML class diagram in Figure 8.2 shows the relationship between Logger and Level. It also includes Logger's relationship to other relevant classes that you will learn about in the upcoming sections.

Figure 8.2. The Logger Hierarchy




Agile Java. Crafting Code with Test-Driven Development
Agile Javaв„ў: Crafting Code with Test-Driven Development
ISBN: 0131482394
EAN: 2147483647
Year: 2003
Pages: 391
Authors: Jeff Langr

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