Logging and Debugging

Server side logging and debugging is implemented through Java classes located in the package ifs.fnd.log. The classes are included in the library file ifs-fnd-common.jar. This file doesn’t have any external dependencies and is included in all the Enterprise Application (ear) files created by the build process, but it can be necessary to include this file to the class path for compilation of Java classes.

Read more about the concepts and configuration in the Foundation1 Administration Guide – Administrate System Services – Server Logging.

Contents

Concepts

The base concept of the logging framework is Logger. A Logger is an instance that is used for writing output using one of the predefined methods. For a complete list of available methods see the JavaDoc Reference documentation for class ifs.fnd.log.Logger.

A set of static methods for obtaining different Loggers is available through the class ifs.fnd.log.LogMgr (see the JavaDoc Reference for complete list of available methods).
A set of Loggers, one for each category, is created per thread basis and encapsulated in an instance of ThreadLoggers class. A Logger defines also a set of boolean flags corresponding to the defined level.

A Logger instance is immutable, thus level of an existing Logger instance can not be changed, which can lead to a problem only if global or thread levels have been changed while a particular Logger instance is still in use. In such situation it can be necessary to obtain a new Logger instance.
Normally this is not a problem, but in some cases, for example in stand alone servers, in infinite loops, one should consider obtaining new Logger instances from time to time instead of using a locally stored one during the whole time.
On the other hand obtaining of Logger instances is an operation that takes some time, so if possible it is recommended to store once fetched Logger instance locally and reuse it as long as possible.
The framework will discover automatically if prerequisites for a particular Logger have changed and only create new instances if necessary, so obtaining a Logger instance through one of the static methods in LogMgr will not necessary create a new instance each time.
Never store Loggers in static variables!
But you can store a Logger in a member (instance) variable if you know that your class is short-lived, i.e. is created per request basis and destroyed as soon as the request has been processed. If you do not deal with threads and infinite, or at least long-lived loops, it is often a good idea to store a Logger instance in a local variable in a method, if storing as member variable in a class is not possible.

How to use Loggers

First in your code obtain a Logger by calling one of the get methods in ifs.fnd.log.LogMgr, e.g.:

Logger log = LogMgr.getApplicationLogger();

There is one get method for each Logger category.
Then you can use your log instance to call different logging methods on, depending on logging level and number of arguments.

There are five groups of methods: error(), warning(), info(), trace() and debug(), one group for each level. Within each group there are a number of overloaded methods taking an instance of Throwable and/or message with up to 9 parameters. If using syntax with parameters, parameter placeholders are denoted by sequences ‘&1’ to ‘&9’ within the message string. You can send an arbitrary object or type as a parameter and it can be null.

With exception of error() methods, calls to all other methods have to be preceded by checking a corresponding boolean flag, i.e.:

log.error(...);
if(log.warning)  log.warning(...);
if(log.info)     log.info(...);
if(log.trace)    log.trace(...);
if(log.debug)    log.debug(...);

This construction is used to avoid evaluation of possible expressions send to function call in case the respective level is disabled.
Because error() methods are always enabled, there is no corresponding flag for them. Therefore the error() methods should be used with caution.

Typically application code will use only one Logger instance, but if necessary, it is possible to obtain some of them and store in different variables.

Dealing with Levels

Normally a developer doesn’t need to think about setting levels for different Loggers. Levels are set globally through the configuration. But when developing any kind of framework it can be sometimes necessary to deal with levels. It can be necessary to set/reset global and/or per thread defined levels depending on other configuration than the default one and/or as a response to per request basis level definitions.

For setting of levels there are a number of set methods in ifs.fnd.log.LogMgr class. Methods of type setDefault are for levels applicable for the entire Java VM, i.e. have the same function as definitions in the configuration files, while the remaining set methods are per thread basis.

If it is necessary to set thread levels for more than one Logger category at the same time, the better way of doing that, instead of calling a number of set methods from LogMgr, is to call LogMrg.getThreadLoggers() to obtain a container instance and then call corresponding set methods from the class ifs.fnd.log.ThreadLoggers.

Similarly, if obtaining several Loggers, the best method is to first fetch a ThreadLoggers container instance and then obtain Loggers from this container by calling corresponding get method. This is because each get or set method from LogMgr class has to fetch the ThreadLoggers instance anyway before obtaining a proper Logger, which takes some CPU time.

If it is necessary to manipulate "per thread" levels, typically at the start of a request, it can be necessary to reset level values to their initial values when the request is done. This can be especially important when executing within an Application Server (or any other framework) that implements any kind of thread pool where threads can be reused during subsequent calls.
To reset thread levels to their initial values simply call LogMgr.reset() method.

Tip: Sometimes you may want to temporarily enable debugging of the code you’re about to develop without affecting the entire system. You may want to add some hard coded statements in your code for doing this, which you will typically remove or comment once you’re done with your task.

A way of performing this using the new logging framework could be to temporarily change the logging level of the logger you’re using:

Logger log = LogMgr.getApplicationLogger();                            
...                                                                    
//the line below you can remove or comment once you’re done            
int currLevel = LogMgr.setApplicationLevel(LogMgr.DEBUG);              
...                                                                    
//here you can have your debug printouts as usual                      
// - you can left them in the code once done                           
if(log.debug) log.debug(...);                                          
...                                                                    
//once finished you have to set back the initial level                 
// – even this line you’ll remove or comment once your task is finished
LogMgr.setApplicationLevel(currLevel);                                 


A better way to achieve this goal is to use class debugging described below.

Tags

Tag concept makes it possible to tag single messages with an identifier that can be used for filtering the output.

For this purpose there is a class ifs.fnd.log.Tags that can store one or more string identifiers.
Instances of Tags can be created and stored on any level – from local variables through member variables to static variables. Typically an instance of Tags will represent a single identifier, but there are situation where messages need to be tagged with a couple of tags – then an instance of Tags can represent a collection of single tags.

For tagging support all methods in ifs.fnd.log.Logger exist in versions taking Tags as argument.

Tag concept is widely used within the IFS Connect framework - logging output is often tagged with the current instance name, i.e.: name of reader, sender, queue or server.

Tag type

A tag can be typed or untyped. A tag type is a string identifier that can be used for grouping and filtering tags. Tag collection doesn't allow duplicated tag names, but two tags with the same name and different types are treated as different.

Class debugging

Sometimes, especially during development, there are needs for additional debug statements that can be very useful during the development cycle, but maybe not so useful later on, in the finished code. Statements that typically write a lot of output to the debug destination.

Usually developers put some additional debugging statements that are commented or removed from the code once the task is done. Statements that, of different reasons, are not necessary even for normal debugging purpose of the finished code later on.

But there can be situations where it would be useful to keep this type of debugging statements in the code anyway with possibility for enabling those if necessary, for example when surrounding a bug.

For similar purposes there is a concept of class debugging. Class debugging is a concept of additional Loggers created per class. Class loggers can be enabled or disabled independently of other Loggers. Class loggers are used for output of class specific debug information that is not interesting during normal debugging of a system flow.

You can obtain an instance of a class logger by calling LogMgr.getClassLogger(java.lang.Class) method. When class debugging is disabled in the configuration, the call to this function will return a dummy logger, common to the entire system, that does nothing, except for the error() method.

Class loggers can be used as ordinary loggers with all the available levels, but most probably a typical usage will involve only 'debug' or maybe also 'trace' levels. Typically, if you want to use the class logger concept, you will have two different loggers in your code: a “normal” logger for one of the ten predefined categories, for example ‘application’, and a second one – class logger for your particular class.

Note that class loggers are referred by names, so this is important to send right instance of java.lang.Class to the method in LogMgr, e.g. if you want to debug all the subclasses using the same class logger – use the supper class when obtaining the logger.

Filters

The logging output can be filtered in several different ways: by level, category, but also by assigning specially designed filters to Handlers.

The framework is delivered with three filter implementations: ifs.fnd.log.TagFilter, ifs.fnd.log.TagTypeFilter and ifs.fnd.log.ClassFilter, but if required, new filters can be developed.

To develop a new filter simply extend your new filter class from ifs.fnd.log.IfsFilter. The only method that must be implemented is:

boolean isRecLoggable(java.util.logging.LogRecord).

This method returns true if the incoming LogRecord should be passed through the filter, false otherwise.

If your new class is expecting some arguments, you probably have to implement also method init(), where you can read the passed arguments by calling int getArgumentCount() and String getArgument(int index). The init() method is called only once, when the filter instance is created, while isRecLoggable() is called for each logging event.

Debugging stack trace

Sometimes you need to write out the current stack trace. For this purpose the Logger class offers a method with name debugStackTrace(). Especially when debugging from within code executed within an application server, the stack trace can be quite huge - not seldom several pages with information that is, for most of cases quite uninteresting. To limit the amount of output this method only prints out lines from IFS classes. But if it is necessary to print the entire stack, it is possible to call the overloaded version of the method taking a boolean as argument telling if only IFS lines should be printed ('true') or everything ('false').

The stack trace printed using this method is formatted in slightly different way as formatted by standard Java to avoid possible confusions with thrown exceptions.

Forwarding Loggers

If of any reason you need to forward everything that is written to a particular Logger instance within the current thread to another Logger, there is a mechanism for that. Calling forward(Logger to) on a Logger instance will cause everything send to this instance within the current thread to be forwarded to the instance send as method argument - not only from your current code, but everything from all code executed within the same thread after the forward() method has been called. Forwarding can be stopped by calling method stopForwarding(). This is also possible to check if the current Logger is already forwarded or not by calling boolean isForwarded().

Warning:
This functionality has to be used with caution because threads within an application server are reused - you must be absolutely sure that the forwarding is stopped before you leave your code within the current request. Especially that the mechanism allows nested forwarding!

More information

You can find more information in the JavaDoc Reference documentation for the Java package ifs.fnd.log.