Logger framework
Introduction
The aim of this package was not to implement a new logging mechanism. The idea of this
framework was more to have a simple logging framework which enables to replace the main
logging mechanism after developing a software.
This happens as example when you develope a third party library. In this case you can use
the jptools logging mechanism. After developing the library it will used (integrate) in a
project. Probably the project has different requirements to the logger mechnism than you
defined in your library.
Currently a lot of libraries are using only a simple OutputStream to write
the log. The OutputStream can be set. This is a simple solution but the user
of such a library has no change to filter some output of the log output or can change the
output format.
The jptools logger is a fast and simple logging mechanism. The logger has no special
overhead or a big footprint. The implementation is straight foreward but contains the
main functionality of a logger:
The logger supports the following logging levels (which are defined in te
Levelclass):INFO
DEBUG
WARN
ERROR
FATAL
PROFILE
- The logger can be configured with an instance of the class
LogConfig. ThegetLoggerandsetLoggerare methods of the classLoggerto enables the access to the configuration. - The format of the logger output is defined by the used layout class (
Layout). - The writer class (
LogWriter) implements the concrete logger. - Some bindings to other logger frameworks are also supported:
To sum up, the jptools logging mechanism enables you to have the control of the logger not only in your project but also in all third party libraries which are using this logging mechanism.
Design
The Logger class
The main class of this package is the Logger. Each class which have to log
defines an instance of this class:
public class Demo
{
private static final Logger log = Logger.getLogger( Demo.class );
...
}
The Logger class defines methods to log to a special level like
info(...) which logs a simple information. The logger holds also a member
attribute in the background, an instance of a LogWriter. Each class which
implements this interface implements a concrete logger.
The following classdiagram gives an overview of the logging framework:

The LogWriter classes
A LogWriter which is wrapped by the Logger class implements the
concrete logger. The LogWriter use an Appender to write the message
(LogMessage).
The class diagramm gives an overview of the implemented LogWriter classes:

The following LogWriter implementations are available:
AbstractFeatureSupportLogWriter: This class implements some general features which should not implemented by eachLogWriterclass like the support of the version number etc.
This class is the base class of all LogWriter classes. This class works with theAppender. Each subclass returns with the methodgetAppender()a concrete implementation.AbstractLogWriter: This LogWriter class implements class context feature.DispatchLogWriter: This class supports the feature to dispatch theLogMessageto mulitpleAppender's.FileLogWriter: This class use theFileAppenderto implements functionality to log to a file.NullLogWriter: This class implements the interface but do nothing in the background. It is used to disable the logger.SimpleLogWriter: This class writes the output over theStreamAppenderto a defined stream like the standard output (stdout) or standard error (stderr).StreamLogWriter: This log writer extends an output stream. It can be used to log to the logger over a stream. Different java libraries offers only anOutputStreamwhere logs will be written to. This log writer gives a simple way to write this logs also to the logger.
The key logger.writer defines in the logger configuration which
LogWriter should be used. The defaut value is jptools.logger.SimpleLogWriter.
(see The logger configuration).
The Appender classes
An Appender implements where the logger output will written to. The class diagramm
gives an overview of the implemented Appender classes:

