bea.com | products | dev2dev | support | askBEA

Download Docs | Site Map | Glossary
Search

Developing Adapters

Using the Logging Toolkit

Logging is an essential feature of an adapter component. Most adapters are used to integrate different applications; they do not interact with end users while data is being processed. Unlike a front-end component, when an adapter encounters an error or warning condition, it cannot stop processing and wait for an end-user to respond.

With the ADK, you can log adapter activity by implementing a logging framework. This framework gives you the ability to log internationalized and localized messages to multiple output destinations. It provides a range of configuration parameters you can use to tailor message category, priority, format, and destination.

This section contains information about the following subjects:

- Logging Toolkit

- Logging Configuration File

- Logging Concepts

- How to Set Up Logging

- Logging Framework Classes

- Internationalization and Localization of Log Messages

- Saving Contextual Information in a Multithreaded Component

Logging Toolkit

The ADK logging toolkit allows you to log internationalized messages to multiple output destinations. The logging toolkit leverages the work of the Apache Log4j open source project. This product includes software developed by the Apache Software Foundation (http://www.apache.org).

The logging toolkit is a framework that wraps the necessary Log4j classes to provide added functionality for J2EE-compliant adapters. It is provided in the logtoolkit.jar file under WLI_HOME/lib. This JAR file depends on DOM, XERCES, and Log4j. The XERCES dependency is satisfied by the weblogic.jar and xmlx.jar files provided with WebLogic Server. WebLogic Integration provides the required version of Log4j, log4j.jar, in WLI_HOME/lib.

The Log4j package is distributed under the Apache public license, a full-fledged open source license certified by the open source initiative. The latest Log4j version, including full-source code, class files, and documentation, can be found at the Apache Log4j Web site (http://www.apache.org).

Logging Configuration File

Throughout this section, you will see references to and code excerpts from the logging configuration file. This file is an .xml file that is identified by the adapter logical name, such as BEA_WLS_DBMS_ADK.xml. It contains the base information for the four logging concepts discussed in Logging Concepts and can be modified for your specific adapter.

The ADK provides a basic logging configuration file, BEA_WLS_SAMPLE_ADK.xml, in WLI_HOME/adapters/sample/src. To modify this file for your adapter, run GenerateAdapterTemplate. This utility customizes the sample version of the logging configuration file with information pertinent to your new adapter and places the customized version in the new adapter's development environment. For more information about GenerateAdapterTemplate, see Creating a Custom Development Environment.

Logging Concepts

Before using the logging toolkit provided with the ADK, you should understand a few key concepts of the logging framework. Logging has four main components:

- Message Categories

- Message Priority

- Message Appenders

- Message Layout

These components work together to enable you to log messages according to message type and priority, and to control, at run time, how these messages are formatted and where they are reported.

Message Categories

Categories identify log messages according to criteria you define and are a central concept of the logging framework. In the ADK, a category is identified by its name, such as BEA_WLS_SAMPLE_ADK.DesignTime.

Categories are hierarchically defined and any category can inherit properties from a parent category. The hierarchy is defined as follows:

- A category is an ancestor of another category if its name, followed by a dot, is a prefix of the descendant category name.

- A category is a parent of a child category if there are no ancestors between itself and the descendant category.

For example, BEA_WLS_SAMPLE_ADK.DesignTime is a descendant of BEA_WLS_SAMPLE_ADK which, in turn, is a descendant of the root category, as shown in the following diagram.

>     ROOT CATEGORY
>       |
>       |->BEA_WLS_SAMPLE_ADK
>           |
>           |->BEA_WLA_SAMPLE.ADK.DesignTime

The root category resides at the top of the hierarchy; it cannot be deleted or retrieved by name.

When you create categories, you should name them according to components in the adapter to which they belong. For example, if an adapter has a design-time user interface component, the adapter might have a category with the following name: BEA_WLS_SAMPLE_ADK.DesignTime.

Message Priority

Every message has a priority that indicates its importance. Message priority is determined by the ILogger interface method used to log the message. For example, if you call the debug method on an ILogger instance, a debug message is generated.

The logging toolkit supports five possible priorities for a given message. These priorities are listed, in descending order of importance, in Table 5-1.

Table 5-1 Logging Toolkit Priorities

Priority

Indicates

AUDIT

An extremely important log message related to the business processing performed by an adapter. Messages with this priority are always written to the log.

ERROR

An error in the adapter. Error messages are internationalized and localized for the user.

WARN

A situation that is not an error, but that might cause problems in the adapter. Warning messages are internationalized and localized for the user.

INFO

An informational message that is internationalized and localized for the user.

DEBUG

A debug message, that is, information used to determine how the internals of a component are working. Debug messages are typically not internationalized.

The BEA_WLS_SAMPLE_ADK category has priority WARN because of the following child element:

>     <priority value='WARN' class='com.bea.logging.LogPriority'/>

The class for the priority must be com.bea.logging.LogPriority.

Assigning a Priority to a Category

You can assign a priority to a category. If a given category is not assigned a priority, it inherits one from its closest ancestor with an assigned priority; that is, the inherited priority for a given category is equal to the first non-null priority above the given category in the hierarchy.

A log message is sent to the log destination if its priority is higher than or equal to the priority of its category. Otherwise, the message is not written to the log. A category without an assigned priority inherits one from the hierarchy. To ensure that all categories can eventually inherit a priority, the root category always has an assigned priority. A log statement of priority p, in a category with inherited priority q, is enabled if p >= q. This rule is based on the assumption that priorities are ordered as follows: DEBUG < INFO < WARN < ERROR < AUDIT.

Message Appenders

The logging framework allows an adapter to log messages to multiple destinations by using an interface called an appender. Log4j provides appenders for:

- Console

- Files

- Remote socket servers

- NT event loggers

- Remote UNIX Syslog daemons

In addition, the ADK logging toolkit provides an appender that you can invoke to send a log message to your WebLogic Server log.

A category may refer to multiple appenders. Each enabled logging request for a given category is forwarded to all the appenders in that category, as well as all the appenders higher in the hierarchy. In other words, appenders are inherited cumulatively from the category hierarchy.

For example, if a console appender is added to the root category, then all enabled logging requests are displayed, at a minimum, on the console. If, in addition, a file appender is added to category C, then enabled logging requests for C and C's children are printed in a file and displayed on the console. It is possible to override this default behavior (that is, to stop appender inheritance from being cumulative) by setting the additivity flag to false.

Note: If you also add the console appender directly to C, you get two messages—one from C and one from root—on the console. The root category always logs to the console.

Listing 5-1 shows an appender for the WebLogic Server log.

Listing 5-1 Sample Code Showing an Appender for the WebLogic Server Log

>     <!--
>       A WeblogicAppender sends log output to the Weblogic log. If running outside of
>       WebLogic, the appender writes messages to System.out
>       -->
>       <appender name="WebLogicAppender"
>          class="com.bea.logging.WeblogicAppender"/>
>     </appender>

Message Layout

Log4j enables you to customize the format of a log message by associating a layout with an appender. The layout determines the format of a log message, while an appender directs the formatted message to its destination. The logging toolkit typically uses PatternLayout to format its log messages. PatternLayout, part of the standard Log4j distribution, lets you specify the output format according to conversion patterns similar to the C language printf function.

For example, if you invoke PatternLayout with the conversion pattern %-5p%d{DATE} %c{4} %x - %m%n, a message such as the following is generated:

>     AUDIT 21 May 2001 11:00:57,109 BEA_WLS_SAMPLE_ADK - admin opened connection to EIS

In this conversion pattern:

- The value of %-5p is the priority of the message; in the example shown here, the priority is AUDIT.

- The value of %d{DATE} is the date of the message; in the example shown here, the date is 21 May 2001 11:00:57,109.

- The value of %c{4} is the category for the log message; in the example shown here, the category is BEA_WLS_SAMPLE_ADK.

The text after the dash (-) is the message of the statement.

Putting the Components Together

Listing 5-2 declares a new category for the sample adapter, assigns a priority to the new category, and declares an appender in order to specify the type of file to which log messages should be sent.

Listing 5-2 Sample XML Code for Declaring a New Log Category

>     <!-

>     IMPORTANT!!! ROOT Category for the adapter; making this unique prevents other
>      adapters from logging to your category
>     -->

>     <category name='BEA_WLS_SAMPLE_ADK' class='com.bea.logging.LogCategory'>
>       <!-
>         Default Priority Level; may be changed at runtime
>         DEBUG means log all messages from the adapter's code base
>         INFO means log informationals, warnings, errors, and audits
>         WARN means log warnings, errors, and audits
>         ERROR means log errors and audits
>         AUDIT means log audits only
>       -->

>         <priority value='WARN' class='com.bea.logging.LogPriority'/>
>        <appender-ref ref='WebLogicAppender'/>

>     </category>

Note: You must specify the class as com.bea.logging.LogCategory.

How to Set Up Logging

Note: The following procedure is based on the assumption that you have cloned a development environment by running the GenerateAdapterTemplate utility. For more information about this utility, see Creating a Custom Development Environment.

To set up the logging framework for your adapter:

1. Identify all the basic components used in the adapter. For example, if your adapter has an EventGenerator, you might want an EventGenerator component; if it supports a design-time GUI, you need a design-time component.

2. Open the base log configuration file from the cloned adapter. This file is found in WLI_HOME/adapters/ADAPTER/src/. Its name includes the .xml extension. For example, the DBMS sample adapter configuration file is WLI_HOME/adapters/dbms/src/BEA_WLS_DBMS_ADK.xml.

3. In the base log configuration file, add the category elements for all adapter components you identified in step 1. For each category element, establish a priority. Listing 5-3 shows how a category for an EventGenerator with a priority of DEBUG is added.

Listing 5-3 Sample Code for Adding an EventGenerator Log Category with a Priority of DEBUG

>     <category name='BEA_WLS_DBMS_ADK.EventGenerator'
>                 class='com.bea.logging.LogCategory'>
>         <priority value='DEBUG'
>                   class='com.bea.logging.LogPriority'/>
>     </category>

1. Determine which appender is needed and specify it in the configuration file. If necessary, add message formatting information. Listing 5-4 shows how a basic file appender is added within the <appender> element. Instructions within the <layout> element identify the message format.

   Note: By default, WebLogicAppender is used in all sample adapters provided by WebLogic Integration 7.0.

Listing 5-4 Sample Code for Adding a File Appender and Layout Pattern

>     <!-- A basic file appender -->

>     <appender name='FileAppender'
>        class='org.apache.Log4j.FileAppender'>

>         <!-- Send output to a file -->

>         <param name='File' value='BEA_WLS_DBMS_ADK.log'/>

>         <!-- Truncate existing -->

>         <param name="Append" value="true"/>

>         <!-- Use a basic LOG4J pattern layout -->

>         <layout class='org.apache.Log4j.PatternLayout'>
>            <param name='ConversionPattern' value='%-5p %d{DATE} %c{4}
>              %x - %m%n'/>
>         </layout>

>     </appender>

At this point, you should check the setting in the following configuration files:

- WLI_HOME/adapters/ADAPTER/src/eventrouter/web-inf/web.xml—The AbstractEventGenerator uses the logging information entered in the base configuration file to configure the log framework at initialization time.

- WLI_HOME/adapters/ADAPTER/src/rar/META-INF/ra.xml and weblogic-ra.xml—The AbstractManagedConnectionFactory uses the logging information entered in the base configuration file to configure the log framework at initialization time.

- WLI_HOME/adapters/ADAPTER/src/war/web-inf/web.xml—The RequestHandler (the parent of AbstractDesignTimeRequestHandler) uses the logging information entered in the base configuration file to configure the log framework at initialization time.

In the preceding paths, ADAPTER represents the name of your adapter. For example, the name of the DBMS sample adapter appears in the pathname for the associated configuration file, as follows:

WLI_HOME/adapters/dbms/src/rar/META-INF/ra.xml

Logging Framework Classes

In addition to understanding the basic concepts of the logging framework, you also need to understand the three main classes provided in the logging toolkit:

- com.bea.logging.ILogger

- com.bea.logging.LogContext

- com.bea.logging.LogManager

com.bea.logging.ILogger

This class is the main interface to the logging framework. It provides numerous convenience methods for logging messages.

The How to Set Up Logging procedure explains how you can configure logging in the base log configuration file. You can also configure logging programmatically by implementing the following logging methods:

- logger.setPriority("DEBUG") changes the minimum priority of messages printed from the current ILogger.

- logger.addRuntimeDestination (writer) adds the appender that is used when the container passes its PrintWriter to the adapter.

- logger.warn("Some message", true) logs a message with the priority level of WARN, without using the ResourceBundle. The boolean indicates that the string is a message, not a key.

- logger.warn("someKey") logs a message with the priority level WARN, by looking it up with "someKey" in ResourceBundle.

- logger.info("someKey", anObjArray) logs a message with the priority level of INFO by looking up a template with someKey in ResourceBundle and filling in the blanks with the elements of anObjArray.

- logger.error(exception) logs a message with the priority level of ERROR, by passing an exception (Throwable) to this method. It calls getMessage() and includes a stack trace. (All logging methods that take a Throwable as an argument log a stack trace.)

com.bea.logging.LogContext

This class encapsulates the information needed to identify an ILogger instance in the logging framework. Currently, the LogContext class encapsulates a log category name and a locale, such as en_US. This class is the primary key for uniquely identifying an ILogger instance in the log manager.

com.bea.logging.LogManager

This class provides a method that allows you to configure the logging framework and gain access to ILogger instances.

To ensure that you can properly configure the logging toolkit for your adapter, the ADK implements the LogManager's configure() method with the arguments shown in Listing 5-5.

Listing 5-5 Sample Code for Configuring the Logging Toolkit

>     public static LogContext
>        configure(String strLogConfigFile,
>                  String strRootLogContext,
>                  String strMessageBundleBase,
>                  Locale locale,
>                   ClassLoader classLoader)

Table 5-2 describes the arguments passed by configure().

Table 5-2 configure() Arguments

Argument

Description

strLogConfigFile

File that contains the log configuration information for your adapter. The file's location should be included in the classpath. We recommend that you include this file in your adapter's main JAR file so that it can be included in the WAR and RAR files for your adapter. This file should conform to the Log4j.dtd. The Log4j.dtd file is provided in the Log4j.jarfile provided with WebLogic Integration.

strRootLogContext

Name of the logical root of the category hierarchy for your adapter. For the sample adapter, its value is BEA_WLS_SAMPLE_ADK.

strMessageBundleBase

Base name of the message bundles for your adapter. The ADK requires the use of message bundles. For the sample adapter, its value is BEA_WLS_SAMPLE_ADK.

locale

Nation and language of the users. The logging toolkit organizes categories into different hierarchies, based on locale. For example, if your adapter supports two locales, en_US and fr_CA, the logging toolkit maintains two hierarchies: one for en_US and one for fr_CA.

classLoader

ClassLoader that should be used by the LogManager to load resources, such as ResourceBundles and log configuration files.

Once the configuration is complete, you can retrieve ILogger instances for your adapter by supplying a LogContext object.

Listing 5-6 Sample Code for Supplying a LogContext Object

>     LogContext logContext = new LogContext("BEA_WLS_SAMPLE_ADK",   java.util.Locale.US);

>     ILogger logger = LogManager.getLogger(logContext); logger.debug("I'm logging now!");

The ADK hides most of the log configuration and setup from you. The com.bea.adapter.spi.AbstractManagedConnectionFactory class configures the logging toolkit for service adapters and the AbstractEventGenerator configures the logging toolkit for event adapters. In addition, all of the Client Connector Interface (CCI) and Service Provider Interface (SPI) base classes included in the ADK provide access to an ILogger and the LogContext associated with it.

An adapter may also include layers that support the CCI/SPI layer, such as a socket layer used for establishing communication with the EIS. To make it possible for such adapters to access the correct ILogger object, you can take either of two approaches:

- The CCI/SPI layers can pass the LogContext object into the lower layers. This method works, but it adds overhead.

- The CCI layer can establish the LogContext for the current running thread at the earliest possible place in the code. The ADK's com.bea.adapter.cci.ConnectionFactoryImpl class sets the LogContext for the current running thread in the getConnection() methods. The getConnection() methods are the first point of contact between a client program and your adapter. Consequently, lower layers in an adapter can safely access the LogContext for the current running thread by using the following code:

Listing 5-7 Code Accessing LogContext for the Current Thread

>     public static LogContext getLogContext(Thread t)
>        throws IllegalStateException, IllegalArgumentException

Additionally, we supply the following convenience method on LogManager:

>     public static ILogger getLogger() throws IllegalStateException

This method provides an ILogger for the current running thread. There is one caveat to using this approach: lower layers should not store LogContext or ILogger as members. Rather, they should dynamically retrieve them from LogManager. An IllegalStateException is thrown if this method is called before a LogContext is set for the current running thread.

Internationalization and Localization of Log Messages

Internationalization (I18N) and localization (L10N) are central concepts to the ADK logging framework. All logging convenience methods on the ILogger interface, except the debug methods, allow I18N. The implementation follows the Java Internationalization standards, using ResourceBundle objects to store locale-specific messages or templates. Sun Microsystems provides a good online tutorial on using the I18N and L10N standards of the Java language.

Saving Contextual Information in a Multithreaded Component

Most real-world systems must manage multiple clients simultaneously. In a typical multithreaded implementation of such a system, different threads handle different clients. Logging is especially well suited to tracing and debugging complex distributed applications. A common way of differentiating between the logging output of two clients is to instantiate a separate category for each client. This approach has a drawback however: categories proliferate and the overhead required to manage them increases.

A lighter technique is to stamp each log request initiated from the same client interaction with a unique identifier. Neil Harrison describes this method in "Patterns for Logging Diagnostic Messages" in Pattern Languages of Program Design 3, edited by R. Martin, D. Riehle, and F. Buschmann (Addison-Wesley, 1997).

To stamp each request with a unique identifier, the user pushes contextual information into the Nested Diagnostic Context (NDC). The logging toolkit provides a separate interface for accessing NDC methods. The interface is retrieved from the ILogger by using the getNDCInterface() method.

NDC printing is turned on in the XML configuration file (with the symbol %x). Every time a log request is made, the appropriate logging framework component includes the entire NDC stack for the current thread in the log output. The user does not need to intervene in this process. In fact, the user is responsible only for placing the correct information in the NDC by using the push and pop methods at a few well-defined points in the code.

Listing 5-8 Sample Code

>     public void someAdapterMethod(String aClient) {
>         ILogger logger = getLogger();
>       INestedDiagnosticContext ndc = logger.getNDCInterface();
>       // I'm keeping track of this client name for all log messages
>       ndc.push("User name=" + aClient);
>       // method body
>       ndc.pop();
>       }

A good place to use the NDC is in your adapter's CCI Interaction object.