Showing posts with label logging. Show all posts
Showing posts with label logging. Show all posts

2009-03-20

Bridging java.util.logging to SLF4J

This little code snippet will pass everything logged using java.util.logging (JUL) to SLF4J AND remove JUL's default Handler, so to avoid having everything logged twice:

// Jersey uses java.util.logging - bridge to slf4
SLF4JBridgeHandler.removeHandlersForRootLogger();
SLF4JBridgeHandler.install();

The reason I'm posting this is that it took me a little longer than expected to write it. I've never really used JUL, but I've recently been working on a small JAX-RS application using the Jersey framework.

Jersey uses JUL as it's logging framework. The rest of the application uses SLF4J and Logback, so I wanted everything logged using JUL redirected to SLF4J. For that purpose the SLF4J project provides jul-to-slf4j.jar and the class SLF4JBridgeHandler as described here.

According to the above mentioned site all that's required is to call SLF4JBridgeHandler.install(), and that also works very well, except it just installs an extra Handler on JUL's root logger - it dosn't remove the handler which is there by default. That also makes sense, but since I've never used JUL it took me a while to figure out how to make it stop logging anything. I've seen a lot of people asking how to "replace" JUL logging with SLF4J, and answers was far fewer, so I thought I'd share my solution.

2008-08-07

Test Logging

How do you test (in a unittest) that a method writes the log statements you expect it to write?

Apparantly being able to do that is not a very common requirement - at least Google wasn't it's normal helpful self when I asked it how to, and frankly I never needed it myself. Before now, that is.

AbstractInterruptibleChannel.close() throws one of those pesky checked IOExceptions, which you always have to write a lot of verbose and error-prone code to "handle", and which you really can't do anything about anyway.

Code working with two IO channels normally looks something like this:

AbstractInterruptibleChannel c1, c2;
try {
    // Open and use channels c1 and c2
} catch (IOException ioe) {
    // Display or log errormessage or whatever
} finally {
    try {
        c1.close();
    } catch (IOException e) {
        logger.error("Failed to close a channel.", e);
    }
    try {
        c2.close();
    } catch (IOException e) {
        logger.error("Failed to close a channel.", e);
    }
}

I would much rather have something like this:

AbstractInterruptibleChannel c1, c2;
try {
    // Open and use channels c1 and c2
} catch (IOException ioe) {
    // Display or log errormessage or whatever
} finally {
    closeChannels(c1, c2);
}

Therefore I wrote a small utility method to close IO Channels and encapsulate the try/catch hassle normally associated with that:

public static void closeChannels(AbstractInterruptibleChannel... channels) {
    for (AbstractInterruptibleChannel channel : channels) {
        if (channel == null) continue;
        try {
            channel.close();
        } catch (Exception e) {
            logger.error("Failed to close channel.", e);
        }
    }
}

It suppresses errors - if an Exception occurs it will be logged, but otherwise silently ignored. And now we are finally getting to what I really wanted to write about. I wanted to verify that the method acually logs an error message when it fails to close a channel.
That sounds like something which should be very easy to do. It's not hard, but not exactly obvious either. As mentioned earlier I first tried Google without result. The SLF4J. and Logback manuals didn't have any examples exactly matching my needs either, but based on the Logback documentation I did manage to write a Filter to capture whatever was logged during executon of the test. It took quite a bit of code though, and after Ceki Gülcü (the author of Logback) pointed out that there are applicaple examples in the Logback code I dropped the Filter idea and used Logbacks ListAppender instead:

public void testCloseChannels() { 
    // Capture log entries from FileUtil 
    Logger logger = (Logger) LoggerFactory.getLogger(FileUtil.class); 
    logger.setAdditive(false); 
    ListAppender<LoggingEvent> listAppender = new ListAppender<LoggingEvent>(); 
    listAppender.start(); 
    logger.addAppender(listAppender); 
    // Close a mix of normal, broken and completely missing channels 
    closeChannelse(null, new UnclosableChannelStub(), null, new ChannelStub(), new ChannelStub()); 
    // Restore normal logging 
    logger.detachAppender(listAppender); 
    logger.setAdditive(true); 
    // Perform checks 
    assertEquals("Exactly 1 failure should have been logged.", 1, listAppender.list.size()); 
} 

Now the test works exactly as I wanted.

For a while I have wanted to take a closer look at Hamcrest and, to avoid doing any real work, I decided to further refine the test to use Hamcrest matchers to perform the same test.

The following is an example of a Matcher which matches LoggingEvents with a particular message text.

public class LoggingEventMatchers {

    /**
     * Does the LoggingEvent have a particular message?
     */
    @Factory
    public static TypeSafeMatcher&lt;LoggingEvent&gt; message(String message) {
        return new MessageMatcher(message);
    }


    /**
     * Matcher to check if an LoggingEvent has a particular message.
     *
     * @author Claus Nielsen
     */
    private static class MessageMatcher extends TypeSafeMatcher&lt;LoggingEvent&gt; {

        private final String theMessage;

        /**
         * Constructor.
         * 
         * @param theMessage
         *            The predicate evaluates to true for LoggingEvents
         *            with this message.
         */
        private MessageMatcher(String theMessage) {
            this.theMessage = theMessage;
        }

        @Override
        public boolean matchesSafely(LoggingEvent event) {
            return theMessage.equals(event.getMessage());
        }

        @Override
        public void describeTo(Description description) {
            description.appendText("the message ").appendValue(theMessage);
        }

    }


}

In addition to writing a set of matchers similar to the one above, I moved the logger configuration code to a seperate class called LogCapturingTestTemplate, and now the test itself is as simple as this:

 public void testErrorLoggedForUnclosableChannels() {
 // Capture logging events while closing come Channels.
 final CapturedLoggingEvents loggingEvents = new LogCapturingTestTemplate(FileUtil.class) {
  protected void monitorThis() {
   closeChannels(null, c1, new UnclosableChannelStub(), null, c2);
  }
 }.execute();
  assertThat(loggingEvents.getEvents(), hasItem(
   allOf(level(ERROR), message(FileUtil.FAILED_TO_CLOSE_CHANNEL))
  ));
 }

This test is actually far more specific. Where the previous version only checked that exactly one entry was logged during the call to closeChannels, this test checks that an ERROR message with a specific message is logged.
Full source code for both the test and the Hamcrest-style matchers is part of the clanie-core project (version 0.0.1). The source is available in the Git repository, and both binary builds, source, javadoc and project documentation can be found in the Maven repository.