Subscribing to Messages
When WebLogic Server message catalogs and the NonCatalogLogger generate messages, they distribute their messages to a java.util.logging.Logger object. The Logger object allocates a WLLogRecord object to describe the message and publishes the WLLogRecord to any message handler that has subscribed to the Logger.
WebLogic Server instantiates and subscribes a set of messages handlers that receive and print log messages. You can also create your own message handlers and subscribe them to the WebLogic Server Logger objects (see Figure 5-1).
For example, if your application runs in a client JVM and you want the application to listen for the messages that your application generates, you can create a handler and subscribe it to the Logger object in the client JVM. If your application receives a log message that signals the failure of a specific subsystem, it can perform actions such as:
- E-mail the log message to the WebLogic Server administrator.
- Shut down or restart itself or its subcomponents.
The following sections describe creating and subscribing a message handler:
- Creating and Subscribing a Handler: Main Steps
- Example: Subscribing to Messages in a Server JVM
- Comparison of JDK 1.4 Handlers with JMX Listeners
For more information about WebLogic Server loggers and handlers, refer to Overview of Distributing and Filtering Messages.
Figure 5-1 Subscribing a Handler
Creating and Subscribing a Handler: Main Steps
A handler that you create and subscribe to a Logger object receives all messages that satisfy the level and filter criteria of the logger. Your handler can specify additional level and filter criteria so that it responds only to a specific set of messages that the logger publishes.
To create and subscribe a handler:
- Create a handler class that includes the following minimal set of import statements:
import java.util.logging.Handler;
import java.util.logging.LogRecord;
import java.util.logging.ErrorManager;import weblogic.logging.WLLogRecord;
import weblogic.logging.WLLevel;
import weblogic.logging.WLErrorManager;
import weblogic.logging.LoggingHelper;- In the handler class, extend java.util.logging.Handler.
- In the handler class, implement the Handler.publish(LogRecord record) method.
This method:
- Casts the LogRecord objects that it receives as WLLogRecord objects.
- Applies any filters that have been set for the handler.
- If the WLLogRecord object satisfies the criteria of any filters, the method uses WLLogRecord methods to retrieve data from the messages.
- Optionally writes the message data to one or more resources.
- In the handler class, implement the Handler.flush and Handler.close methods.
All handlers that work with resources should implement the flush method so that it flushes any buffered output and the close method so that it closes any open resources.
When the parent Logger object shuts down, it calls the Handler.close method on all of its handlers. The close method calls the flush method and then executes its own logic.
- Create a filter class that specifies which types of messages your Handler object should receive. For more information, refer to Setting a Filter for Loggers and Handlers.
- Place the handler and filter objects in the classpath of the JVM on which the Logger object is running.
- Create a class that invokes one of the following LoggingHelper methods:
- getClientLogger if the current context is a client JVM.
- getServerLogger if the current context is a server JVM and you want to filter the Logger object that a server uses to manage its local server log.
- getDomainLogger if the current context is the Administration Server and you want to filter the Logger object that manages the domain server log.
- In this class, invoke the Logger.addHandler(Handler myHandler) method. Then invoke the Logger.setFilter(Filter myFilter) method.
Example: Subscribing to Messages in a Server JVM
This example creates a handler that connects to a JDBC data source and issues SQL statements that insert messages into a database table. The example implements the following classes:
- A Handler class. See Example: Implementing a Handler Class.
- A Filter class. See Setting a Filter for Loggers and Handlers.
- A class that subscribes the handler and filter to a server's Logger class. See Example: Subscribing to a Logger Class.
Example: Implementing a Handler Class
The example Handler class in Listing 5-1 writes messages to a database by doing the following:
- Extends java.util.logging.Handler.
- Constructs a javax.naming.InitialContext object and invokes the Context.lookup method to look up a data source named myPoolDataSource.
- Invokes the javax.sql.DataSource.getConnection method to establish a connection with the data source.
- Implements the setErrorManager method, which constructs a java.util.logging.ErrorManager object for this handler.
If this handler encounters any error, it invokes the error manager's error method. The error method in this example:
- Prints an error message to standard error.
- Disables the handler by invoking LoggingHelper.getServerLogger().removeHandler(MyJDBCHandler.this).
Note: Instead of defining the ErrorManager class in a separate class file, the example includes the ErrorManager as an anonymous inner class.
For more information about error managers, refer to the Sun API documentation for java.util.logging.ErrorManager.
- Implements the Handler.publish(LogRecord record) method. The method does the following:
- Casts each LogRecord object that it receives as a WLLogRecord objects.
- Calls an isLoggable method to apply any filters that are set for the handler. The isLoggable method is defined at the end of this handler class.
- Uses WLLogRecord methods to retrieve data from the messages.
For more information about WLLogRecord methods, refer to the WLLogRecord Javadoc.
- Formats the message data as a SQL prepareStatement and executes the database update.
The schema for the table used in the example is as follows:
Name
Null?
Type
MSGID
CHAR(25) LOGLEVEL
CHAR(25) SUBSYSTEM
CHAR(50) MESSAGE
CHAR(1024)
- Invokes a flush method to flush the connection.
- Implements the Handler.close method to close the connection with the data source.
When the parent Logger object shuts down, it calls the Handler.close method, which calls the Handler.flush method before executing its own logic.
Listing 5-1 Example: Implementing a Handler Class
import java.util.logging.Handler;
import java.util.logging.LogRecord;
import java.util.logging.Filter;
import java.util.logging.ErrorManager;import weblogic.logging.WLLogRecord;
import weblogic.logging.WLLevel;
import weblogic.logging.WLErrorManager;
import javax.naming.InitialContext;
import javax.naming.NamingException;import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.PreparedStatement;
import weblogic.logging.LoggingHelper;public class MyJDBCHandler extends Handler {private Connection con = null;public MyJDBCHandler() throws NamingException, SQLException {InitialContext ctx = new InitialContext();
DataSource ds = (DataSource)ctx.lookup("myPoolDataSource");
con = ds.getConnection();
setErrorManager(new ErrorManager() {
public void error(String msg, Exception ex, int code) {
System.err.println("Error reported by MyJDBCHandler "
+ msg + ex.getMessage());
//Removing any prior istantiation of this handler
LoggingHelper.getServerLogger().removeHandler(
MyJDBCHandler.this);
}
});
}public void publish(LogRecord record) {
WLLogRecord rec = (WLLogRecord)record;
if (!isLoggable(rec)) return;
try {
PreparedStatement stmt = con.prepareStatement(
"INSERT INTO myserverLog VALUES (?, ?, ? ,?)");
stmt.setEscapeProcessing(true);
stmt.setString(1, rec.getId());
stmt.setString(2, rec.getLevel().getLocalizedName());
stmt.setString(3, rec.getLoggerName());
stmt.setString(4, rec.getMessage());
stmt.executeUpdate();
flush();
} catch(SQLException sqex) {
reportError("Error publihsing to SQL", sqex,
ErrorManager.WRITE_FAILURE);
}
}public void flush() {
try {
con.commit();
} catch(SQLException sqex) {
reportError("Error flushing connection of MyJDBCHandler",
sqex, ErrorManager.FLUSH_FAILURE);
}
}public boolean isLoggable(LogRecord record) {
Filter filter = getFilter();
if (filter != null) {
return filter.isLoggable(record);
} else {
return true;
}
}
public void close() {
try {
con.close();
} catch(SQLException sqex) {
reportError("Error closing connection of MyJDBCHandler",
sqex, ErrorManager.CLOSE_FAILURE);
}
}}
Example: Subscribing to a Logger Class
The example class in Listing 5-2 does the following:
- Invokes the LoggingHelper.getServerLogger method to retrieve the Logger object.
- Invokes the Logger.addHandler(Handler myHandler) method.
- Invokes the Logger.getHandlers method to retrieve all handlers of the Logger object.
- Iterates through the array until it finds myHandler.
- Invokes the Handler.setFilter(Filter myFilter) method.
If you wanted your handler and filter to subscribe to the server's Logger object each time the server starts, you could deploy this class as a WebLogic Server startup class. For information about startup classes, refer to "Startup and Shutdown Classes in the Administration Console Online Help.
Listing 5-2 Example: Subscribing to a Logger Class
import java.util.logging.Logger;
import java.util.logging.Handler;
import java.util.logging.Filter;
import java.util.logging.LogRecord;
import weblogic.logging.LoggingHelper;
import weblogic.logging.FileStreamHandler;
import weblogic.logging.WLLogRecord;
import weblogic.logging.WLLevel;
import java.rmi.RemoteException;
import weblogic.jndi.Environment;import javax.naming.Context;public class LogConfigImpl {public void configureLogger() throws RemoteException {
Logger logger = LoggingHelper.getServerLogger();
try {
Handler h = null;
h = new MyJDBCHandler();
logger.addHandler(h);
h.setFilter(new MyFilter());
} catch(Exception nmex) {
System.err.println("Error adding MyJDBCHandler to logger "
+ nmex.getMessage());
logger.removeHandler(h);
}
}public static void main(String[] argv) throws Exception {
LogConfigImpl impl = new LogConfigImpl();
impl.configureLogger();
}}
Comparison of JDK 1.4 Handlers with JMX Listeners
Prior to WebLogic Server 8.1, the only technique for receiving messages from the WebLogic logging services was to create a Java Management Extensions (JMX) listener and register it with a LogBroadcasterRuntimeMBean. With the release of WebLogic Server 8.1, you can now use JDK 1.4 handlers to receive (subscribe to) log messages.
While both techniques - JDK 1.4 handlers and JMX listeners - provide similar results, the JDK 1.4 APIs include a Formatter class that a Handler object can use to format the messages that it receives. JMX does not offer similar APIs for formatting messages. For more information about formatters, refer to the Sun API documentation for Formatter: http://java.sun.com/j2se/1.4/docs/api/java/util/logging/Formatter.html.
In addition, the JDK 1.4 Handler APIs are easier to use and require fewer levels of indirection than JMX APIs. For example, the following lines of code retrieve a JDK 1.4 Logger object and subscribe a handler to it:
Logger logger = LoggingHelper.getServerLogger();
Handler h = null;
h = new MyJDBCHandler();
logger.addHandler(h)To achieve a similar result by registering a JMX listener, use lines of code similar to Listing 5-3. The code looks up the MBeanHome interface, looks up the RemoteMBeanServer interface, looks up the LogBroadcasterRuntimeMBean, and then registers the listener.
For information on using JMX listeners, refer to "Using WebLogic Server MBean Notifications and Monitors in Programming WebLogic Management Services with JMX.
Listing 5-3 Registering a JMX Listener
MBeanHome home = null;
RemoteMBeanServer rmbs = null;//domain variables
String url = "t3://localhost:7001";
String serverName = "Server1";
String username = "weblogic";
String password = "weblogic";//Using MBeanHome to get MBeanServer.
try {
Environment env = new Environment();
env.setProviderUrl(url);
env.setSecurityPrincipal(username);
env.setSecurityCredentials(password);
Context ctx = env.getInitialContext();//Getting the Administration MBeanHome.
home = (MBeanHome) ctx.lookup(MBeanHome.ADMIN_JNDI_NAME);
System.out.println("Got the Admin MBeanHome: " + home );
rmbs = home.getMBeanServer();
} catch (Exception e) {
System.out.println("Caught exception: " + e);
}try {
//Instantiating your listener class.
MyListener listener = new MyListener();
MyFilter filter = new MyFilter();//Construct the WebLogicObjectName of the server's LogBroadcasterRuntimeMBean.WebLogicObjectName logBCOname = new
WebLogicObjectName("WebLogicLogBroadcaster",
"LogBroadcasterRuntime", myDomain, serverName);
//Passing the name of the MBean and your listener class to the
//addNotificationListener method of MBeanServer.
rmbs.addNotificationListener(logBCOname, listener, filter, null);
} catch(Exception e) {
System.out.println("Exception: " + e);
}
}