Feb 14, 2009

Formatting Your Log in Microlog

When adding logging to your application, it is important that you log the right information. Otherwise you have no use of your log when examining it afterwards. In Microlog you have the possibility to format the logging output. The formatting is not formatting the log message, but rather you add information like the time when the logging was done.

The formatting in Microlog is done using a an instance of a Formatter. There are three formatters available out-of-the-box when downloading Microlog:
  • SimpleFormatter
  • ConfigurableFormatter
  • PatternFormatter
The SimpleFormatter is the default formatter for Microlog. Is is the smallest and fastest formatter. It prints the time, the level, the message and the Throwable object if there is one available. This is always a good starting point when using Microlog for the first time. You could set the formatter in code like this:
ConsoleAppender appender = new ConsoleAppender();
SimpleFormatter formatter = new SimpleFormatter();
appender.setFormatter(formatter);

The final string looks like this:

494:[INFO]-Starting app



The ConfigurableFormatter gives you a little bit more control of the formatting. The order is pre-defined, but you get to choose what to log. You get to choose if you want to log the level, message and the name of the logger. You could set the time format and specify which delimiter that is used between each printed field. If you choose to print the time as date it looks like this:

2009-02-10 19:38:00.188:[INFO]:Starting app

The available properties are:
  • PrintName - Set to true if you wish to print the name of the Logger instance.
  • PrintLevel - Set to true if you wish to print the current Level.
  • TimeFormat - Specifies which time format to use. The possible values are: NO_TIME, DATE_TO_STRING and TIME_IN_MILLIS. The DATE_TO_STRING uses the toString() of the current Date object (now). The TIME_IN_MILLIS writes the time in milliseconds.
  • PrintMessage - Set to true if you wish to print the logged message.
  • Delimiter - Set the delimiter to separate each printed part. Use a String of your choice.

The PatternFormatter is the Formatter with the most opportunities. It works by defining your own formatting pattern. The pattern could be set in your code or by a property in a properties file. For example it could look like this in your property file:

microlog.formatter=net.sf.microlog.format.PatternFormatter
microlog.formatter.PatternFormatter.pattern=%d [%P] %m %T

The formatted string look like this:
15:38:49,199 [INFO] Starting app

The available formatting options are:

  • %c - prints the name of the Logger
  • %d - prints the date (absolute time)
  • %m - prints the logged message
  • %P -prints the priority, i.e. Level of the message.
  • %r - prints the relative time of the logging. (The first logging is done at time 0.)
  • %t - prints the thread name.
  • %T - prints the Throwable object.
  • %% - prints the '%' sign.

If you are not satisfied with the supplied Formatter implementations, you could create your own Formatter. This is done by creating a class that implements the Formatter interface. The interface looks like this:
public interface Formatter {

/**
* Format the given message and the Throwable object.
*
* @param name
* the name of the logger.
* @param time
* the time since the first logging has done (in milliseconds).
* @param level
* the logging level
* @param message
* the message
* @param t
* the exception.
* @return a String that is not null.
*/
String format(String name, long time, Level level, Object message,
Throwable t);

/**
* Configure the appender.
*
* @param properties
* Properties to configure with
*/
void configure(PropertiesGetter properties);
}

The format() method is where the actual formatting is done. It creates a String object that is the actual String that is logged. Nothing more, nothing less. The configure() method is used for configuration of the Formatter. The PropertiesGetter object is used for fetching the properties that the Formatter is interested in. Thus you could define your own properties that you use for your Formatter implementation.

I hope that you have learned how to master the art of formatting in Microlog by reading this. If you still have any questions about this, please use the official Microlog forums to find your answers.

14 comments:

Thiago Bruno Melo de Sales said...

Hello,
I got the microlog source code and I think I have found an error. I was trying to use the Properties class, and it was not loading my desired formatter class. I have a microlog.properties file such as the following bellow:

microlog.level=WARN
microlog.appender=net.sf.microlog.appender.ConsoleAppender
microlog.formatter=net.sf.microlog.format.ConfigurableFormatter
microlog.formatter.ConfigurableFormatter.name=MyFormatterName
microlog.formatter.ConfigurableFormatter.level=INFO
microlog.formatter.ConfigurableFormatter.delimiteR=<>

The initProperties (Properties.java) method first initializes some default values (configuring the SimpleFormatter, for instance) and after that, it loads the configuration file with "fileProperties = new PropertyFile(propertyFileName);". However, instead of replacing the default values to the new ones inserted in the configuration file, it creates new values in the hashtable, because, instead of writing "microlog.*", it writes "icrolog.*", without the "m" letter. Therefore, we have two keys (in the hashtable), instead of one. For instance:
icrolog.appender
microlog.appender

