## 3. How does the CLF work?

Applications make logging calls on commonLogger objects. These commonLogger objects pass log messages to Handler for publication. Both commonLoggers and Handlers may use logging Levels to decide if they are interested in a particular log message. Since a commonLogger object is tied with an underlying logger tool, such a category or a channel according to the underlying logging service, the Handler responsible to manage the output is also tied with the respective mechanism managing the output as defined by the same underlying logging service such an Appender or a LogWriter. That is a Handler can use appropriate mechanism to format and localize a log message before publishing it to the output.

### 3.1 commonLogger, LoggerFactory and LogManager

commonLogger objects are provided by a LoggerFactory object, which hides the type of the underlying logging implementation. The LogManager is responsible to create a LoggerFactory appropriate to an underlying logging implementation. For this aim, the LogManager uses the property named "com.hp.mw.common.utils.logging.factory", to determine the underlying class that shall be loaded to create commonLogger objects. The classes provided by this distribution are:

- com.hp.mwlabs.common.util.logging.log4j.ArjLoggerFactory (Default class). This class allows to build log4j logger objects.
- com.hp.mwlabs.common.util.logging.csf.ArjChannelFactory. This class allows to build logger objects specific to the HP logging API mainly used by the HP CSF framework.
- com.hp.mwlabs.common.util.logging.simpleLog.ArjSimpleLoggerFactory . This class allows to build simple commonLogger objects if an underlying logging tool is not provided.

The way to provide the classname, identifying the LoggerFactory to use, allows defining new logging services without rebuilding the Common Logging Framework. The following example illustrates how a LoggerFactory object and a commonLogger object are created:

import com.hp.mw.common.util.logging.LogManager; import com.hp.mw.common.util.logging.LoggerFactory; import com.hp.mw.common.util.logging.commonLogger; .. static LoggerFactory log_fac = LogManager.getLogFactory(); static commonLogger mylogger = (commonLogger)log_fac.getLogger("TestLogLevels");

An application can be executed as follows where the property specifying the underlying logging service is provided:

> java –Dcom.hp.mw.common.utils.logging.factory =<log_classname> MyApplication

Where <log_classname> may have one of the values described above or any class that implements the LoggerFactory interface.
Rather that using the Java -D flag, all all properties may be defined in a configuration file used by the CLF.

### 3.2 Logs Levels

Each log message has an associated log Level. The Level gives the importance and urgency of a log message. Log level objects encapsulate an integer value, with higher values indicating higher priorities.

Similarly commonLogger may be assigned log Levels. The set of possible Log Levels are DEBUG, INFO, WARN, ERROR and FATAL defined in the com.hp.mw.common.util.logging.CommonLevel class. Since Log Levels are assumed to be associated to the underlying levels or priorities defined by the underlying logging service, they are defined as static but no final variables in such way that their final values will be assigned at initialisation by the appropriate LoggerFactory.

The table below describes how is mapped the concept of the Level interface and the CommonLevel values to the underlying logging service.

Common Logging Framework | Log4j | HP Logging | JDK1.4 Logging API*
Level | Priority | LogLevel | Level
FATAL | FATAL | CRITICAL | SEVERE
ERROR | ERROR | ERROR | SEVERE
WARN | WARN | WARNING | WARNING
INFO | INFO | INFO | INFO
DEBUG | DEBUG | DEBUG | FINEST

*Not Implemented yet

Clients should normally use the predefined Common Level such as CommonLevel.FATAL or CommonLevel.INFO.

As stated earlier, client code sends log requests to commonLogger objects. Each commonLogger keeps track of a log level that it is interested in, and discards log requests that are below this level. In other words, a logging request is said to be enabled if its log Level is higher than or equal to the Log Level of its commonLogger. Otherwise, the request is said to be disabled. By default, a commonLogger is assigned with the CommonLevel.DEBUG, the lowest value allowing the commonLogger to accept any log request. The Level associated with the commonLogger can be changed with the setLogLevel method defined in the commonLogger interface.

