patkua@work

The intersection of technology and leadership

Page 19 of 53

Testing logging with Logback

On my current project, we’re using the logback framework (behind SL4J) to do logging. For some parts of our system, it was particularly important some information made their way into the log files, and so we wanted to not test the correct output. Rather than do it with interaction based tests, I followed the pattern that I described in a previous post.

Here’s a test I might write (note that I’m writing the test in a way to actually test the appender behaviour in this case because my domain class doesn’t nothing special):

package com.thekua.spikes;

import org.junit.After;
import org.junit.Test;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;

public class LogbackCapturingAppenderTest {
    @After
    public void cleanUp() {
        LogbackCapturingAppender.Factory.cleanUp();
    }

    @Test
    public void shouldCaptureAGivenLog() throws Exception {
        // Given
        LogbackCapturingAppender capturing = LogbackCapturingAppender.Factory.weaveInto(OurDomainWithLogger.LOG);
        OurDomainWithLogger domainClass = new OurDomainWithLogger();

        // when
        domainClass.logThis("This should be logged");

        // then
        assertThat(capturing.getCapturedLogMessage(), is("This should be logged"));
    }

    @Test
    public void shouldNotCaptureAGiveLogAfterCleanUp() throws Exception {
        // Given
        LogbackCapturingAppender capturing = LogbackCapturingAppender.Factory.weaveInto(OurDomainWithLogger.LOG);
        OurDomainWithLogger domainClass = new OurDomainWithLogger();
        domainClass.logThis("This should be logged at info");
        LogbackCapturingAppender.Factory.cleanUp();

        // when
        domainClass.logThis("This should not be logged");

        // then
        assertThat(capturing.getCapturedLogMessage(), is("This should be logged at info"));
    }
}

And the corresponding Logback appender used in tests.

package com.thekua.spikes;

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.Appender;
import ch.qos.logback.core.AppenderBase;

import java.util.ArrayList;
import java.util.List;

public class LogbackCapturingAppender extends AppenderBase<ILoggingEvent> {
    public static class Factory {
        private static List<LogbackCapturingAppender> ALL = new ArrayList<LogbackCapturingAppender>();

        public static LogbackCapturingAppender weaveInto(org.slf4j.Logger sl4jLogger) {
            LogbackCapturingAppender appender = new LogbackCapturingAppender(sl4jLogger);
            ALL.add(appender);
            return appender;
        }

        public static void cleanUp() {
            for (LogbackCapturingAppender appender : ALL) {
                appender.cleanUp();
            }
        }
    }

    private final Logger logger;
    private ILoggingEvent captured;

    public LogbackCapturingAppender(org.slf4j.Logger sl4jLogger) {
        this.logger = (Logger) sl4jLogger;
        connect(logger);
        detachDefaultConsoleAppender();
    }

    private void detachDefaultConsoleAppender() {
        Logger rootLogger = getRootLogger();
        Appender<ILoggingEvent> consoleAppender = rootLogger.getAppender("console");
        rootLogger.detachAppender(consoleAppender);
    }

    private Logger getRootLogger() {
        return logger.getLoggerContext().getLogger("ROOT");
    }

    private void connect(Logger logger) {
        logger.setLevel(Level.ALL);
        logger.addAppender(this);
        this.start();
    }

    public String getCapturedLogMessage() {
        return captured.getMessage();
    }

    @Override
    protected void append(ILoggingEvent iLoggingEvent) {
        captured = iLoggingEvent;
    }

    private void cleanUp() {
        logger.detachAppender(this);

    }
}

I thought it’d be useful to share this and I’ve created a github project to host the code.

General testing approach for logging in Java

I’ve seen a number of different patterns for testing logging and we just ran into this again at work, so I thought it’d be worth writing up a couple of them.

Simple logging
Many of the java based logging frameworks allow you to log and then choose to separate what you log from what needs to be done by attaching appenders to them. The common pattern here is to declare a static log instance inside each class, typically using the fully qualified class name. Most logging frameworks then treat this as a hierarchy of loggers that allow you to configure what type of logging at different levels.

