This page provides a general overview of the Log4E library and its usage. An Installation page and a set of compilation notes are also available. Full class interfaces and relationships are available in the class documentation.
The following table lists the clusters provided with the Goanna Log4E library.
Library Cluster | Description |
library | Core classes for the library including the logger hierarchy, logger, priority and abstract classes for filters, appenders, and layouts. |
library/appenders | Commonly used appenders such as file appenders, stdout and stderr appenders, and simple rolling file appenders. |
library/appenders/net | A socket appender, syslog appender and an externally rolled appender (which can be rolled by connecting on a socket). |
library/appenders/nt | NT Event log appender. |
library/config | XML configuration parser and support classes. |
library/filters | Log event filters including string match, priority match and priority range. |
library/helpers | Classes used internally by Log4E including an internal log. |
library/layouts | Log event layout classes including a simple layout, time layout, date time layout and configurable pattern layout. |
library/layouts/pattern | Supporting classes for the pattern layout. |
The Jakarta Log4j library is highly regarded as one of the most comprehensive and useful logging frameworks available for Java. Its flexibility and extensibility make it one of the recommended third party libraries for any Java development that has logging requirements.
Log4E is an attempt to provide the same capabilities for Eiffel applications and to provide a readily available logging framework for Eiffel developers. It was originally developed as an integral component of the Goanna library but has since been converted to a standalone library because of its general usefulness to the Eiffel community.
The logging framework is managed by a log hierarchy represented by the class L4E_HIERARCHY. Instances of this class can be used to manage a hierarchy of loggers (L4E_LOGGER), each of which can process logging events differently. The hierarchy is arranged using a naming convention that separates parents from children using a full stop. For example, a logger with the name "a.b" is actually a child of a logger with the name "a". The hierarchy can be arranged in any configuration you require for an application. A hierarchy might represent different classes within your application with the parents mirroring the clusters that the classes are contained within. In the Java world, a Log4j hierarchy typically follows a package/class structure.
Each logger in the hierarchy is given a priority (L4E_PRIORITY) which represents the level of log events that will be processed by the logger. The event priorities supported by Log4E include: FATAL, ERROR, WARN, INFO, and DEBUG each of which are defined in the class L4E_PRIORITY_CONSTANTS. The priority determines if a log event will be processed or ignored. Each log event (L4E_EVENT) is raised with a particular priority and as it progresses through the logging hierarchy, each logger can determine if it should process the event by comparing its priority with the priority of the event. If the event priority is greater or equal to the logger priority then it will be processed.
At the top of the logging hierarchy is the root logger. This logger is predefined and cannot be removed or replaced. You can retrieve the root logger, perhaps to attach an appender, through the feature root of class L4E_HIERARCHY. All loggers that you create become decendants of the root logger.
Priorities are inherited by children in the log hierarchy. Thus if the root logger is given the priority INFO then all descendant loggers (that don't define their own priority) will use the root priority. Similarly, the children of a logger lower down in the hierarchy, that does define its own priority, will inherit the priority of its parent. The following table illustrates the inheritance of logging priorities.
Logger | Assigned Priority | Inherited Priority |
root | INFO | N/A |
x | none | INFO |
x.y | DEBUG | N/A |
x.y.z | none | DEBUG |
The logger x inherits the priority of the root logger while logger x.y defines its own. Finally, logger x.y.z inherits the priority DEBUG from its immediate parent logger x.y.
Apart from the root logger, all loggers are created on the fly (including any intermediate parents) when they are first requested. For example, given the following attribute declarations:
hierarchy: L4E_HIERARCHY
logger: L4E_LOGGER
The following code fragment assigns a logger named x.y to the logger attribute:
logger := hierarchy.logger ("x.y")
If a logger named x.y does not already exist then the hierarchy will create it. The hierarchy also checks to see if appropriate parent loggers exist and creates them if they don't. In this case, a logger named x will be created.
The class L4E_HIERARCHY also provides features for disabling particular event priorities at a global level and accessing the full hierarchy of loggers. See the flat class interface for more details.
Processing a log event involves passing the event on to a log appender, represented by the deferred class L4E_APPENDER. Descendants of this class implement different types of processing for events such as writing to a file, sending across a socket, and anything else you could imagine doing to an event! A logger is not restricted to one appender, in fact, there are no limitations. If one particular logger needs to process events in a number of ways, then it can attach more than one appender to itself. For example, an application may require an audit log that writes all login attempts to a file and also to a database. Two separate appenders can be attached to the audit logger so that each event is processed by both.
A log event is handled by the logger in which it was generated and then by that logger's parent and so on until, finally, it is handled by the root logger. The propagation through the parents is governed by the value of the additive flag in each logger. If a logger is set to additive then an event will be handled and then passed to its parent. To illustrate this capability, the following table shows a set of loggers with different additive values and shows which appenders will receive a log event at each level of the heirarchy.
Logger | Appenders | Additive? | Event Handling |
root | A1 | N/A | A1 |
x | X1, X2 | Yes | X1, X2, A1 |
x.y | none | Yes | X1, X2, A1 |
x.y.z | Z1 | Yes | Z1, X1, X2, A1 |
audit | AUDIT1 | No | AUDIT1 |
audit.login | none | Yes | AUDIT1 |
The logger audit is the only one with additive set to False. In this case, events originating in audit and audit.login are only sent to the appender named AUDIT1. The loggers x, x.y and x.y.z all pass events to their parents for handling. The additive flag can be set by calling the routine set_additive on class L4E_LOGGER.
The Log4E library includes a number of appenders that provide the most common logging required by an application such as writing to a file. Additional appenders are also available that can write events to sockets, Unix syslog and the NT event log and you can implement your own custom appenders easily. The following table describes each of the appenders available in the standard Log4E library.
Appender Class | Description |
L4E_FILE_APPENDER | Writes events to a file. The file can either be appended to or overwritten each time an application starts. |
L4E_STDOUT_APPENDER | Writes events to standard output. |
L4E_STDERR_APPENDER | Writes events to standard error. |
L4E_ROLLING_FILE_APPENDER | Writes events to a file that will be "rolled over" when it reaches a certain size. When the file is rolled over a new file is created and a specified number of old files are retained. |
L4E_CALENDAR_ROLLING_APPENDER | Writes events to a file that is rolled every minute, hour or day. A specified number of rolled files are retained. |
L4E_SOCKET_APPENDER | Writes log events (in the form of a L4E_STORABLE_EVENT) to a specified host and TCP port. A server listening on the socket can read the event and process them as needed. |
L4E_SYSLOG_APPENDER | Writes log events to a Unix syslog at a specified host and UDP port. The event is written with a to a specified "facility". |
L4E_EXTERNALLY_ROLLED _FILE_APPENDER | Writes logs to a file which can be rolled by connecting to a configurable socket. This appender uses a threaded class that performs the socket listen and notifies the appender to roll whenever a connection is made to the socket. |
L4E_NT_EVENT_LOG_APPENDER | Writes log events to the NT Event Log using a specified application name. Appropriate registry entries are created, if neede,d for the appender to function correctly. The DLL NTEventLogAppender.dll must be on the path for this appender to function correctly. |
When a log event is processed by an appender it can be formatted using a layout (L4E_LAYOUT) which provides the mechanism to convert the event into a formatted representation. The default layout for newly created (file) appenders is the L4E_SIMPLE_LAYOUT which formats an event using the priority and log message separated by a hyphen character. The layout classes available in Log4E are listed in the following table.
Layout Class | Description |
L4E_SIMPLE_LAYOUT | Formats an event as: <priority> "-" <message>. Where <priority> is the event priority string and <message> is the log message sent with the event. |
L4E_TIME_LAYOUT | Formats an event as: <seconds> "-" <priority> "-" <message>. Where <seconds> is the number of seconds elapsed since the application started. |
L4E_DATE_TIME_LAYOUT | Formats an event as: <timestamp> "-" <priority> "-" <message>. Where <timestamp> is a detailed date time of the form "YYYY/MM/DD HH:MM:SS". |
L4E_PATTERN_LAYOUT | Formats an event using a customisable pattern. See below for more detail. |
A layout can be registered with an appender by creating an instance of a pattern class and passing it with a set_layout call.
The pattern layout class provides a flexible way of creating custom layouts using a printf like conversion pattern. The pattern string consists of plain text interspersed with escape sequences that will be substituted with event information. Each escape sequence consists of the character '@' followed by a predefined character that represents the information that will be substituted. The following table lists the escape sequences supported by the pattern layout.
Escape Sequence | Result |
@@ | Insert the literal character '@' |
@c | Insert the name of the logger that raised the event. |
@d | Insert the event date and time |
@m | Insert the event message |
@r | Insert a date and time relative to the start of execution |
@p | Insert the event priority |
Any other text not preceded by an '@' character is inserted as is. If any other character follows an '@' sign then an error will be raised. You can further modify the output of each escape sequence by adding format modifiers between the '@' sign and the specifier character. The next table shows the types of format modifiers you can use.
Escape Sequence |
Left Justify
|
Minimum Width
|
Maximum Width
|
Description |
@20c |
False
|
20
|
none
|
Set the minimum length to 20 characters and right justify. Left pad with spaces if the logger name is less than 20 characters in length. |
@-20c |
True
|
20
|
none
|
Set the minimum length to 20 characters and left justify. Right pad with spaces if the logger name is less than 20 characters in length. |
@.30c |
N/A
|
none
|
30
|
Set the maximum length to 30 characters. Truncates from the beginning if the logger name is longer than 30 characters. |
@20.30c |
False
|
20
|
30
|
Set the minimum length to 20 and the maximum length to 30 and right justify. Left pad if the logger name is shorter than 20 characters or truncate if it is longer than 30 characters. |
@-20.30c |
True
|
20
|
30
|
Set the minimum length to 20 and the maximum length to 30 and left justify. Right pad if the logger name is short than 20 characters or truncate if it is longer than 30 characters. |
The following convertion pattern:
@d [@6p] @c - @m%N
Will result in log events formatted with a date, space character , left square bracket, priority (left justified in a 6 character string), right square bracket, space character, the logger name, another space, the message, and finally, a newline character (which in Eiffel is converted to an appropriate %R%N pair if required by the operating system). Therefore, a log event sent to a logger named audit, with the priority INFO and with the message "glennm successfully logged in", would result in the formatted string:
2002/06/30 12:30:00 [INFO ] audit - glennm successfully logged in
Each appender can have a series of filters (L4E_FILTER) that can reject log events based on configurable criteria. Filters are added to an appender by calling add_filter and they are invoked in the order that they were added. A filter can either accept a log event for processing, reject the event, or indicate that it does not care (neutral) and pass the event to the next filter to decide.
The filters available in Log4E are listed in the following table.
Filter Class | Description |
L4E_PRIORITY_MATCH_FILTER | Examines the log event priority and depending on the match_on_filter flag either accepts or rejects the event. If the priority does not match then the filter is neutral. |
L4E_PRIORITY_RANCH_FILTER | If the priority is outside the range (inclusive) then Filter_reject is returned. If the priority is within the range and match_on_filter is True then Filter_accept is returned. If the priority is within the range and match_on_filter if False then Filter_neutral is returned. |
L4E_STRING_MATCH_FILTER | The decision is Filter_accept if the string is found and match_on_filter is True. If a match is found and match_on_filter is False then Filter_reject is returned. If no match is found then Filter_neutral is returned. |
There are two ways of configuring the Log4E library; programatically or via an XML configuration file. The programmatic approach involves using Eiffel code to create and setup loggers and appenders. Typically this would be done at the earliest point in the application, such as the creation procedure of the root class. The following routine shows an example configuration routine:
configure_logger is -- Setup the Log4E loggers and appenders local h: L4E_HIERARCHY l: L4E_LOGGER a: L4E_APPENDER p: L4E_PATTERN_LAYOUT do -- create the hierarchy with a priority of INFO create h.make (Info_p) -- create a logger named "audit" l := h.logger ("audit") -- create a file appender that will write to a file named -- "app.log" and appends if the file already exists create {L4E_FILE_APPENDER} a.make ("app.log, True) -- Register the appender with the "audit" logger l.add_appender (a) -- create a pattern layout and register it with the appender create p.make ("@d [@6p] @c - @m%N") a.set_layout (p) end
For more information on configuring Log4E programatically see the examples located in $LOG4E/examples/config.
The entire logging framework can also be configured using XML. A sample XML configuration file follows:
<?xml version="1.0"?> <config xmlns="http://goanna.info/spec/log4e"> <!-- create file appender to log to app.log --> <appender name="file" type="file"> <param name="filename" value="app.log"/> <param name="append" value="true"/> <layout type="pattern"> <param name="pattern" value="@d [@-6p] @c - @m
"/> </layout> </appender> <!-- configure logger 'audit' --> <logger name="audit"/> <!-- configure the root category for info events and to log to the appender 'file' --> <root priority="info"> <appender-ref ref="file"/> </root> </config>
More extensive configuration elements are available. Again browse the examples for more details.
The example located in $LOG4E/examples/vision implements a graphical log event processor which listens for events sent via a socket using a socket appender. You can compile this example as you would any of the others, the only additional requirement is that the EiffelVision2 library from Eiffel Software Inc. must be available.
Once the example is compiled, start it by specifying a port number on the command line. For example:
logvision 9000
will start the application so that it listens on port 9000 for log events. The main window will appear and the application will site quietly waiting for events.
To send log events to logvision you need to add a socket appender (L4E_SOCKET_APPENDER) to your application and specify the host and port that logvision is listening on. The example found in $LOG4E/examples/socket/client is a good starting point.
You can filter incoming events by priority, logger name or message. The events received by logvision can also be exported to a file using the layout of the original event. The main window of logvision is shown next.