In addition to specify its Level, a commonLogger may be disabled and enabled. Disabling a CommonLogger, with the DisableLog method on the commonLogger interface, means that it any log message is discarded whatever its Level. Enabling a commonLogger, the EnableLog method on the commonLogger interface, means that log messages with same or higher priority are enabled. The method IsEnabled on the commonLogger interface allows determining if the commonLogger is enabled or not.

Defined Levels are ordered according to their integer values as follows: DEBUG < INFO < WARN < ERROR < FATAL. The following example illustrates how logging request are enabled or disabled:

. . . static LoggerFactory log_fac = LogManager.getLogFactory(); // get a commonLogger instance named "TestLogLevels" static commonLogger mylogger = (commonLogger)log_fac.getLogger("TestLogLevels"); // Set the commonLogger Level to INFO mylogger.setLogLevel(CommonLevel.INFO); // This request is enabled, because FATAL > INFO mylogger.log(CommonLevel.FATAL, "This is the first FATAL Message"); // This request is disabled, because DEBUG < INFO mylog.log(CommonLevel.DEBUG, "This is the first DEBUG Message"); // This CommonLogger is disabled the two next request are disabled mylogger.DisableLog(); mylogger.log(CommonLevel.INFO, "This is the first Info Message"); mylogger.log(CommonLevel.FATAL, "This is the second FATAL Message"); /* This CommonLogger is enabled the two next request are enabled since their values >= INFO */ mylogger.EnableLog(); mylogger.log(CommonLevel.INFO, "This is the second INFO Message"); mylogger.log(CommonLevel.FATAL, "This is the third FATAL Message"); . . .

### 3.3 Logging Requests

The commonLogger interface provides the log method with a variety of arguments as described hereafter:

/** Log a message, with no arguments if the the commonLogger is enabled for * the given message level */ void log(Level level, Object message); /** Log a throwable arguments message */ void log(Level level, Throwable throwable); /** Log a message, with a throwable arguments */ void log(Level level, Object message, Throwable throwable); /** Log a message, with arguments */ void log(Level level, Object message, Object[] params); /** Log a message, with arguments and with a throwable arguments */ void log(Level level, Object message, Object[] params, Throwable throwable);