I find the best strategy for testing this style of logging is to add an in memory appender that will capture the output that is sent to the logging framework. I will post an example with given frameworks in a second, but here are a few different concerns you need to think about:

  • Getting a reference to the log – Most loggers are made private static. You can choose to break encapsulation slightly by weakening the access to package local just so that a test can get access to the log. I find that injecting it in as a dependency in the constructor appears to complicated, and dislike setter based dependency injection in an attempt to keep fields more immutable.
  • Creating an appender to capture the output – This is where you’ll have to go to whichever logging framework and find out how appenders work. Most have a console appender, or something that you can simply extend and capture a logging event
  • Make the appender capture the output – This is pretty easy. You must make a choice whether or not you want only your appender to capture the log events, or if you want it to go to other appenders as well.
  • Clean up – The consequence of adding an appender with state is not a problem in a single test. When wired up across a long test suite, you potentially increase the amount of memory being consumed to the point where you’ll get out of memory errors. I find that it’s important to make sure that your appenders remove themselves from the logger at the end of the test to avoid side effects and to make sure they get garbage collected by the JVM. In a following post, you’ll see the pattern I tend to use.

More complex logging services
If you’re application requires more than just file based logging, and other actions need to be performed, you may still consider injecting a type of logger into the class that uses it. At this point, testing it becomes like any other interaction-based test and you can probably use mocking or stubbing to test the correct things are passed to to it.

By default, I wouldn’t really go through the motions of setting up a logging service unless there was some real need for it. The standard style of loggers and configuration of behaviour gives you quite a lot already.

Converting Code to Images

I needed to convert some code into an image for a presentation recently and found a number of different ways of doing so. The easiest, of course, is when you take a screen capture of the code in your IDE. This fails when your code goes well past the side of your screen and you still want to show it all.

Alternatively, you can do this in a two step process.

  • Save the code to HTML – I know that both IntelliJ and NetBeans allow you to do this for Java code.
  • Use webkit to save to an image – I used PhantomJS for doing this. Take a look at the example code of how to do this.

Leave a comment if you’ve had any other good experiences doing this. I’d be interested in other ways that have worked for you.

String XML interpolation in Scala

In java, if you’re formatting a small XML document, it might be tempting to simply do a String.format to substitute it directly into a string. Transitioning into scala, this is then easy to convert into one of their XML representations with XML.loadString. You might very well end up with code that looks like this:

val myValue = "will be substituted"
val xml = XML.loadString(String.format("<node>%s</node>", node))

You can actually just do this inline with scala directly, and end up with this instead

val myValue = "will be substituted"
val xml = <node>{myValue}</node>

Neat-o!

Coaching Tool: Me We Us

Short Description: A facilitation technique (e.g. for retrospectives) or also for planning that helps the quieter people to also come up with ideas.

Original source: Facilitation training by Grape People.

Example Use:
Questions are prepared by facilitators. Three different rounds

  1. Me – Everyone writes down answers for him/herself using sticky notes
  2. We – Discussing with your neighbour and answers refined
  3. Us – Combine all issues into one big chart

Contributed from the participants of my XP2011 workshop

Coaching Tool: Circle of Questions

See Doc List’s original post.

Comments:
Gets everyone involved. People understand better by explaining. It’s really about learning by teaching. Generative thinking. Works well if you go around twice. Widely applicable. Can be used for almost anything. Makes a non-talkative group more talkative.

Tips:
Enforce the circle. One person at a time and use a talking stick/token. Egg timers work well for Italian and Spanish groups. The more challenge the team has to stay on time, the more rituals need to be introduced/enforced.

Contributed from the participants of my XP2011 workshop

Coaching Tool: The High Performance Tree

Short Description: A way to create a metaphor that reminds the team of the holistic view of the team

Original Source: Looks like it’s here.

Example Use:
Labelling the expected fruits, very useful when a team has the fundamentals but could be better. Used as a roadmap to move towards higher performances. Shows that many practices are rooted in values.

Notes/comments:
Doesn’t always work. Some people don’t connect to the metaphor. In that case, the technique should be dropped. Can be used in open spaces to show the value streams, etc.

Potential Variants:
Garden or orchard -> organic seems to work better than mechanic. Allows you to talk about the weeds

Contributed from the participants of my XP2011 workshop

Coaching Tool: 7 x 7 x 7

Short Description: What I’m doing in the next seven hours, next seven days, seven weeks put onto a board

Example use
Introduction of yourself as a coach, and used to articulate what the coach does in flip chart using visual information. Set up a board with three columns with the title “7 Hours”, “7 Days”, “7 Weeks” and add activities to demonstrate what you’re doing.

Contributed from the participants of my XP2011 workshop

« Older posts Newer posts »

© 2024 patkua@work

Theme by Anders NorenUp ↑