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
Level
class):INFO
DEBUG
WARN
ERROR
FATAL
PROFILE
- The logger can be configured with an instance of the class
LogConfig
. ThegetLogger
andsetLogger
are methods of the classLogger
to 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 eachLogWriter
class 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 theLogMessage
to mulitpleAppender
's.FileLogWriter
: This class use theFileAppender
to 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 theStreamAppender
to 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 anOutputStream
where 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 theLogConfig
with 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
ERROR
messages:ERROR
- log
INFO
,DEBUG
andERROR
log messages:INFO + DEBUG + ERROR
- log
ALL
messages exceptDEBUG
andUNKNOWN
messages: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.offText
etc. 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.test
package:
logger.filter.com.test = ALL
log only
WARN
,ERROR
andFATAL
of thecom.test
package:
logger.filter.com.test = WARN + ERROR + FATAL
log only
WARN
,ERROR
andFATAL
of thecom.test
package. But log alsoDEBUG
logs of theMainTest
class :
logger.filter.com.test = WARN + ERROR + FATAL
logger.filter.com.test.MainTest = WARN + ERROR + FATAL + DEBUG
log only
WARN
,ERROR
andFATAL
of thecom.test
package. But log alsoDEBUG
logs of theMainTest
class. 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 ); } } }