Logging (log4e) The log4e library provides a complete framework for logging messages in an application. Logged messages can be processed based on priority and content and sent to multiple destinations. A destination, in Goanna log4e, is known as an appender. The types of appenders include files and the NT event log. The classes in log4e are a direct port of Jakarta log4j which can be found at the Apache Jakarta project. For the most part, the source has been ported as is. A number of Eiffel-like improvements have been made such as appropriate contracts, and the use of generic types. The general struture and usage of the library remains very similar to the original Java version.
Overview The central object of the log4e library is the hierarchy. The hierarchy holds each of the logging categories on which messages can be logged. As its name suggests, the hierarchy maintains the collection of categories in a hierarchy with a predefined root category at the top and user defined sub-categories beneath. Each category has a name, a logging priority and zero or more appenders. Different types of appenders can log messages to different destinations, be it a file, NT event log or socket. They can also format those messages any way required and filter messages based on priority and/or message content.
The Category Hierarchy The category hierarchy, represented by the class LOG_HIERARCHY, holds a collection of categories that you define. Multiple independant hierarchies can exist within an application. However, it is more common to use one hierarchy with multiple categories to represent different logging areas. LOG_HIERARCHY provides methods for accessing categories and setting hierarchy level logging priorities. The hierarchy is the only object that can create instances of categories (LOG_CATEGORY and maintains the parents of each category internally. In addition, each hierarchy instance initialises a root category that resides at the top of the category hierarchy. If required, the root category can be used as the sole logging category in an application.
Categories The category hierarchy is determine by the names given to each category — a dot in the name indicates a parent-child relationship between categories. For example, if a category is created with the name a.b it identifies the category named b with the parent category named a. When this category is created the category hierarchy will also instantiate any intermediate parent categories if needed. For the category named a.b the hierarchy will create a category named a if it doesn't already exist and set it as the parent of b. A category name can contain an arbitrary number of parts separated by dots ('.'). The hierarchy will manage the parents automatically for you. The naming of categories follows your requirements for logging. Category hierarchies can be defined that provide logging categories for an application as a whole, subsystems and/or source modules. You can create very simple logging hierarchies, such as using only the root category, through to very complex hierarchies, such as providing a category for every source file or module in your application. A category can only be created by the category hierarchy by calling the category function of LOG_CATEGORY. The sole parameter to this function specifies the name of the category you would like to access. The hierarchy will create the category (and its parents) if required and return the category to you. This ensures that there will only be one instance of a named category in each category hierarchy and that it is correctly positioned in the hierarchy. Once you have a reference to the category you can then configure the category's priority and appenders and log messages to it. For example, given the following variables declarations: hierarchy: LOG_HIERARCHY category: LOG_CATEGORY you can create a category named server.access and assign it to the variable category using: category := hierarchy.category ("server.access") Messages can be sent to a category using the routines debuggingThe original log4j library uses the routine name debug however this is a reserved word in Eiffel and has therefore been renamed debugging, warn, info, error, fatal or log to log at predefined priorities or a specified priority in the case of log.
Priorities Each log message has a priority which can determine what should be done with the message, whether it should be written to a log file or discarded. All log4e priorities are defined in the class LOG_PRIORITY_CONSTANTS of type LOG_PRIORITY and range from the lowest priority Debug_p through Info_p, Warning_p and Error_p, to the highest priority Fatal_p. The logging priorities are represented by a positive integer ranging from 50,000 for Fatal_p through to 10,000 for Debug_p. The integers representing priorities could just as easily ranged from 5 to 1. The numbers were originally used in the log4j library and it was decided not to change them. This is to facilitate comparisons between priorities to determine if a log message matches or exceeds a set priority and should therefore be written to a log file. A log priority also has a string representation that identifies the priority and can be written to log files. For example, the Debug_p has the string representation DEBUG which can be inserted into a log message during the formatting process. Each category can have its own priority setting which determines the level of log messages that will be processed or ignored. If a category does not have a priority set it will inherit one from its parent. The category priority determines the level of logging that will take place on that category — all log messages that meet or exceed the priority will be logged, all others will be discarded. To set the priority of a category you need to call set_priority and pass a priority object from the class LOG_PRIORITY_CONSTANTS. For example, you can set the logging priority of the category defined above to Info_p using: category.set_priority (Info_p) after which you can access the category's priority using the function priority which will return the priority or the parent category's priority if one has not been set. In addition to priorities set at the level of categories, a priority can be set at the hierarchy level. A hierarchy's priority is set during initialisation. The creation procedure make specifies a single argument, priority, which is used to set its priority level. The hierarchy priority level is checked before any category level priority to determine if a log message should be handled. Fine grained control over priorities can also be performed at the hierarchy level by disabling a particular priority (and and priorities below it). When a hierarchy is first created all priorities are enabled by default. If you need to disable a priority for an entire hierarchy, the most efficient way is to call disable of LOG_HIERARCHY passing the priority to disable. Convenience routines are also included to disable the most common priorities including disable_debug and disable_info used to disable debugging and information logging, respectively. You can also enable or disable all levels of logging using the functions enable_all and disable_all. The logging hierarchy typically determines whether to handle a message at a particular priority using internal integer comparisons. You can also determine whether a log message will be handled by calling is_enabled_for and passing the relevant priority object. The function will return True if that level of logging or above is enabled.
Additive Categories Once a category has handled a log message it can pass it on to its parent for additional handling. This is known as an additive category. The parent can then handle the log message and pass on to its parent if it is also additive. A category is additive by default as indicated by the query is_additive. If you need to change the additive status of a category call set_additive passing True or False depending on the status you need. The following table shows the affect of the is_additive flag. Affect of <function>is_additive</function> flag. Category Name is_additive Handled by root not applicable root x True x, root x.y True x, y and root x.y.z True x, y, z and root security False security security.access True access and security
Logging To send a log message of each priority to the category we created earlier, you can write: category.debugging ("This is a debug message") category.info ("This is an information message") category.warn ("This is a warning message") category.error ("This is an error message") category.fatal ("This is a fatal message") category.log (hierarchy.Info_p, "This is another information message") Each of the statements send a message to the server.access category with different priorities. The final statement explicitly logs a message with the priority Info_p as defined in LOG_PRIORITY_CONSTANTS. Many of the log4e classes inherit (sometimes indirectly) from LOG_PRIORITY_CONSTANTS and therefore provide access to all of the predefined priority constants, as used in the log example above. We could just as easily inherited from the LOG_PRIORITY_CONSTANTS class to access the priority directly. All of the logging routines take an object of type ANY as the message parameter thus allowing objects of arbitrary types to be logged. The category calls out on the object to convert it to a string that can then be written to the appropriate log. Basic types, such as INTEGER, DOUBLE and CHARACTER, all define reasonable implementations of out. If you need to log objects other than basic types you will need to redefine out to return the STRING representation you require. For example, given the following class USER: class USER inherit ANY redefine out end creation make feature -- Initialisation make (first, last: STRING) is do first_name := first surname := last end feature -- Access first_name: STRING surname: STRING feature -- Basic routines out: STRING is do create Result.make (100) Result.append ("USER first_name=" + first_name) Result.append (" surname=" + surname) end end -- class USER We can create an instance and log details about that instance to a category by passing the instance itself: create user.make ("Jim", "Smith") category.info (user) The string that will be sent to the log will be: USER first_name=Jim surname=Smith
Appenders Describe the purpose of appenders and how to create one.
File Appenders Describe general file appender classes.
Standard Out and Standard Error Describe standard out and standard error file appenders.
Rolling Files Describe rolling file appenders: rolling file, calendar rolling file and externally rolled.
NT Event Log Describe logging to the NT Event log.
Appender Layouts Describe creation and usage of layouts.
Simple Layout Describe simple layouts.
Date/Time Layout Describe time layout and date time layouts.
Pattern Layout Describe pattern layouts.
Filters Describe filtering of log events.
Priority Match Filters Describe priority match filters.
Priority Range Filters Describe priority range filters.
String Match Filters Describe string match filters.
Configuration Describe general configuration.
Programmatic Configuration Describe general programmatic configuration and shared logging hierarchies.
XML Configuration Describe configuration using XML.