The following Appender implementations are available:
AbstractAppender: This class is the base class of all appenders. Its implement a named cache where appender information can be stored or loaded by a given key.CommonAppender: This class appends the log messages to the common logger framework.DatabaseAppender: This class appends the log messages to a table in a database. This implementation is a simple buffer which collects the last log entries. Only if a log entry with specific level occurs it logs the defined last log entries over a defined appender.DailyFileAppender: This appender implements the functionality to change filenames automaticly. Note that the filename doesn't have to change every day, making it possible to have logfiles which are per-week or per-month.FileAppender: This appender writes the output to a file. The filename can be configured in theLogConfigwith the key logger.destination.JDKAppender: This class appends the log messages to the JDK logger framework.JMSQueueAppender: This appender logs the messages to a JMS queue.JMSTopicAppender: This appender logs the messages to a JMS topic.LogRotateBufferAppender: This class implements a simple buffer which collects the last log entries. Only if a log entry with specific level occurs it logs the defined last log entries over a defined appender.MailAppender: This class implements a simple buffer which collects the last log entries. Only if a log entry with specific level occurs it logs the defined last log entries over mail (smtp).RollingFileAppender: This appender writes the output to a file which can grow to a defined size. If it is reached the file rotates to a backup file. The number of backup files can be configured. If the max. number of backup files is reached than the oldest file will delete.StreamAppender: This class writes the output to a defined stream.
The Appender class use the defined Layout to write the message to a stream,
file etc.
The Layout class
The definition of the output of a simple log line will decided by the used layout. The
interface Layout defines the simple API. The following layout
implementation are available:
AbstractLayout: This class implements some useful helper methods which can be used to write your own layout class.SimpleLayout: This class implements the layout to log the output as plain ASCII. This layout supports the timestamp, the log-level, some additional log information, the message, etc.XMLLayout: This class implements a simple XML layout structure which can be used as logger layout.
The following classdiagram gives an overview of the logger layout:
The key logger.layout defines in the logger configuration which
Layout instance should be used. The defaut value is jptools.logger.SimpleLayout.
(see The logger configuration).

