4 Subscribing to Messages
Oracle WebLogic Server logging services provides the ability to create and subscribe a message handler. 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.
For more information about WebLogic Server loggers and handlers, see The Role of Logger and Handler Objects.
Overview of Message Handlers
Logger objects (see Figure 4-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.
Note:
When creating your own message handlers, be careful to avoid executing custom code which runs in the WebLogic Server process before the server initialization has completed and the server has come to a running state. In some cases, custom code can interfere with server services which are being initialized. For example, custom log handlers that make an outbound RMI call which use the
PortableRemoteObjectbefore the IIOP server service is initialized, can cause server startup to fail.
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
LogRecordobjects that it receives asWLLogRecordobjects. -
Applies any filters that have been set for the handler.
-
If the
WLLogRecordobject satisfies the criteria of any filters, the method usesWLLogRecordmethods to retrieve data from the messages. -
Optionally writes the message data to one or more resources.
-
-
In the handler class, implement the
Handler.flushandHandler.closemethods.All handlers that work with resources should implement the
flushmethod so that it flushes any buffered output and theclosemethod so that it closes any open resources.When the parent
Loggerobject shuts down, it calls theHandler.closemethod on all of its handlers. The close method calls theflushmethod and then executes its own logic. -
Create a filter class that specifies which types of messages your
Handlerobject should receive. See Setting a Filter for Loggers and Handlers. -
Create a class that invokes one of the following
LoggingHelpermethods:-
getClientLoggerif the current context is a client JVM. -
getServerLoggerif the current context is a server JVM and you want to attach a handler to the serverLoggerobject. -
getDomainLoggerif the current context is the Administration Server and you want to attach a handler to the domainLoggerobject.LoggingHelper.getDomainLogger()retrieves theLoggerobject that manages the domain log. You can subscribe a custom handler to this logger and process log messages from all the servers in a single location.
-
-
In this class, invoke the
Logger.addHandler(Handler myHandler)method. -
Optional. Invoke the
Logger.setFilter(Filter myFilter)method to set a filter.
Example: Subscribing to Messages in a Server JVM
-
A
Handlerclass. See Example: Implementing a Handler Class. -
A
Filterclass. See Setting a Filter for Loggers and Handlers. -
A class that subscribes the handler and filter to a server's
Loggerclass. See Example: Subscribing to a Logger Class.
Example: Implementing a Handler Class
The example Handler class in Example 4-1 writes messages to a database by doing the following:
-
Extends
java.util.logging.Handler. -
Constructs a
javax.naming.InitialContextobject and invokes theContext.lookupmethod to look up a data source namedmyPoolDataSource. -
Invokes the
javax.sql.DataSource.getConnectionmethod to establish a connection with the data source. -
Implements the
setErrorManagermethod, which constructs ajava.util.logging.ErrorManagerobject for this handler.If this handler encounters any error, it invokes the error manager's
errormethod. Theerrormethod 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
ErrorManagerclass in a separate class file, the example includes theErrorManageras an anonymous inner class.
For more information about error managers, see the API documentation for the
java.util.logging.ErrorManagerclass athttp://docs.oracle.com/javase/8/docs/api/java/util/logging/ErrorManager.html. -
-
Implements the
Handler.publish(LogRecord record)method. The method does the following:-
Casts each
LogRecordobject that it receives as aWLLogRecordobjects. -
Calls an
isLoggablemethod to apply any filters that are set for the handler. TheisLoggablemethod is defined at the end of this handler class. -
Uses
WLLogRecordmethods to retrieve data from the messages.For more information about
WLLogRecordmethods, see the description of theweblogic.logging.WLLogRecordclass in Java API Reference for Oracle WebLogic Server. -
Formats the message data as a SQL
prepareStatementand executes the database update.The schema for the table used in the example is as follows:
Table 4-1 Schema for Database Table in Handler Example
Name Null? Type MSGIDn/a
CHAR(25)LOGLEVELn/a
CHAR(25)SUBSYSTEMn/a
CHAR(50)MESSAGEn/a
CHAR(1024) -
-
Invokes a
flushmethod to flush the connection. -
Implements the
Handler.closemethod to close the connection with the data source.When the parent
Loggerobject shuts down, it calls theHandler.closemethod, which calls theHandler.flushmethod before executing its own logic.
Example 4-1 illustrates the steps described in this section.
Example 4-1 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;
private PreparedStatement stmt = null;
public MyJDBCHandler() throws NamingException, SQLException {
InitialContext ctx = new InitialContext();
DataSource ds = (DataSource)ctx.lookup("myPoolDataSource");
con = ds.getConnection();
PreparedStatement stmt = con.prepareStatement
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 {
("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 Logger class in Example 4-2 does the following:
-
Invokes the
LoggingHelper.getServerLoggermethod to retrieve theLoggerobject. -
Invokes the
Logger.addHandler(Handler myHandler)method. -
Invokes the
Logger.getHandlersmethod to retrieve all handlers of theLoggerobject. -
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, see Use custom classes to configure servers in the Oracle WebLogic Server Administration Console Online Help.
Example 4-2 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();
}
}Example: Implementing a Log4j Appender Class
append(LoggingEvent event)method.
Note:
The use of Log4j with the WebLogic logging service, as an alternative to Java logging, is deprecated as of WebLogic Server 12.1.3.Appender class in Example 4-3 connects to a JDBC data source and issues SQL statements that insert messages into a database table:
-
Extends
AppenderSkelton. -
Constructs a
javax.naming.InitialContextobject and invokes theContext.lookupmethod to look up a data source namedMyDataSource. -
Invokes the
javax.sql.DataSource.getConnectionmethod to establish a connection with the data source. -
Implements the
append(LoggingEvent event)method. The method does the following:-
Casts each
LoggingEventobject that it receives as aWLLog4jLogEvent. -
Uses
WLLog4jLogEventmethods to retrieve data from the messages.For more information about
WLLog4jLogEventmethods, see the description of theweblogic.logging.log4j.WLLog4jLogEventclass in Java API Reference for Oracle WebLogic Server. -
Creates a SQL
prepareStatementand executes the database update whenever a logging event arrives.The schema for the table used in the example is as follows:
Table 4-2 Schema for Database Table in Log4j Appender Example
Name Null? Type SERVERNAMEn/a
CHAR(30)MSGIDn/a
CHAR(20)SEVERITYLEVELn/a
CHAR(20)LOGGERNAMEn/a
CHAR(100)MESSAGEn/a
VARCHAR(2048)TIMESTAMPn/a
LONG -
-
Implements the
closemethod to close the connection with the data source.
Example 4-3 illustrates the steps described in this section.
Example 4-3 Log4j Appender Examples Startup
package weblogic.logging.examples;
import java.util.Enumeration;
import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.PropertyConfigurator;
import org.apache.log4j.Logger;
import org.apache.log4j.spi.Filter;
import org.apache.log4j.spi.LoggingEvent;
import weblogic.logging.LoggerNotAvailableException;
import weblogic.logging.NonCatalogLogger;
import weblogic.logging.Severities;
import weblogic.logging.log4j.AppenderNames;
import weblogic.logging.log4j.Log4jLoggingHelper;
import weblogic.logging.log4j.WLLog4jLevel;
import weblogic.logging.log4j.WLLog4jLogEvent;
import org.apache.log4j.jdbc.JDBCAppender;
import java.sql.Connection;
import java.sql.SQLException;
import javax.naming.InitialContext;
import weblogic.logging.log4j.WLLog4jLogEvent;
import weblogic.logging.Severities;
/**
* This class sets up a Log4j Appender as a listener to the
* Server Logger for log events.
*/
public class Log4jAppenderExampleStartup {
public static void main(String[] args) {
try {
System.out.println("Invoked the appender example startup class");
Logger serverLogger = Log4jLoggingHelper.getLog4jServerLogger();
// Configure the JDBC appender
MyJDBCAppender jdbcAppender = new MyJDBCAppender();
// Now add the JDBC appender to the server logger
serverLogger.addAppender(jdbcAppender);
// Now test the filter
NonCatalogLogger nc = new NonCatalogLogger("MyAppenderTest");
nc.info("Test INFO message");
nc.warning("Test WARNING message");
} catch(Exception ex) {
System.err.println("Init failure " + ex.getMessage());
ex.printStackTrace();
}
}
private static class MyJDBCAppender extends AppenderSkeleton {
private Connection connection;
private java.sql.PreparedStatement stmt;
public MyJDBCAppender() throws javax.naming.NamingException, SQLException {
InitialContext ctx = new InitialContext();
javax.sql.DataSource ds
= (javax.sql.DataSource) ctx.lookup ("MyDataSource");
connection = ds.getConnection();
// Table schema creation SQL command
// Create table SERVER_LOG (server_name char(30),msg_id char(20),
// severity_level char(20),logger_name char(100),message varchar(2048),
// timestamp long);
stmt = connection.prepareStatement("INSERT INTO SERVER_LOG VALUES (?, ?, ?, ?, ?, ?)");
stmt.setEscapeProcessing(true);
connection.setAutoCommit(true);
}
// Override execute method
public void append(LoggingEvent event) {
WLLog4jLogEvent wlsEvent = (WLLog4jLogEvent) event;
try {
stmt.setString(1, wlsEvent.getServerName());
stmt.setString(2, wlsEvent.getId());
stmt.setString(3, Severities.severityNumToString(wlsEvent.getSeverity()));
stmt.setString(4, wlsEvent.getSubsystem());
stmt.setString(5, wlsEvent.getMessage().toString());
stmt.setLong(6, wlsEvent.getTimestamp());
stmt.executeUpdate();
} catch (SQLException e) {
System.err.println(e.toString());
}
}
public boolean requiresLayout() {
return false;
}
public void close() {
try {
stmt.close();
connection.close();
} catch(SQLException sqlex) {
System.err.println("Error closing JDBC appender");
sqlex.printStackTrace();
}
}
}
}Comparison of Java Logging 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 also use Java Logging handlers to receive (subscribe to) log messages.
While both techniques - Java Logging handlers and JMX listeners - provide similar results, the Java Logging 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, see the API documentation for the Formatter class at http://docs.oracle.com/javase/8/docs/api/java/util/logging/Formatter.html.
In addition, the Java Logging Handler APIs are easier to use and require fewer levels of indirection than JMX APIs. For example, the following lines of code retrieve a Java Logging 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, you must use lines of code similar to Example 4-4. The code looks up the MBeanHome interface, looks up the RemoteMBeanServer interface, looks up the LogBroadcasterRuntimeMBean, and then registers the listener.
Optimally, you would use Java Logging handlers to subscribe to log messages on your local machine and JMX listeners to receive log messages from a remote machine. If you are already using JMX for monitoring and you simply want to listen for log messages, not to change their formatting or reroute them to some other output, use JMX listeners. Otherwise, use the Java Logging handlers.
Example 4-4 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
//log broadcaster.
WebLogicObjectName logBCOname = new
WebLogicObjectName("TheLogBroadcaster",
"LogBroadcasterRuntime", domainName, 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);
}
}