Simple Log User Guide

Simple Log - Recommended Use

Lost?

Here's just a few tips about how to use Simple Log in your applications.
If you haven't already read the Quick Start, you should go read it first, as it contains a number of important steps to follow when using Simple Log that won't be repeated here.

When and Where Should I Log?

Obviously, this is one of the most important questions when using logging. In general, I try to think about this when deciding what to log:

I can't add logging at runtime, I can only turn it on and off.

If you can get this drilled into your head and start to make logging a natural part of writing code, you will find your logging will be much more useful once your application is being run. This is especially true once it goes into production and you don't have an opportunity to add more logging; if what you logged when you first wrote the code wasn't sufficient to debug your problem, it's too late.

Let's talk practically. I usually log the entry into and exit out of every method that isn't a simple accessor or mutator. If there is a fork in my code (e.g. an if or if/else), I will either log straight after the fork (on both sides) or log the variable or variables that are used to determine how the fork is executed.

Example:

if (record.isNew())
{
   log.debug("Creating ID for new record");
   ...
}

Sometimes I'll change the code at forks like this to accomodate my logging requirements.

Example:

if (file.exists() && file.isNormalFile() && file.canRead())
{
   ...
}
else
{
   ...
}

boolean exists = file.exists();
boolean normalFile = file.isNormalFile();
boolean readable = file.canRead();

log.verboseObject("exists", exists);
log.verboseObject("normalFile", normalFile);
log.verboseObject("readable", readable);

if (exists && normalFile && readable)
{
   ...
}
else
{
   ...
}

Some people will have an immediate negative reaction to this kind of behaviour, usually int he form of "I'm not going to declare a VARIABLE just so I can log!" Fair enough. You don't have to. I find that this makes my code easier to read and my logging easier to write. But this page is called 'Recommended Use', so feel free to ignore anything which causes a violent reaction.

Exceptions

Whenever I catch an exception, I log it. This just makes sense. Exceptions indicate less-than-normal program flow, a condition you'll usually want to be appearing in the log pretty prominently.

However, I have got into the habit of logging the a message about the exception at an appropriate level, but not always logging the actual exception at the same level. This is because I believe the exception details and stack trace are, in some cases, not 100% necessary.

In general, if an exception is in some respects normal (e.g. FileNotFoundException) and the program can recover from it, I'll log a message at the Warn level and the actual exception at the Debug level:

log.warn("File not found exception");
log.dbe(DebugLevel.L5_DEBUG, e);

If the exception is more unexpected than this kind, I'll usually log the exception and the message at the Error level as there's much less chance that the exception will be reproducible.

One rule that I think is really important is never to catch an exception just for the purpose of logging it. So don't do this:

try
{
   ...
}
catch (MyException me)
{
   log.debugException(me);
   throw me;
}

I have seen lots of code like this, and what often happens is that, when an exception does occur, it gets printed in the log between two and five times. A similar guideline applies if an exception is being caught just to be "wrapped" in another exception and "re-thrown". You can pretty safely assume that some other code will log the wrapping exception (if it needs to), including the information from the wrapped exception, and so it's really only necessary to log the wrapped exception at something like the Verbose level.

As with all rules, there is an exception to this one (no pun intended), which is that you should catch exceptions just for the purpose of logging them at the boundaries of inter-process calls. That is, I surround the whole body of any implementation of a CORBA, Remote or Web Service interface with a try/catch like the one above, making sure to catch and throw every specific type of exception.

What to log at each level

Lost?

I think that what I believe should be logged at each level is pretty thoroughly documented in the javadoc for DebugLevel .

But one thing I'd like to add to this is this: avoid the temptation to log messages and objects in "less important" classes at the more verbose levels. The decision as to which class has log output that is more important will be made by the person looking at the log file. If you use the levels consistently throughout every class, it will make it much easier for that person to decide which level to set for each package or class.

When to use instance logging

Instance logging is when you create a SimpleLogger for each instance of a class, rather than just one for all instances of the class. The added value is that each message output by instance loggers contains an ID, provided by you, that identifies the instance which is logging the message. This makes it possible to determine not just in which class your code is executing, but also on which object.

The questions to ask before thinking about using instance logging are:

If the answer to any of these is 'no', you probably don't need instance logging, either because you'll be able to distinguish which instance is logging using some other information (e.g. when or in which thread the logging occurred) or because you just won't care which instance is logging. Of course, if you think there's a chance it might be useful info, it's sometimes better to be safe than sorry - you can't magically switch to instance logging at runtime.

Just in case you're worried about creating a logger for each instance of a class, rest assured that Simple Log keeps a list of instance loggers as WeakReferences, meaning that as soon as the object using the logger is garbage collected, the instance logger should be as well.

How to log objects

Logging objects is really easy and really useful. It's demonstrated in the Quick Start, but just in case you missed it, you can do it like this:

Object someObject = ...;
int aCountOfSomething = ...;
boolean isPixelBlack = ...;

log.debugObject("someObject", someObject);
log.verboseObject("aCountOfSomething", aCountOfSomething);
log.ludicrousObject("isPixelBlack", isPixelBlack);

Or, if you need to log something other than an Object, int or boolean, or at a level other than Debug, Verbose or Ludicrous, you can use the general-purpose dbo() methods:

long objectId = ...;
log.dbo(DebugLevel.L4_INFO, "objectId", objectId);

Note that you should avoid doing this (seeing as you don't need to):

log.debug("objectId: " + objectId);
and there's no need to do this,
if (log.wouldLog(DebugLevel.L5_DEBUG))
   log.debugObject("objectId", objectId);
because Simple Log does the level check internally before string-ifying the value.

When to use wouldLog()

So, when would you use the wouldLog() method?

The rule for when to use wouldLog() is exactly the same as that for using Log4J's isDebugEnabled() method, it's just that Simple Log's dbo()-derived methods relegate most of the use cases where this has traditionally been necessary.

The rule is: If work has to be done to produce the value that will be passed into Simple Log, you should test with wouldLog() first.

So, for example, if you are planning on transforming an XML Document into a string and logging that, you'd want to check that it will be logged before you do the transformation. However, if you have a representation of that document which has a toString() method that will produce the output you want, there's no need to do a check the level before logging, even if the toString() method does a lot of work, because Simple Log will check the level internally before invoking the toString() method.

Log Bridge

If you're writing a software framework or a component which you intend to be used in many applications then some people who you intend to use your software might consider it rude if you force them to use Simple Log by using it in your software.

The solution to this problem is to use a bridge, a library that presents a simple interface for logging that can be implemented using many different logging packages. I have written such a library, and called it Log Bridge. It was written specifically with the purpose of supporting the majority of Simple Log's features, but also comes with implementations that bridge to Log4J, the JDK Logging introduced in J2SE 1.4 (java.util.logging) and a couple of other logging packages.

Simple Log User Guide