Thiago Bruno Melo de Sales said...

Hello,
I got the microlog source code and I think I have found an error. I was trying to use the Properties class, and it was not loading my desired formatter class. I have a microlog.properties file such as the following bellow:

microlog.level=WARN
microlog.appender=net.sf.microlog.appender.ConsoleAppender
microlog.formatter=net.sf.microlog.format.ConfigurableFormatter
microlog.formatter.ConfigurableFormatter.name=MyFormatterName
microlog.formatter.ConfigurableFormatter.level=INFO
microlog.formatter.ConfigurableFormatter.delimiteR=<>

The initProperties (Properties.java) method first initializes some default values (configuring the SimpleFormatter, for instance) and after that, it loads the configuration file with "fileProperties = new PropertyFile(propertyFileName);". However, instead of replacing the default values to the new ones inserted in the configuration file, it creates new values in the hashtable, because, instead of writing "microlog.*", it writes "icrolog.*", without the "m" letter. Therefore, we have two keys (in the hashtable), instead of one. For instance:
icrolog.appender
microlog.appender

My Open Source Software Development Blog said...

Hi,

thanks for your feedback. Are you using the latest snapshot build?

I will take a look at it and fix it ASAP.

Regards
Johan

My Open Source Software Development Blog said...

Hi,

just one small comment; the ConfigurableFormatter is removed in the latest V2.0 SNAPSHOT, since the PatternFormatter class does exactly the same work and is faster.

Regards
Johan

My Open Source Software Development Blog said...

Hi,

I have tried the latest snapshot build and it seems to be working. I need to know which version you used, in order to fix the problem.

Regards
Johan

Thiago Bruno Melo de Sales said...

Hello,
I'm so sorry. I had an older version. Now, everything is working with this new SNAPSHOT

My Open Source Software Development Blog said...

Glad it worked out!

If there are any other problems, please do not hesitate to ask a question again.

Regards
Johan

Thiago Bruno Melo de Sales said...

Hello Johan,
Thanks. One more doubt. The Formatter interface defines the format method. What does the first parameter (clientID) mean? Why was it created?
Thanks once again!

My Open Source Software Development Blog said...

Hi,

the client id is used to set an unique id for your client. This should be used when you use several phones that logs to the same server. This is not fully implemented on the server side, but will be before the real 2.0.0 release.

Regards
Johan

Anonymous said...

Tried to run it in BlackBerry JDE 4.5 using a bunch of Microlog jars as Library .. did not compile ..
Is there anyway to use this framework fro BBs?

C:\Program Files\Research In Motion\BlackBerry JDE 4.5.0\bin\rapc.exe -quiet import="..\..\..\Research In Motion\BlackBerry JDE 4.5.0\lib\net_rim_api.jar" library=MICROLOGLib MICROLOGLib.rapc warnkey=0x52424200;0x52525400;0x52435200 "C:\Program Files\Oracle Jdeveloper10g\jdev\Microlog\microlog-logger-core-2.2.5.jar" "C:\Program Files\Oracle Jdeveloper10g\jdev\Microlog\microlog-logger-midp-2.2.5.jar" "C:\Program Files\Oracle Jdeveloper10g\jdev\Microlog\microlog-logger-midp-file-2.2.5.jar" "C:\Program Files\Oracle Jdeveloper10g\jdev\Microlog\microlog-logger-midp-wma-2.2.5.jar" "C:\Program Files\Oracle Jdeveloper10g\jdev\Microlog\microlog-server-socket-2.2.5.jar"
net.sf.microlog.core.CyclicBuffer: Error!: Missing stack map at label: 28
Error while building project

maratm (at) r o g e r s , com

My Open Source Software Development Blog said...

Hi,

this is some kind of compiler problem. Since I do not have any BlackBerry it is hard for me to test this. Maybe I could install the BB SDK?

Anyway, you could use the Microlog source code directly. Please be aware that some classes can/should be removed.

Please report back if this worked for you.

Regards
Johan

Matt Sidesinger said...

I just wanted to update everyone that Blackberry developers are still getting the following error: net.sf.microlog.core.CyclicBuffer: Error!: Missing stack map at label: 28 even with the latest version.

My Open Source Software Development Blog said...

Hi,

the error indicates that bytecode is corrupt. Could anyone please indicate how I should compile and build the jars to avoid this? BTW which version did you use?

Until then I recommend to use the source code version of Microlog.

My Open Source Software Development Blog said...

Sorry I read through your post very fast. I noticed the version information afterward. However my initial question is still valid.