Simple Log User Guide

Simple Log Comparison with Log4J

You have a choice

Simple Log was written out of frustration with other "logging frameworks". Many of its features are designed to contrast with the way in which these frameworks are used. Because of this, it seemed sensible to show a comparison between Simple Log and one of these frameworks so that you can see the difference before committing to using Simple Log in your own code. I have chosen Log4J for the comparison, as this is undoubtedly the most widely used logging framework.

Not all of the comparisons will show Simple Log having an advantage. Some of them are just there to say "Simple Log can do the same" or "here's how you'd do that in Simple Log". I intend this to be a fair comparison, so if you see something that's not fair or is wrong, please let me know.

Don't Make Me Think!

Don't Make Me Think !

One of the driving principles of Simple Log is "Don't Make Me Think!". You should be able to use all but the most advanced features of your logger without having to think about what you're doing.

In the examples below, if I consider the work necessary to use a feature in one of the logging packages requires more thinking than should be necessary, I have flagged it using the "Don't Make Me Think" banner shown on the right. There aren't many of these, but I think they flag some of the most important differences.

Creating a Logger

Simple Log

private static final SimpleLogger log = new SimpleLogger(Test.class);

Log4J

private static final Logger log = Logger.getLogger(Test.class);

Logger Output With No Configuration

Simple Log

Given no configuration file, Simple Log produces no output. This is a feature that allows you to easily have zero output in deployment if desired.

Log4J

log4j:WARN No appenders could be found for logger (test.Test). log4j:WARN Please initialize the log4j system properly.

Most Basic Configuration

Simple Log

Given an empty configuration file, Simple Log writes all messages logged at the Fatal, Error, Warn and Info levels to System.err.

Log4J

With Log4J, the minimum configuration required to produce output (ignoring the BasicConfigurator) is:

log4j.rootLogger=INFO,Console log4j.appender.Console=org.apache.log4j.ConsoleAppender log4j.appender.Console.layout=org.apache.log4j.SimpleLayout

Don't Make Me Think !

However, even that only gets you the most basic of output. To generate the same format of output that Simple Log gives by default, you'd need to switch to a PatternLayout and then author something like this as well:

log4j.appender.Console.layout.ConversionPattern=%d{EEE yyyy/MM/dd HH:mm:ss.SSS}| |%t|%c|%m%n

Default Output

Simple Log

The default output of a Simple Log text log message is shown below.

Thu 2006/02/09 08:38:11.269| |main|Test|Plain message

The output shows:

Each field of the output is separated by a pipe, making it easy to parse the line (if necessary) using a simple string parsing method like split().

All this information was produced with only an empty properties file and one line of code:

log.info("Plain message");

Log4J

Log4J has no default output, except error messages saying that it hasn't been configured.

The easiest way to get output from Log4J is to install a BasicConfigurator, which gives the following output:

0 [main] INFO test.Test - Plain message

The output shows:

Log4J's default output uses three different types of separators; there are spaces between most fields, brackets around the thread name and an extra hyphen and space before the log message. This output would require a regular expression or some custom code in order to be parsed into its components.

To get this information, I needed to configure Log4J from within my code:

BasicConfigurator.configure(); log.info("Plain message");

Type of Output

Simple Log

Simple Log maintains a concept of having different types of log messages. There are plain text messages, object values, exceptions and tracing (entry and exit). Each of these five message types has its own format, making it simple to have different message types being easily distinguished from each other in your log file.

Shown below is an example of one of each type of message: an entry, a plain message, an object value, an exception and an exit.

Thu 2006/02/09 08:38:11.269|>>>|main|Test|main() Thu 2006/02/09 08:38:11.269| |main|Test|Plain message Thu 2006/02/09 08:38:11.269|---|main|Test|object|java.lang.Object@bf2d5e Thu 2006/02/09 08:38:11.269|***|main|Test|java.lang.Exception: Test Exception java.lang.Exception: Test Exception at test.Test.main(Test.java:52) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:324) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:90) Thu 2006/02/09 08:38:11.279|<<<|main|Test|main()

Below is the code used to produce the above output.

log.entry("main()"); log.info("Plain message"); log.infoObject("object", new Object()); log.errorException(new Exception("Test Exception")); log.exit("main()");

Log4J