Rather than calling the log method on the commonLogger interface, for convenience the Common Logging Framework provides methods associated with Level. That is rather than to invoke “commonLogger.log(CommonLevel.FATAL, …” a programmer will invoke “commonLogger.fatal(…”.

### 3.4 Debugging Granularity Extension

The Common Logging Framework provides an extension to filter logging messages according to finer granularity an application may define. That is, when a log message is provided to the commonLogger with the DEBUG level, additional conditions can be specified to determine if the log message is enabled or not. Note that these conditions are applied if and only the DEBUG level is enabled and the log request performed by the application specifies debugging granularity.

When enabled, Debugging is filtered conditionally on three variables:

- Debugging level: this is where the log request with the DEBUG Level is generated from, e.g., constructors or basic methods.
- Visibility level: the visibility of the constructor, method, etc. that generates the debugging.
- Facility code: for instance the package or sub-module within which debugging is generated, e.g., the object store.

According to these variables the Common Logging Framework defines three interfaces. A particular product may implement its own classes according to its own finer granularity.

public interface DebugLevel { /** return the value of the Debug level. */ long getLevel (String level); /** return the string representation of the Debug level. */ String printString (long level); }

public interface FacilityCode { /** return the value of the facility level. */ long getLevel (String level); /**return the string representation of the facility level. */ String printString (long level); }

public interface VisibilityLevel{ /** return the value of the visibility level. */ long getLevel (String level); /** return the string representation of the visibility level. */ String printString (long level); }

To permit the possibility to associate finer granularity values for a debug messages, the commonLogger interface provide the following operation:

> void debug(Object message, long dl, long vl, long fl);

The debug message is sent to the output only if the specified debug level (dl), visibility level (vl), and facility code (fl) match those allowed by the commonLogger. Finer levels assigned to the commonLogger are initialised at its creation or can be defined dynamically via a set of operations provided by the commonLogger interface. An application can specify its own values, however the Common Logging Framework provides default level values, per finer class, to indicate that no possible debug message is permitted or all debug messages are permitted.

### 3.4.1 Common Values

1. The CommonDebugLevel class provides the following enumerated types

public class CommonDebugLevel { public static final long NO_DEBUGGING = 0; public static final long CONSTRUCTORS = 0x00000001; public static final long DESTRUCTORS = 0x00000002; public static final long CONSTRUCT_AND_DESTRUCT = CONSTRUCTORS | DESTRUCTORS; public static final long FUNCTIONS = 0x00000010; public static final long OPERATORS = 0x00000020; public static final long FUNCS_AND_OPS = FUNCTIONS | OPERATORS; public static final long ALL_NON_TRIVIAL = CONSTRUCT_AND_DESTRUCT | FUNCTIONS | OPERATORS; public static final long TRIVIAL_FUNCS = 0x00000100; public static final long TRIVIAL_OPERATORS = 0x00000200; public static final long ALL_TRIVIAL = TRIVIAL_FUNCS | TRIVIAL_OPERATORS; public static final long FULL_DEBUGGING = 0xffffffff; }

- NO_DEBUGGING: No diagnostics.
A commonLogger object assigned with this values discard all debug requests
- FULL_DEBUGGING: Full diagnostics.
A CommonLogger object assigned with this value allows all debug requests if the facility code and the visibility level match those allowed by the commonLogger.

Additional Debugging Values are:
- CONSTRUCTORS: Diagnostics from constructors.
- DESTRUCTORS: Diagnostics from finalizers.
- CONSTRUCT_AND_DESTRUCT: Diagnostics from constructors and finalizers.
- FUNCTIONS: Diagnostics from functions.
- OPERATORS: Diagnostics from operators, such as equals.
- FUNCS_AND_OPS: Diagnostics from functions and operations.
- ALL_NON_TRIVIAL: Diagnostics from all non-trivial operations.
- TRIVIAL_FUNCS: Diagnostics from trivial functions.
- TRIVIAL_OPERATORS: Diagnostics from trivial operations, and operators.
- ALL_TRIVIAL: Diagnostics from all trivial operations.

2. The CommonFacilityCode class provides two enumerated types as described below:

public class CommonFacilityCode { public static final long FAC_NONE = 0x00000000; public static final long FAC_ALL = 0xfffffff; }

- FAC_NONE: No Diagnostic
A commonLogger object assigned with this values discard all debug requests
- FAC_ALL: Full Diagnostic
A commonLogger object assigned with this value allows all debug requests if the debug level and the visibility level match those allowed by the commonLogger.

3. The CommonVisibilityCode class provides two enumerated types as described below:

public class CommonVisibilityLevel { public static final long VIS_NONE = 0x00000000; public static final long VIS_PRIVATE = 0x00000001; public static final long VIS_PROTECTED = 0x00000002; public static final long VIS_PUBLIC = 0x00000004; public static final long VIS_PACKAGE = 0x00000008; public static final long VIS_ALL = 0xffffffff; }

- VIS_NONE: No Diagnostic
- VIS_PRIVATE : only from private methods.
- VIS_PROTECTED only from protected methods.
- VIS_PUBLIC only from public methods.
- VIS_PACKAGE only from package methods.
- VIS_ALL: Full Diagnostic

A commonLogger object assigned with this value allows all debug requests if the debug level and the facility code match those allowed by the commonLogger.

Once enabled, logging requests with DEBUG Level can be controller by setting the property variables DEBUG_LEVEL, VIS_LEVEL and FAC_LEVEL appropriately.

### 3.4.2 Defining its own finer debugging classes

For each type of variable an application may define its own class providing its own enumerated values, which shall differ those described above - In other words different from 0x00000000 or 0xffffffff. However, to avoid any confusion between finer values, the application should take benefit from the bit representation by defining its values as follow.

> Level_1 = 0x00000001
> Level_2 = 0x00000002
> . . .
> public class MyDebugValues
> public static final long

Since each debugging level is represented by a single bit in the controller, debugging of multiple levels can be produced by OR-ing together the fields. For instance a new value can be defined as follow:

> Level_3 = Level_1 | Level_2

If for instance, a CommonLogger object is assigned the Level_3, it enables a debug message only if the debug request is provided either with Level_3 or with Level_1 | Level_2.

As a template the following example illustrates how to build its own debugging classes providing a finer granularity.

import com.hp.mw.common.util.logging.DebugLevel; import com.hp.mw.common.util.logging.CommonDebugLevel; public class MyDebugLevel implements DebugLevel { public static final long DEBUG_LEVEL_1 = 0x00000001; public static final long DEBUG_LEVEL_2 = 0x00000002; public static final long DEBUG_LEVEL_3 = 0x00000004 . . . public final long getLevel (String level) { if (level.equals("N0_DEBUGGING")) return CommonDebugLevel.NO_DEBUGGING; if (level.equals("DEBUG_LEVEL_1")) return DEBUG_LEVEL_1; if (level.equals("DEBUG_LEVEL_2")) return DEBUG_LEVEL_2; . . . if (level.equals("FULL_DEBUGGING")) return CommonDebugLevel.FULL_DEBUGGING; return CommonDebugLevel.NO_DEBUGGING; //Default } public final String printString (long level) { if (level == CommonDebugLevel.NO_DEBUGGING) return "NO_DEBUGGING"; if (level == CommonDebugLevel.FULL_DEBUGGING) return "FULL_DEBUGGING"; String sLevel = null; if ((level & DEBUG_LEVEL_1) != 0) sLevel = ((sLevel == null) ? "DEBUG_LEVEL_1" : " & DEBUG_LEVEL_1"); if ((level & DEBUG_LEVEL_2) != 0) sLevel = ((sLevel == null) ? "DEBUG_LEVEL_2" : " & DEBUG_LEVEL_2"); . . . return ((sLevel == null) ? "NO_DEBUGGING" : sLevel); } }

In a similar way the application may define its own classes implementing the classes FacilityCode and VisibilityLevel.

### 3.4.3 commonLogger Debugging Values

Finer debugging values are assigned to a commonLogger object dynamically through the following methods on the commonLogger interface:

public interface CommonLogger extends Handler { . . . // All Level void setLevels (long dl, long vl, long fl); // Debug Level long getDebugLevel (); void setDebugLevel (long level); void mergeDebugLevel (long level); // Visibility Level long getVisibilityLevel (); void setVisibilityLevel (long level); void mergeVisibilityLevel (long level); // FacilityCode Levels long getFacilityCode (); void setFacilityCode (long level); void mergeFacilityCode (long level); .... }

- getXXX methods are used to obtain the current level assigned to a CommonLogger object.
- setXXX methods are used to set the level of a CommonLogger object
- mergeXXX methods are used to add a level value to CommonLogger.

### 3.4.4 A simple scenario

The following example illustrates how finer granularity extension is used when the DEBUG is enabled by the commonLogger. The application provides its own finer classes. We assume that the default finer values used to initialize the commonLogger are defined a configuration file.

import com.hp.mw.common.util.logging.CommonLevel; import com.hp.mw.common.util.logging.commonLogger; import com.hp.mw.common.util.logging.LoggerFactory; import com.hp.mw.common.util.logging.LogManager; import com.hp.mw.common.util.logging.Level; import com.hp.mw.common.util.logging.CommonDebugLevel; public class DebugExt{ static LoggerFactory log_fac = LogManager.getLogFactory(); static commonLogger mylog = (commonLogger)log_fac.getLogger("TestLogLevels"); public static void main(String[] args) { mylog.debug("This is a DEBUG Message"); mylog.debug("This debug message is enabled since it matches default Finer Values", MyDebugLevel.DEBUG_LEVEL_1, MyVisibilityLevel.VIS_LEVEL_1, MyFacilityCode.LEVEL_1); mylog.debug("This debug message is discarded since it does'nt match default Finer Values", MyDebugLevel.DEBUG_LEVEL_2, MyVisibilityLevel.VIS_LEVEL_1, MyFacilityCode.FAC_LEVEL_1); mylog.mergeDebugLevel(CommonDebugLevel.FULL_DEBUGGING); mylog.debug("This debug message is enabled since it the CommonLogger allows full debugging", MyDebugLevel.DEBUG_LEVEL_3, MyVisibilityLevel.VIS_LEVEL_1, MyFacilityCode.FAC_LEVEL_1); } }

Main Previous Next