Colour-coded Console Logging with Log4J

Posted in Java by Dan on April 9th, 2006

A few years ago I knocked up a Log4J appender that used ANSI escape sequences to colour-code log messages based on their priority. This is quite an effective way of dealing with the information overload from console logging without having to disable the lower log levels. Your eyes are immediately drawn towards the more important messages but the detailed information is still there for you to diagnose the problems.

Unfortunately I can’t get it to work under Windows (even using Cygwin), but if you are using an ANSI-enabled terminal (such as most of the Linux terminals), it’s pretty helpful. It’s also trivial to implement, so here it is:

import org.apache.log4j.ConsoleAppender;
import org.apache.log4j.Level;
import org.apache.log4j.Priority;
import org.apache.log4j.spi.LoggingEvent;   
 
/**
 * Colour-coded console appender for Log4J.
 */
public class ANSIConsoleAppender extends ConsoleAppender
{
    private static final int NORMAL = 0;
    private static final int BRIGHT = 1;
    private static final int FOREGROUND_BLACK = 30;
    private static final int FOREGROUND_RED = 31;
    private static final int FOREGROUND_GREEN = 32;
    private static final int FOREGROUND_YELLOW = 33;
    private static final int FOREGROUND_BLUE = 34;
    private static final int FOREGROUND_MAGENTA = 35;
    private static final int FOREGROUND_CYAN = 36;
    private static final int FOREGROUND_WHITE = 37;   
 
    private static final String PREFIX = "\u001b[";
    private static final String SUFFIX = "m";
    private static final char SEPARATOR = ';';
    private static final String END_COLOUR = PREFIX + SUFFIX;   
 
    private static final String FATAL_COLOUR = PREFIX
      + BRIGHT + SEPARATOR + FOREGROUND_RED + SUFFIX;
    private static final String ERROR_COLOUR = PREFIX
      + NORMAL + SEPARATOR + FOREGROUND_RED + SUFFIX;
    private static final String WARN_COLOUR = PREFIX
      + NORMAL + SEPARATOR + FOREGROUND_YELLOW + SUFFIX;
    private static final String INFO_COLOUR = PREFIX
      + NORMAL+ SEPARATOR + FOREGROUND_GREEN + SUFFIX;
    private static final String DEBUG_COLOUR = PREFIX
      + NORMAL + SEPARATOR + FOREGROUND_CYAN + SUFFIX;
    private static final String TRACE_COLOUR = PREFIX
      + NORMAL + SEPARATOR + FOREGROUND_BLUE + SUFFIX;   
 
    /**
     * Wraps the ANSI control characters around the
     * output from the super-class Appender.
     */
    protected void subAppend(LoggingEvent event)
    {
        this.qw.write(getColour(event.getLevel()));
        super.subAppend(event);
        this.qw.write(END_COLOUR);   
 
        if(this.immediateFlush)
        {
            this.qw.flush();
        }
    }   
 
    /**
     * Get the appropriate control characters to change
     * the colour for the specified logging level.
     */
    private String getColour(Level level)
    {
        switch (level.toInt())
        {
            case Priority.FATAL_INT: return FATAL_COLOUR;
            case Priority.ERROR_INT: return ERROR_COLOUR;
            case Priority.WARN_INT: return WARN_COLOUR;
            case Priority.INFO_INT: return INFO_COLOUR;
            case Priority.DEBUG_INT:return DEBUG_COLOUR;
            default: return TRACE_COLOUR;
        }
    }
}

This should work with both the latest version of Log4J, which includes a TRACE level, and the previous versions. Using it is simply a matter of making sure the class is on the classpath with Log4J and referring to it from the configuration file.

Effective Bug Tracking – Ownership & Responsibility

Posted in Software Development by Dan on April 5th, 2006

The first key premise of effective bug tracking is the concept of ownership. Ownership is crucial in software teams because without ownership there is no responsibility, and without responsibility there is no direction. In order for a bug to get fixed in a timely manner there must, at all times, be somebody, a single individual, who is personally responsible for ensuring that that particular bug gets fixed. This person may be a programmer, but it could equally be the project manager or someone else. All that is important is that there is one person, and no more than one person, to be held accountable.

If this all seems obvious so far, consider that in many bug tracking applications, bugs are entered and then remain unassigned until somebody goes in and chooses who will deal with the problem. Where’s our accountability there? From the bug tracking application’s perspective at least, there is nobody responsible. We can do better than that. Golden rule number one is this:

“From the point at which the bug is entered into the system, and at all times afterwards, there will be somebody, a single individual, who owns that bug.”

In other words, there can never, at any time, be such a terrible thing as an unassigned, unresolved bug.

So how do we achieve this? Who does the bug get assigned to when it is first entered?

We could let the person who reports the bug decide, but more often than not the bug reporter is a software tester. They may well know who is the best person to assign the bug to, but we shouldn’t assume that they do. Futhermore, a software tester really shouldn’t, and probably won’t, have the authority to decide the workload of individual developers. All-in-all, it’s probably best that we remove these destructively-minded pessimists from this decision-making process.

Ideally the system should be able to provide us with a sensible default owner. Fortunately this is pretty straightforward to achieve. If we apply the same ownership rules to projects that we have applied to bugs, every project in the system will have an owner. Therefore every bug reported against a project has a ready-made default owner.

This approach works great for small projects with just one or two team members but doesn’t really scale. With a team of 20 developers the project owner would have to go through the list of bugs and re-assign each one to an appropriate team member. This is not much of an improvement on systems that leave the bugs unassigned initially.

This problem however is just one of granularity. By allowing projects to be sub-divided into components (and of course having an owner for each component), we can make the approach scale quite nicely. Bugs are then reported against components and initially assigned to the owner of that component. When components are added to projects their default owner is the project owner, but this can be over-ridden.

The sub-division of a project into components must necessarily be quite coarse-grained. Bugs are reported by testers and these testers must easily be able to identify the various application components without an intimate knowledge of the implementation. I see no reason to further divide components into sub-components. There is little to be gained from such a hierarchical composition.