Log4J has no concept of different log message types. All messages, regardless of whether they are text, an object, an exception or tracing, are seen as just a message, and get rendered using a single Layout (per Appender) with a single pattern.

The output produced by Simple Log could probably be achieved with Log4J by having five Loggers for every category (i.e. five Loggers per class), one for each type of message, though this would undoubtedly not be worth the effort.

Logging a Message

Simple Log

log.info("Message");

Log4J

log.info("Message");

Logging an Object

Simple Log

Simple Log provides methods for debugging objects that will check the log level before performing any concatenation. As long as the object or value you are passing is not produced using concatenation, this means you don't need to check the level yourself before logging your object.

Note that, while the below example shows the use of the method debugObject(), there are corresponding convenience methods for all DebugLevels at which you might normally debug an object, e.g. infoObject(), as well as the generic dbo() methods that take any level.

log.debugObject("new Object()", new Object());

Log4J

Log4J doesn't provide any methods specifically designed for debugging objects. Most people use something like the following code.

if (log.isDebugEnabled()) log.debug("new Object(): " + new Object());

Don't Make Me Think !

Note that, because you have to perform a concatenation in order to log both the object's name and value, best practice is to check whether the message will actually be logged before performing the concatenation.

I should note that it is actually possible to do something like what Simple Log does with objects by using Log4J's l7dlog() method:

log.l7dlog(Level.DEBUG, "newObject", new Object[] {new Object()}, null);

However, to make this happen you have to do two other undesirable things:

Because of the latter point, you probably would still want to do the pre-logging level-check anyway, which totally defeats the point of trying to use this method.

Testing the Log Level Before Logging

Simple Log

In case you're wondering, it is possible to check whether a message will be logged before logging it using Simple Log.

if (log.wouldLog(DebugLevel.L5_DEBUG)) ...

And I can't think why you'd ever want to use this one, but it's there just in case:

if (log.isTracing()) ...

Log4J

if (log.isDebugEnabled()) ...

Logging a Primitive

Simple Log

Basically the same story as above; Simple Log lets you log any primitive just by passing it in. There are convenience methods for int and boolean at the most frequently-used levels (e.g. debugObject(String, int), verboseObject(String, boolean)). For other primitive types or for other levels, you can pass any DebugLevel and primitive to a dbo() method.

List kittens = ...; long id = ...; log.debugObject("kittens", kittens.size()); log.dbo(DebugLevel.L5_DEBUG, "id", id);

Log4J

Don't Make Me Think !

As with objects, Log4J requires you to concatenate primitives yourself, after the all-important level-check:

List kittens = ...; long id = ...; if (log.isDebugEnabled()) { log.debug("kittens: " + kittens.size()); log.debug("id: " + id); }

Logging an Array/Byte Array/Char Array

Simple Log

Simple Log applies special formatting to object arrays, byte arrays and char arrays to ensure you always get useful output without having to think about it.

log.debugObject("strings", new String[] {"One", "Two", "Three"}); log.debugObject("chars", new char[] {'c', 'h', 'a', 'r', 'a', 'c', 't', 'e', 'r', 's'}); byte[] bytes = new byte[10]; new Random().nextBytes(bytes); log.debugObject("bytes", bytes);

The above code produces the following output:

Fri 2006/02/10 07:51:41.250|---|main|Test|strings|[One, Two, Three] Fri 2006/02/10 07:51:41.250|---|main|Test|chars|characters Fri 2006/02/10 07:51:41.250|---|main|Test|bytes|0x[06, 9A, 2D, FC, BA, A0, F1, 63, B9, E0]

Log4J

Because Log4J has no special allowances for printing objects, you either get the hashcode of your array as the output, or you have to do the work yourself to get it's contents.

Printing the array normally will get you its hashcode:

if (log.isDebugEnabled()) log.debug("strings: " + new String[] {"One", "Two", "Three"});