Filtering log messages
Filtering classes and packages
The class AbstractAppender uses the Filter class
to filter the logger output. It enables to filter whole package structures or just
a simple class out. Each subclass of the AbstractAppender can use the
method checkFilter to test in its writeMessage method if
the message should logged or not.
The mapping role is simple:
- Each filter role defines a level for a package or class.
- The level definition is either a simple level or neither a level expression. A level
expression is a string which contains levels whith arithmetic characters like +
and -.
Examples:- log only
ERRORmessages:ERROR - log
INFO,DEBUGandERRORlog messages:INFO + DEBUG + ERROR - log
ALLmessages exceptDEBUGandUNKNOWNmessages:ALL - DEBUG - UNKNOWN
IMPORTANT: The level names in this example are equal to this of the logger configration (see above). If you change the keys
logger.allText,logger.offTextetc. than you have also to modify the filter roles. - log only
- If the role contains a package or class wich is a or in a subpackage of an already
defined package than the last entry wins (last means the deepest in the class hierarchy).
Examples:log everything of the
com.testpackage:
logger.filter.com.test = ALLlog only
WARN,ERRORandFATALof thecom.testpackage:
logger.filter.com.test = WARN + ERROR + FATALlog only
WARN,ERRORandFATALof thecom.testpackage. But log alsoDEBUGlogs of theMainTestclass :
logger.filter.com.test = WARN + ERROR + FATAL
logger.filter.com.test.MainTest = WARN + ERROR + FATAL + DEBUGlog only
WARN,ERRORandFATALof thecom.testpackage. But log alsoDEBUGlogs of theMainTestclass. Log everything of the subpackageruns:
logger.filter.com.test = WARN + ERROR + FATAL
logger.filter.com.test.runs = ALL
logger.filter.com.test.MainTest = WARN + ERROR + FATAL + DEBUG
Filtering additional information
The AbstractAppender also supports an additional user defined filter.
Each log method of the class Logger like info, warn, error and fatal supports
as first parameter a LogInformation instance.
The Interface LogInformation defines only the method getLogInformation
to print out the additional log information. The aim of this additional log information is
to filter at runtime in use with regular expression the user defined log entries.
This feature also can be used in combination with the class and package filter!
Key |
Default value |
Description |
|---|---|---|
|
Defines the log information filter. The filter should to be a regular expression.
Example: Filter all log information which ends with myName: |
The logger configuration (LogConfig)
The class LogConfig implements the configuration of the logger. The methods
getConfig and setConfig of the class Logger could be
used to receive or change settings at runtime. The methods getProperty and
setProperty can be used to get or set a logger attribute.
LogConfig conf = Logger.getConfig(); conf.setProperty( LogConfig.LOG_WRITER, "jptools.logger.SimpleLogWriter" ); conf.setProperty( LogConfig.LOG_LAYOUT, "jptools.logger.SimpleLayout" ); Logger.setConfig( conf );
In the sub chapters all settings of the logger configuration in the jptools.properties
are explained. The link shows a whole example of a
logger configuration.
General logger configuration
The following table shows the general logger configuration:
Key |
Default value |
Description |
|---|---|---|
|
|
Defines the |
|
|
Defines the enabled level to use. See also chapter filtering |
|
|
Defines the |
|
|
Defines to default writer context name |
|
|
Enable/disabe the mbean support. |
|
|
Defines to do RMI logging |
|
|
Enable/disabe the bootstrap log information. Mostly used to debug. |
|
|
Enable/disabe the statistic information. The logger statistic log only the
|
|
|
Enable/disabe the log information in the header |
|
|
Enable/disabe the version information in the header. The version number of a class is taken from the attribut VERSION if it exist. The RCS/CVS notation is allowd. It follows an example:
/** Version-number for version-control */
public static final String VERSION = "$Revision: 1.0 $";
|
|
|
Enable/disabe the hierarchy log |
|
|
Enable/disabe the additional stacktrace information. Performance: DON'T USE FOR PRODUCTION USE! |
Configuration of the StreamAppender (also SimpleLogWriter)
The following table shows StreamAppender specific settings:
Key |
Default value |
Description |
|---|---|---|
|
|
Defines the outputstream to use. The following values are valid:
|
Configuration of the FileAppender (also FileLogWriter)
The following table shows FileAppender specific settings:
Key |
Default value |
Description |
|---|---|---|
|
|
Defines the name of the log output. |
|
|
Append the log file: true / false |
Configuration of the DailyFileAppender
The following table shows DailyFileAppender specific settings:
Key |
Default value |
Description |
|---|---|---|
|
|
Defines the name of the log output.
As example: |
|
|
Append the log file: true / false |
|
Defines the current name if it should be different. If it is not defined
As example: |
Configuration of the JMSQueueAppender
The following table shows JMSQueueAppender specific settings:
Key |
Default value |
Description |
|---|---|---|
|
|
Defines the context factory. |
|
|
Defines the JMS url. |
|
|
Defines the JMS connection factory. |
|
|
Defines the JMS queue name. |
Configuration of the JMSTopicAppender
The following table shows JMSTopicAppender specific settings:
Key |
Default value |
Description |
|---|---|---|
|
|
Defines the context factory. |
|
|
Defines the JMS url. |
|
|
Defines the JMS connection factory. |
|
|
Defines the JMS topic name. |
Configuration of the RollingFileAppender
The following table shows RollingFileAppender specific settings:
Key |
Default value |
Description |
|---|---|---|
|
|
Defines the name of the log output. |
|
|
Defines the max. file size. The number can be followed by K, M, G and T (kilo, mega, giga and tera bytes). |
|
|
Defines the max. number of backup files. |
Configuration of the LogRotateBufferAppender
The following table shows LogRotateBufferAppender specific settings:
Key |
Default value |
Description |
|---|---|---|
|
|
Defines the size of the buffer. |
|
|
Defines the alert level to log out the buffer. |
|
|
Defines the appender which is used to real log the buffered entries. |
|
|
Defines the start string of each log entry. If it is empty it doesn't appear. |
|
Defines the end string of each log entry.. If it is empty it doesn't appear. |
Configuration of the MailAppender
The MailAppender is a subclass of the LogRotateBufferAppender. The configuration
of the MailAppender is the same like the LogRotateBufferAppender. The additional
configuration is used to the define mail specific settings:
Key |
Default value |
Description |
|---|---|---|
|
|
Defines the mail hostname. |
|
Defines the from address of the mail. |
|
|
Defines the to address of the mail. |
|
|
|
Defines the mail subject. |
Configuration of the DatabaseAppender
The DatabaseAppender is a subclass of the LogRotateBufferAppender. The configuration
of the DatabaseAppender is the same like the LogRotateBufferAppender. The additional
configuration is used to the define mail specific settings:
Key |
Default value |
Description |
|---|---|---|
|
|
Defines the default table. |
|
Defines the DataSource. To use the |
|
|
|
Create table if it does not exist. |
Configuration of the DispatchLogWriter
The DispatchLogWriter configuration starts with logger.dispatch. and
follows by a user defined name. After the name followed by a dot (.) the normal LogConfig
configuration follows.
The following example use as default the SimpleLayout and uses the
DispatchLogWriter. It contains three different appenders (main, file, dailyFile):
logger.layout = jptools.logger.SimpleLayout logger.writer = jptools.logger.DispatchLogWriter # main: log all debug logs to the console logger.dispatch.main.appender = jptools.logger.appender.StreamAppender logger.dispatch.main.filter.jptools = DEBUG # file: log to a file with name appender-file.txt. Log only warning, errors and fatals. # The output starts with the month and day etc. logger.dispatch.file.appender = jptools.logger.appender.FileAppender logger.dispatch.file.destination = appender-file.txt logger.dispatch.file.filter.jptools = WARN + ERROR + FATAL logger.dispatch.file.dateFormat = MM.dd-HH\:mm\:ss.SSS # dailyFile: log to a file which contains the year, month and day in the filename. The filename # will created every day new logger.dispatch.dailyFile.appender = jptools.logger.appender.DailyFileAppender logger.dispatch.dailyFile.destination = trace-'yyyy-MM-dd'.log
Configuration of the AbstractLayout and SimpleLayout
The following table shows AbstractLayout and SimpleLaoyut
specific settings:
Key |
Default value |
Description |
|---|---|---|
|
|
Defines the |
|
|
Defines the |
|
|
Defines the |
|
|
Defines the |
|
|
Defines the |
|
|
Defines the |
|
|
Defines the |
|
|
Defines the |
|
|
Defines the exception start message |
|
|
Defines the date format |
|
|
Defines the item separator (separator between the header information) |
|
|
Defines the message separator (separator between the header and the message) |
|
|
Defines the character to fillup header messages if they have a too short length |
|
|
Defines the hierarchy start tag in the message header |
|
|
Defines the hierarchy end tag in the message header |
|
|
Defines the string to indent hierarchy log messages. If the string is empty no indention will done. |
|
Defines the start string for indention of the hierarchy log messages. If the string is empty no indention will done. |
|
|
|
Defines the max. hierarchy level to indent. A negative value defines no limitation. |
|
|
Enable/disabe the thread name in the header message |
|
|
Enable/disabe the time stamp in the header message |
|
|
Enable/disabe the package name in the header message |
|
|
Enable/disabe the class name in the header message |
|
|
Enable/disabe the level in the header message |
|
|
Enable/disabe the log message |
|
|
Enable/disabe to show the stacktrace information by an exception |
|
|
Enable/disabe the hierarchy level in the log header |
|
|
Enable/disabe the hierarchy correction. Often the output of a filtered hierarchy log do not looks very well. The correction tries to solve the output presentation problem. |
|
|
Defines the with of the level in the header field. There are two possibilities: |
|
|
Defines the with of the thread name in the header field. There are two possibilities: |
|
|
Defines the with of the thrace information in the header field. There are two possibilities: |
|
|
Defines the with of the log information ( |
|
|
Defines the with of the version in the header field. There are two possibilities: |
|
|
Defines the with of the hierarchy level in the header field. There are two possibilities: |
Examples
Simple logging
import jptools.logger.Logger;
public class Demo
{
private static final Logger log = Logger.getLogger( Demo.class );
public static void main( String[] args ) throws Exception
{
log.info( "This is a simple info message" );
log.debug( "This is a simple debug message" );
log.warn( "This is a simple warn message" );
log.error( "This is a simple error message" );
log.fatal( "This is a simple fatal message" );
}
}
Logging with excpetion
import jptools.logger.Logger;
public class Demo
{
private static final Logger log = Logger.getLogger( Demo.class );
public static void main( String[] args ) throws Exception
{
try
{
...
}
catch( Throwable t )
{
log.error( "An exception occured!", t );
}
}
}
Logging with level check
import jptools.logger.Logger;
public class Demo
{
private static final Logger log = Logger.getLogger( Demo.class );
public static void main( String[] args ) throws Exception
{
// this is used if the log output takes time and is expensive!
if( log.isDebugEnabled() )
{
for( int i=0; i<100; i++ )
log.debug( "The debug message " + i );
}
}
}