As you probably know using System.out.println for logging in Java is a no-no and this is also the case when developing plugins for Notes 8 and Lotus Expeditor. This post outlines how to use the logging framework supplied with Lotus Expeditor, how to manage the logging and where the logs go.
You have probably been using log4j or similar logging framework for years. Avoiding writing to System.out is even more important when writing SWT components and plug-ins for Notes 8 a.k.a. Eclipse since the output goes to a log tucked away under the covers. Luckily there’s an approach which is so much better and more flexible.
Logging basics
Luckily logging has been built into the language (since Java 1.4) and into the Lotus Expeditor platform. Actually there is 5 different frameworks built into the Lotus Expeditor platform. Based on advise from the Lotus and personal experience I really recommend you use the java.util.logging framework built into the core Java language. This is also known as JSR47 loggers. The below graphics shows the logging sub-system of Lotus Expeditor.
Page 8 from the
IBM Lotus Expeditor 6.1 Client for Desktop Serviceability – Logging and IBM Support Assistant (PDF).
As you can see from the above you can use the following logging frameworks in Lotus Expeditor:
- Eclipse Logging
- OSGi Logging
- IBM Commons Logging
- Apache Jakarta Commons Logging
- java.util.logging a.k.a. JSR47
I haven’t got experience with all of the these frameworks and luckily I don’t have to. I just have to know about java.util.logging and there’s an abundance of info on that on Google. The Sun tutorial might be the best place to start if you’ve never used java.util.logging. All this being equal the frameworks work the same with the only difference being how you obtain loggers and what the different logging levels are called. Log levels are called something different in the different frameworks but slide 7 in the Lotus Expeditor documentation on logging shows the mapping between the different frameworks.
Using loggers in code
Using java.util.logging in code is really easy and consists of 3 steps:
- Obtain the logger to use (normally in a static final variable).
- Write info to the logger.
The below is an example of using loggers.
package com.example.logging; import java.util.logging.Logger; public class MyLoggingClass { // logger private final Logger log = Logger.getLogger( this.getClass().getPackage().getName()); public MyLoggingClass() { log.finest("Finest..."); log.finer("Finer..."); log.fine("Fine..."); log.info("Info..."); log.warning("Warning..."); log.severe("Severe..."); } }
The above code creates a logger in a variable called “log” as a private variable. The logger itself is called com.example.logging which is the name used to control the log level (see below). I find it best to name the logger after the package name that contains the class. Sometimes it is however beneficial to also have a logger with more specialized names to log info on specific events.
Configuring the logger log levels
The log level for a single logger or multiple loggers can be controlled using the LogManager using the <Notes data directory>/workspace/.config/rcpinstall.properties file. To change the log level for the com.example.logging logger to FINEST add the following line to the file and restart the client:
com.example.logging.level=FINEST
To change the level dynamically use the OSGi console commands as described below.
Interacting with loggers from the OSGi console
If you start the Notes 8 client with the OSGi console visible you can interact directly with the loggers using the console command called setlogrlev. To set the log level for the com.example.logging logger to FINEST use the following command:
setlogrlev com.example.logging FINEST
Please note that the logger levels are case sensitive so you have to write “FINEST” and not “finest”.
Where the logging goes
All log messages are written to log files in the <Notes data directory>/workspace/logs directory. Opening the directory will reveal a number of files. The two most important are error-log-X.xml and trace-log-X.xml. The file names are quite explanatory and are, as the extension implies, XML-files. When you open them they’ll be rendered using a XSLT stylesheet to be displayed nicely. You can either read them using a browser or using the “Help/Support/View Log” and “Help/Support/View Trace” menu item in the Notes 8 client.
While the log files are usable I personally find it much easier and better to use the OSGi console when developing as I can adjust the log levels on the fly.
Further reading
- IBM Lotus Expeditor 6.1 Client for Desktop Serviceability – Logging and IBM Support Assistant (PDF)
- com.ibm.rcp.samples.log samples plug-in
- com.ibm.rcp.samples.logreader samples plug-in
Hi Mikkel, nice post! I just have a few comments.
(1) In the using loggers in your code section you started with "1. Make your plug-in depend on the com.ibm.rcp.core.logger plugin." This should not be required since all of the APIs you are promoting are from java.util.logging which you get for free in OSGi.
(2) In the section on changing the logger levels in the console you had a typo, it is setlogrlev not setloglev.
(3) You listed the convenience methods for logging logger.fine() logger.warning(), etc. In general I don’t recommend their use since the log formatters in Lotus Expeditor/Notes attempt to provide you with the class and method that the log event was generated in and with the convenience methods this requires it to generate a stackdump from an exception to try to figure it out – which is a real performance killer. I would recommend the exclusive use of the logp() signatures and encourage developers to always provide the source class and method parameters.
Thanks again for the great information!
LikeLike
Mike thanks for the comment. I corrected the post – I don’t know I stated the dependency on the logger plugin – might be because I was using some OSGi code in the class I grabbed the example from. I also corrected the setlogrlev command.
As for using logp() I didn’t think about that – I’ll have to look into it.
Thanks.
LikeLike
More info on the logp() methods can be found here.
LikeLike
Another good practice is to use the isLoggable() method to test whether something is loggable. I use that method for log entries that do lots of string concatenation – this is a big performance hit for classes that get called frequently and append a lot of strings. For example:
if (logger.isLoggable(Level.FINE)){
logger.fine("This is a sample object: " + object.toString() + ", " + nStr);
}
Logger.entry() and exit() are also very useful for debugging.
LikeLike