Fri 2006/02/10 08:01:15.256| |main|test.Test|strings: [Ljava.lang.String;@df8ff1

Don't Make Me Think !

Or you can do the work yourself to get the same output for object arrays as Simple Log gives by default:

// If you can guarantee Java 5 you could do it like this: if (log.isDebugEnabled()) log.debug("strings: " + Arrays.toString(new String[] {"One", "Two", "Three"})); // Otherwise you have to do this: String[] strings = ...; if (log.isDebugEnabled()) { StringBuffer buffer = new StringBuffer("["); for (int i = 0; i < strings.length; i++) { if (i != 0) buffer.append(", "); buffer.append(strings[i]); } buffer.append("]"); log.debug("strings: " + buffer); }

As Arrays.toString() has no special formatting for bytes, to get the same hexadecimal output as Simple Log you would have to write your own code to produce it. For character arrays you can use String.valueOf().

Logging an Exception

Simple Log

Simple Log has specific methods for logging exceptions. There are convenience methods for the Fatal, Error and Warn levels, and the dbe() method for less-frequently used levels.

log.errorException(new Throwable("Pretty Bad Exception")); log.warnException(new Throwable("Recoverable Exception")); log.debug("Caught Expected Exception"); log.dbe(DebugLevel.L6_VERBOSE, new Throwable("Expected Exception"));

Log4J

Log4J also has specific method signatures for logging exceptions. With Log4J, you are encouraged to log your message at the same time as your exception, meaning that your exception will always be printed at the same level as your message.

log.debug("Caught Expected Exception", new Throwable("Expected Exception"));

Configuring Logging

Simple Log

In Simple Log, the level for a particular logger is set by creating a property with the name of the logger and a value of either of the desired DebugLevel's name or numeric value. The level names are case-insensitive.

org.grlea.application.Main = Error org.grlea.application.Processor = 5

As in most logging packages, the levels specified are inherited based on the package name hierarchy. So, for example, you can set the level for all classes in the org.glea.application package and its subpackages using the following:

org.grlea.application = Info

Loggers for which there is no level set - either for the class of the logger or for any of the packages in which the class resides - will have their level set to the default level, which is defined and configured by the property simplelog.defaultLevel.

simplelog.defaultLevel = Warn

Note that, while the levels for loggers in Simple Log are calculated using a hierarchical namespace, there is not actually any logger hierarchy maintained within the code.

Log4J

In Log4J, the level for a particular logger is set by creating a property that is the name of the logger prefixed with 'log4j.logger.'. The value must be the name of the desired Level. The level names are case-insensitive.

log4j.rootLogger=Info,Console log4j.logger.org.grlea.application.Main = Error log4j.logger.org.grlea.application.Processor = Debug

In my experience, people are using XML to configure Log4J more often than a properties file. The equivalent XML is of course more verbose than simple properties, and looks like this:

<category name="org.grlea.application.Main"> <priority value="Error" /> </category> <category name="org.grlea.application.Processor"> <priority value="Debug" /> </category> <root> <priority value="Info" /> <appender-ref ref="Console" /> </root>

Using this format will mean that, every time you need to specify a level for a class or package that isn't currently specified, you need to copy three or four lines (depending on whether you like whitespace) and then change information on two of those lines. The values you need to change are also not at the start or the end of any lines, but kind of towards the end of them. The amount of boilerplate text in this style of configuration also makes it much harder to scan with the naked eye, as the important data is obscured by repetitious tags.

I know I'm being picky and anal here, but in the past when I've been debugging code and needing to change the log levels frequently, I've experienced that reading, scanning and editing this file is really annoying. Being able to just type (or copy/paste) the FQ name of a class followed by a level is much easier.

As shown above, the default level and output semantics of Log4J are set by configuring the "Root Logger", an object which sits at the top of Log4J's internally-maintained hierarchy of Logger objects.

Available Levels

Simple Log

Simple Log provides seven distinct DebugLevels. The first five levels are common to many logging packages. The last two are intended to allow more fine-grained control of debugging output.

  1. Fatal
  2. Error
  3. Warn
  4. Info
  5. Debug
  6. Verbose
  7. Ludicrous

Debugging is what most developers are using logging packages for, so having three grades of debugging levels makes a lot of sense. Some packages, Log4J included, are distributed with only one debugging level, which means that debugging for any particular class must be either on or off. If you have a class that is doing a lot of work, e.g. an implementation of a SAX DefaultHandler, then it very useful to be able to turn on only the most important debug information first. Then, if more detail is required to solve the problem, you can get that detail by setting the level up to Verbose or Ludicrous. The alternative is to always receive reems of output and to waste time trawling through mounds of useless detail messages that may come in use one day but most of the time will just get in your way.

The Simple Log API Documentation is very specific about the intended use of each level. It gives examples of what kinds of messages and objects should be logged at each of the levels. This removes the need for team meetings on the topic "What should we be logging at each level".

Tracing, that is, printing information about the entering and exiting of methods, is configured indepently of debug levels.

Simple Log's DebugLevel class is not extendable. No one has ever complained about this. Seven appears to be the holy number.

Log4J

Log4J provides five Levels intended to be used for logging.

  1. Fatal
  2. Error
  3. Warn
  4. Info
  5. Debug

It also has two levels intended for use only in configuration.

  1. All
  2. Off

As discussed above, there are a number of disadvantages associated with only having one level for debug statements.

Unlike Simple Log's DebugLevel, Log4J's Level class can be extended. Many developers use this fact to rectify Log4J's lack of debugging granularity by creating their own level below Debug, very often called "Trace". I have seen this cause a number of other issues, including more repetetive text in the configuration file (and silent errors when this repetetive text is omitted) and difficulty in upgrading Log4J versions due to the need to have your own build of Log4J.

API Simplicity

Simple Log

The core package of Simple Log contains three classes, of which most people will only ever need to know about two - SimpleLogger and perhaps DebugLevel.

In total, Simple Log version 2 will be released with about 9 public classes, of which:

Log4J

The core package of Log4J contains 23 classes. Of these, you will need to use or at least understand the concept of the following before getting Log4J to do anything useful:

In total, the Log4J 1.2.9 API includes 123 public classes and interfaces.

Tracing

Simple Log

Simple Log has explicit support for tracing, which means logging messages when the thread of control enters and exits a method, or any arbitrary section you might be interested in. Tracing is performed using the entry() and exit() methods, demostrated below and followed by the default output.

log.entry("main()"); log.exit("main()");

Thu 2006/02/09 08:38:11.269|>>>|main|Test|main() Thu 2006/02/09 08:38:11.279|<<<|main|Test|main()

Log4J

As Log4J has no concept of different types of log messages, it has no concept of a tracing message. It doesn't have a level specifically for tracing, and as a result many people end up creating their own custom level just for this purpose.

People who want to do tracing in Log4J usually do it something like the following.

log.debug("IN: main()"); log.debug("OUT: main()");

Configuring Tracing

Simple Log

In Simple Log, tracing is configured independently of the debug levels. To enable or disable tracing for any class or package you create a property with the name of the logger followed by #trace (a hash followed by the word 'trace'), and with a value of either true or false.

org.grlea.application#trace = false org.grlea.application.Main#trace = true org.grlea.application.processing#trace = true org.grlea.application.processing.XmlHandler#trace = false

As with debug levels, tracing is inherited through the package name hierarchy. So, using the above example, tracing would be active for the Main class and for all classes in and under the processing package, except for the XmlHandler class.

The default for tracing is configured by setting the simplelog.defaultTrace property.

simplelog.defaultTrace = true

Log4J

Seeing as Log4J has no dedicated tracing capability, you can't configure your trace statements separately. The impact of this is that, if you have tracing-like debug logs in a class, you have to get all the debug output for that class to see the tracing.

As noted, some people create their own custom Level so that they can have tracing at a different level to their debug logs. While doing so will allow you to see your debug messages without seeing your tracing (assuming you make your Trace level lower than your Debug level), the opposite will not be true, so to see your tracing you will also have to see all your debug statements.

Logging to a File

Simple Log

To log to a file using Simple Log, simply specify the name of the file with the simplelog.logFile property.

simplelog.logFile = application.log

The log file name can be either absolute or relative. If relative, it will be interpreted as being relative to the working directory of the application.

Log4J

To log to a file using Log4J, you need to properties for:

In the simplest scenario, you'd have something like this:

log4j.rootLogger=info,File log4j.appender.File=org.apache.log4j.FileAppender log4j.appender.File.File=application.log log4j.appender.File.layout=org.apache.log4j.SimpleLayout

Appending to/Overwriting a File

Simple Log

By default, Simple Log will always append to its log file. To change this, set the simplelog.logFile.append property to false.

simplelog.logFile.append = false

Log4J

Log4J's FileAppender also appends to its file by default, but can be prevented from doing so by setting the 'append' property of the appender to false.

log4j.appender.File.append = false

Logging to a File and the Console

Simple Log

Simple Log can be made to log to both a file and to the console by setting the value of the simplelog.logFile.andConsole property.

simplelog.logFile = application.log simplelog.logFile.andConsole = true

Log4J

To log to both a file and the console using Log4J, you have to define two Appenders and add both of them to your Logger hierarchy.

log4j.rootLogger=info,File,Console log4j.appender.File=org.apache.log4j.FileAppender log4j.appender.File.File=application.log log4j.appender.File.layout=org.apache.log4j.SimpleLayout log4j.appender.Console=org.apache.log4j.ConsoleAppender log4j.appender.Console.layout=org.apache.log4j.SimpleLayout

Generating a Log File Name containing the Date

Simple Log

To place the date of a log file's creation in the file's name using Simple Log, you need to place {0} in the log file where you want the date to appear. If you want to specify the format of the date, simply use the standard formatting parameters specified by java.text.MessageFormat.

simplelog.logFile = application-{0,date,yyyy_MM_dd}.log

Log4J

So far as I can determine from the documentation, there is no way to include the date in the name of Log4J's main log file, but only in its rolled over logs.

Log File Rolling

Simple Log

To enable log rolling in Simple Log, you need to:

simplelog.logFile=application.log simplelog.rollover=timeOfDay

By default, rolled log files are placed in the same directory as the main log file, and are given the same name as the main log file, prefixed with a unique, incrementing number and a hyphen (e.g. 00-application.log). The directory for rolled log files can be configured by setting the simplelog.rollover.directory property. The name of rolled log files can be configured by setting the simplelog.rollover.filename, which can include the incrementing number and/or the date the rollover file is created.

Simple Log comes bundled with two strategies: File Size and Time of Day. By default, the File Size strategy rolls the log every 100 megabytes and the Time of Day strategy rolls at midnight based on the timezone of the local machine. The strategies can be configured using the properties simplelog.rollover.fileSize.size, simplelog.rollover.timeOfDay.time and simplelog.rollover.timeOfDay.timezone.

simplelog.logFile=application.log simplelog.rollover=timeOfDay simplelog.rollover.timeOfDay.time=02:00 simplelog.rollover.timeOfDay.timezone=GMT+10:00

You can also easily write your own implementation of RolloverStrategy which will have access to all the Simple Log properties.

Log4J

To enable log rolling in Log4J, you need to:

log4j.rootLogger=info,File log4j.appender.File=org.apache.log4j.RollingFileAppender log4j.appender.File.File=application.log log4j.appender.File.layout=org.apache.log4j.SimpleLayout

Rolled log files are placed in the same directory as the main log file. They are given the same name as the main log file, with an increasing number appended. The directory for rolled log files cannot be configured. The name of rolled log files cannot be configured.

Log4J's RollingFileAppender only maintains a specified number of rolled log files, defaulting to one. This can be configured by setting the log4j.appender.File.MaxBackupIndex property. By default, the Rolling File Appender rolls the log every 10 megabytes. This can be configured by setting the property MaxFileSize property of the appender.

log4j.rootLogger=info,File log4j.appender.File=org.apache.log4j.RollingFileAppender log4j.appender.File.File=application.log log4j.appender.File.layout=org.apache.log4j.SimpleLayout log4j.appender.File.MaxFileSize=100MB log4j.appender.File.MaxBackupIndex=9

Log4J also comes with a DailyRollingFileAppender, which uses a date pattern that enables rolling on a monthly, weekly, daily, half-daily, hourly, or minutely schedule.

log4j.rootLogger=info,File log4j.appender.File=org.apache.log4j.DailyRollingFileAppender log4j.appender.File.File=application.log log4j.appender.File.layout=org.apache.log4j.SimpleLayout log4j.appender.File.DatePattern='.'yyyy-MM-dd

Outputting the Source File, Method Name and Line Number

Simple Log

Simple Log has no mechanism for displaying the file, method or line number from which a message was logged. Note that, in order to do this, it is necessary to create a new Throwable every time a message is printed.

Log4J

Log4J's PatternLayout can output this information using the %F, %M and %L conversion characters.

log4j.appender.Console.layout=org.apache.log4j.PatternLayout log4j.appender.Console.layout.ConversionPattern=%d{EEE yyyy/MM/dd HH:mm:ss.SSS}| |%t|%c|%M|%F:%L|%m%n

Note that the Log4J documentation warns against using this feature.

WARNING Generating caller location information is extremely slow. It's use should be avoided unless execution speed is not an issue

Reloading Configuration

Simple Log

Simple Log can periodically check the modified date of the configuration file in use and reconfigure itself if the file changes. To enable this, you set the simplelog.reloading property to true.

simplelog.reloading = true

Log4J

The free Log4J documentation doesn't contain anything about reloading your log configuration at runtime.

The org.apache.log4j.varia package contains a class called ReloadingPropertyConfigurator, though the class is entirely undocumented, so I've no idea how it works or how you'd install it. There is no ReloadingDOMConfigurator.

Customising the Log Format

Simple Log

The format of Simple Log's output can be customised by setting the value of the simplelog.format.* properties. There are two properties for each type of log message (debug, debugObject, debugException, entry and exit) - one for each of the instance and non-instance log message types.

The syntax of the output properties are specified by java.text.MessageFormat.

The arguments passed into the MessageFormat are documented in the configuration file, which is what you'll already be looking at when you need them.

simplelog.format.debug = {0} [{1}] {4} {2} - {5}

Log4J

The format of Log4J's output is customised by specifying a PatternLayout on your Appender and specifying the ConversionPattern for this layout.

The syntax of the output is "closely related to the conversion pattern of the printf function in C" and is documented in the API documentation of the PatternLayout class.

The "conversion specifiers" available to the PatternLayout are also documented in the API documentation of the PatternLayout class.

log4j.appender.Console.layout=org.apache.log4j.PatternLayout log4j.appender.Console.layout.ConversionPattern=%d [%t] %p %c - %m%n

Outputting Fully-Qualified Class Names

Simple Log

With Simple Log, the decision to ouput the fully-qualified name of a class is specified, in code, on a per-class basis. (And defaults to 'false'.)

private static final SimpleLogger log = new SimpleLogger(Test.class, true);

It is implemented like this based on the assumption that most class names are relatively unique and that, should you ever need to see the fully-qualified name of a class, you will only want to see it for those classes whose non-qualified names are ambiguous.

Log4J

In Log4J, the decision about how much of the qualified class name to output is specified in the Conversion Pattern.

log4j.appender.Console.layout.ConversionPattern=%d{EEE yyyy/MM/dd HH:mm:ss.SSS}| |%t|%C|%m%n

This means that every message output by that Appender will show the same amount of detail about the class name. You can limit the information based on either a maximum character length or a number of package names. However, whenever you want to see information about the package of one class that's logging, you will have to output the same amount of information for all classes.

Instance-Based Logging

Simple Log

Simple Log supports a concept of "instance logging", where a SimpleLogger is created for each instance of a class and messages output from that instance include the (specified) ID of the object.

public class TreeNode { private final SimpleLogger log; public TreeNode(String name) { log = new SimpleLogger(TreeNode.class, name); } }

As well as printing the instance ID in the log, Simple Log allows you to configure the log level and trace flag based on the instance ID.

TreeNode = Debug TreeNode.frequentlyOccurringLeaf = Info

Log4J

While Log4J doesn't have the concept of instance logging, it is possible to achieve a similar functionality by concatenating the object's ID onto the class name.

public class TreeNode { private final Logger log; public TreeNode(String name) { log = Logger.getLogger(TreeNode.class.getName() + "." + name); } }

Log4J will then allow you to configure the level for these "instance" loggers in the same way as any other category.

log4j.logger.TreeNode = Debug log4j.logger.TreeNode.frequentlyOccurringLeaf = Info

The shortfall here with Log4J is that, because it doesn't know about the "instance ID", and just sees it as one more level in the logger name hierarchy, the unqualified "class name" of statements printed by this logger will be just the instance ID, without the class name. To see the class name as well, you would need to print more than one level of the logger name. As discussed above under 'Outputting Fully-Qualified Class Names', configuring your pattern layout like this in Log4J will mean that you will see the class logger name information for all your loggers, not just the ones with instance IDs.

Printing Fixed-Width Log Messages

Simple Log

Simple Log does not have any explicit capability for print its output in fixed-width fields. It is easy enough to define a fixed-width date format (as the default format is). The default output will has a fixed width up until the class name for all messages from the same thread, and up until the message contents for all messages from the same class in the same thread.

Thu 2006/02/09 08:38:11.269|>>>|main|Test|main() Thu 2006/02/09 08:38:11.269| |main|Test|About to print arguments Thu 2006/02/09 08:38:11.269|---|main|Test|argv|[test, arguments] Thu 2006/02/09 08:38:11.269|>>>|Event Dispatch Thread|ApplicationMainWindow|main() Thu 2006/02/09 08:38:11.269|<<<|Event Dispatch Thread|ApplicationMainWindow|main() Thu 2006/02/09 08:38:11.269|<<<|main|Test|main()

Log4J

Log4J's PatternLayout allows you to specify fixed-width conversion specifiers for each field, allowing you to define a precisely fixed-width log mesage, provided you are happy to lose a bit of detail if the value being printed is larger than the specified widt of the field.

log4j.appender.Console.layout.ConversionPattern=%d{EEE yyyy/MM/dd HH:mm:ss.SSS}|%-20.20t|%-20.20C|%m%n

Thu 2006/02/09 08:38:11.269|main |Test |IN: main() Thu 2006/02/09 08:38:11.269|main |Test |About to print arguments Thu 2006/02/09 08:38:11.269|main |Test |argv: [test, arguments] Thu 2006/02/09 08:38:11.269|Event Dispatch Threa|ApplicationMainWindo|IN: ApplicationMainWindow() Thu 2006/02/09 08:38:11.269|Event Dispatch Threa|ApplicationMainWindo|OUT: ApplicationMainWindow() Thu 2006/02/09 08:38:11.269|main |Test |OUT: main()

Amount of Documentation in Provided Configuration Files

Simple Log

The Simple Log release includes properties files containing all the properties you will ever need to configure Simple Log, except, of course, those or configuring the levels for your classes. The properties files also contain full documentation of each of the properties.

Log4J

The Log4J release includes a number of example properties files that show how to set up Log4J for a number of different common logging requirements. It does not include a "complete" properties file or set of files that give all the properties that can be used for configuration. The documentation for many of the configuration options for Log4J are contained within the API documentation.

Computation to Output a Log Message

Simple Log

When you log a message using Simple Log (e.g. to SimpleLogger.debug(String)), and that message is going to be output, the following actions take place:

  1. the convenience method SimpleLogger.debug(String) passes control to the generic method SimpleLogger.db(DebugLevel, String)
  2. there is a check to see if the SimpleLog to which SimpleLogger belongs is outputting anything at all
  3. the level of the message is checked against the level of the SimpleLogger (integer comparison).
  4. an array of the data to be output is created, which includes creating a new Date object and retrieving the current Thread name.
  5. the appropriate MessageFormat object is located (a simple if/else) and the message output is created by passing the object array into this format object
  6. the message output is given to the SimpleLog instance which immediately prints it to the main output stream and, if necessary, to System.err.

Log4J

When you log a message using Log4J (e.g. to Category.debug(String)), and that message is going to be output, the following actions take place:

  1. there is a check to see if the LoggerRepository from which the Logger was created is disabled for the level of the message
  2. the "effective level" of the Logger is calculated, which involves walking up the category hierarchy until a category is found which has had its Level explicitly set
  3. the "effective level" of the Logger is checked against the DEBUG level
  4. the convenience method Category.debug(String) passes control to the generic method Category.db(String, Priority, Object, Throwable)
  5. a LoggingEvent object is created, which includes aqcuiring the system time.
  6. callAppenders() navigates up the Category hierarchy, and at each level:
  7. synchronises on the Category
  8. determines whether the Category has a Vector of appenders and, if it does, gets the size of the Vector
  9. retieves each appender from the Vector and passes the event to it using Appender.doAppend(LoggingEvent)
  10. checks the LoggingEvent's level against the Appender's threshold
  11. loops through the Appender's chain of Filters, checking that no Filter filters out the event
  12. passes the event to the append() method, which, for a ConsoleAppender or FileAppender:
  13. checks the entry conditions, which means checking the Appender isn't closed, that it has a Writer and that it has a Layout
  14. passes the event to the subAppend() method, which
  15. formats the event using the Appender's Layout and
  16. prints it to the Appender's output stream.

Simple Log User Guide