4 メッセージのサブスクライブ
Oracle WebLogic Serverロギング・サービスでは、メッセージ・ハンドラを作成およびサブスクライブする機能を利用できます。WebLogic Serverメッセージ・カタログおよびNonCatalogLoggerでメッセージが生成されるとき、それらのメッセージはjava.util.logging.Logger
オブジェクトに配信されます。Loggerオブジェクトは、メッセージを説明するWLLogRecordオブジェクトを割り当て、Loggerをサブスクライブしているすべてのメッセージ・ハンドラにそのWLLogRecordをパブリッシュします。
WebLogic Serverのロガーとハンドラの詳細は、「ロガーとハンドラの役割」を参照してください。
メッセージ・ハンドラの概要
Logger
オブジェクトをサブスクライブするようにもできます(図4-1を参照)。
たとえば、アプリケーションがクライアントJVMで動作しており、そのアプリケーションがアプリケーションの生成するメッセージをリスニングするようにする場合は、ハンドラを作成し、そのハンドラをクライアントJVMのLogger
オブジェクトにサブスクライブすることができます。アプリケーションが特定サブシステムの障害を知らせるログ・メッセージを受信した場合、そのアプリケーションは以下のようなアクションを実行できます。
-
WebLogic Serverの管理者にログ・メッセージを電子メールで送信します。
-
自身またはそのサブシステムを終了または再起動します。
ノート:
ユーザー独自のメッセージ・ハンドラを作成する場合は、サーバーの初期化が完了して実行状態になる前に、WebLogic Serverプロセスで実行されるカスタム・コードを実行しないように注意します。場合によっては、初期化中のサーバー・サービスにカスタム・コードが干渉する場合があります。たとえば、IIOPサーバー・サービスの初期化の前に
PortableRemoteObject
を使用するアウトバウンドRMI呼出しを行うカスタム・ログ・ハンドラは、サーバーの起動が失敗する原因になる場合があります。
ハンドラの作成とサブスクライブ: 主なステップ
作成し、Logger
オブジェクトをサブスクライブさせたハンドラでは、そのLoggerの重大度とフィルタ基準を満たすすべてのメッセージが受信されます。ハンドラでは、Loggerがパブリッシュする特定のメッセージだけに応答するように重大度とフィルタ基準を追加で指定することもできます。
ハンドラを作成してサブスクライブさせるには:
-
以下の最小限のインポート文が含まれるハンドラ・クラスを作成します。
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;
-
ハンドラ・クラスで、
java.util.logging.Handler
を拡張します。 -
ハンドラ・クラスで、
Handler.publish(LogRecord record)
メソッドを実装します。このメソッドは次のように機能します。
-
受信した
LogRecord
オブジェクトをWLLogRecord
オブジェクトとしてキャストします。 -
ハンドラで設定されているフィルタを適用します。
-
WLLogRecord
オブジェクトがフィルタの基準を満たしている場合は、WLLogRecord
のメソッドを使用してメッセージからデータを取得します。 -
必要に応じて、メッセージのデータを1つまたは複数のリソースに書き込みます。
-
-
ハンドラ・クラスで、
Handler.flush
メソッドとHandler.close
メソッドを実装します。リソースと連係して機能するハンドラはすべて、
flush
メソッド(バッファされた出力をフラッシュするため)およびclose
メソッド(開いているリソースを閉じるため)を実装する必要があります。親
Logger
オブジェクトが閉じると、すべてのハンドラでHandler.close
メソッドが呼び出されます。closeメソッドは、flush
メソッドを呼び出してからそれ独自のロジックを実行します。 -
Handler
オブジェクトが受信するメッセージのタイプを指定するフィルタ・クラスを作成します。「ロガーとハンドラのフィルタの設定」を参照してください。 -
次のいずれかの
LoggingHelper
メソッドを呼び出すクラスを作成します。-
getClientLogger
(現在のコンテキストがクライアントJVMである場合)。 -
getServerLogger
(現在のコンテキストがサーバーJVMであり、サーバーのLogger
オブジェクトにハンドラをアタッチする必要がある場合)。 -
getDomainLogger
(現在のコンテキストが管理サーバーであり、ドメインのLogger
オブジェクトにハンドラをアタッチする必要がある場合)。LoggingHelper.getDomainLogger()
は、ドメイン・ログを管理するLogger
オブジェクトを取得します。カスタム・ハンドラにこのロガーをサブスクライブさせて、単一の場所にある全サーバーからのログ・メッセージを処理できます。
-
-
このクラスで、
Logger.addHandler(Handler myHandler)
メソッドを呼び出します。 -
省略可能です。
Logger.setFilter(Filter myFilter)
メソッドを呼び出してフィルタを設定します。
例: サーバーJVMでのメッセージのサブスクライブ
-
Handler
クラス。「例: ハンドラ・クラスの実装」を参照してください。 -
Filter
クラス。「ロガーとハンドラのフィルタの設定」を参照してください。 -
ハンドラとフィルタにサーバーの
Logger
クラスをサブスクライブさせるクラス。「例: ロガー・クラスのサブスクライブ」を参照してください。
例: ハンドラ・クラスの実装
例4-1のHandler
クラスの例では、次のようにしてデータベースにメッセージを書き込みます。
-
java.util.logging.Handler
を機能拡張します。 -
javax.naming.InitialContext
オブジェクトを作成し、Context.lookup
メソッドを呼び出してmyPoolDataSource
というデータ・ソースをルックアップします。 -
javax.sql.DataSource.getConnection
メソッドを呼び出して、データ・ソースとの接続を確立します。 -
setErrorManager
メソッドを実装します。このメソッドは、このハンドラのjava.util.logging.ErrorManager
オブジェクトを作成します。このハンドラでエラーが生じると、エラー・マネージャの
error
メソッドが呼び出されます。この例のerror
メソッドは次のように機能します。-
エラー・メッセージを標準エラーに出力します。
-
LoggingHelper.getServerLogger().removeHandler(MyJDBCHandler.this)
を呼び出してハンドラを無効にします。ノート:
独立したクラス・ファイルで
ErrorManager
クラスを定義するかわりに、この例ではErrorManager
が無名内部クラスとして内包されています。
エラー・マネージャの詳細は、
java.util.logging.ErrorManager
クラスのAPIドキュメント(http://docs.oracle.com/javase/8/docs/api/java/util/logging/ErrorManager.html
)を参照してください。 -
-
Handler.publish(LogRecord record)
メソッドを実装します。このメソッドは、次のことを実行します。-
受信した各
LogRecord
オブジェクトをWLLogRecord
オブジェクトとしてキャストします。 -
isLoggable
メソッドを呼び出して、ハンドラに設定されているフィルタを適用します。isLoggable
メソッドは、このハンドラ・クラスの最後に定義されています。 -
WLLogRecord
のメソッドを使用して、メッセージからデータを取得します。WLLogRecord
メソッドの詳細は、Oracle WebLogic Server Java APIリファレンスのweblogic.logging.WLLogRecord
クラスの説明を参照してください。 -
メッセージのデータをSQL
prepareStatement
としてフォーマットし、データベースの更新を実行します。この例で使用される表のスキーマは次のとおりです。
表4-1 サンプル・ハンドラのデータベース表のスキーマ
名前 Null? 型 MSGID
n/a
CHAR(25)
LOGLEVEL
n/a
CHAR(25)
SUBSYSTEM
n/a
CHAR(50)
MESSAGE
n/a
CHAR(1024)
-
-
flush
メソッドを呼び出して、接続をフラッシュします。 -
Handler.close
メソッドを実装して、データ・ソースとの接続を閉じます。親
Logger
オブジェクトが閉じると、Handler.close
メソッドが呼び出されます。このメソッドは、Handler.flush
メソッドを呼び出してからそれ独自のロジックを実行します。
例4-1に、この節で説明したステップを示します。
例4-1: ハンドラ・クラスの実装
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); } } }
例: ロガー・クラスのサブスクライブ
例4-2のLogger
クラスの例では、以下のことを行います。
-
LoggingHelper.getServerLogger
メソッドを呼び出して、Logger
オブジェクトを取得します。 -
Logger.addHandler(Handler myHandler)
メソッドを呼び出します。 -
Logger.getHandlers
メソッドを呼び出して、Logger
オブジェクトのすべてのハンドラを取得します。 -
myHandler
が見つかるまで配列を検索します。 -
Handler.setFilter(Filter myFilter)
メソッドを呼び出します。
サーバーが起動するたびにハンドラとフィルタがサーバーのLogger
オブジェクトをサブスクライブするようにする場合は、このクラスをWebLogic Server起動クラスとしてデプロイします。起動クラスの詳細は、Oracle WebLogic Server管理コンソール・オンライン・ヘルプのカスタム・クラスを使用したサーバーの構成に関する項を参照してください。
例4-2: ロガー・クラスのサブスクライブ
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(); } }
例: Log4jアペンダ・クラスの実装
append(LoggingEvent event)
メソッドを呼び出す必要があります。
ノート:
Javaロギングの代替としてWebLogicロギング・サービスでLog4jを使用することは、WebLogic Server 12.1.3では非推奨です。Appender
クラスの例では、JDBCデータ・ソースに接続し、メッセージをデータベース表に挿入するSQL文を発行します。
-
AppenderSkelton
を機能拡張します。 -
javax.naming.InitialContext
オブジェクトを作成し、Context.lookup
メソッドを呼び出してMyDataSource
というデータ・ソースをルックアップします。 -
javax.sql.DataSource.getConnection
メソッドを呼び出して、データ・ソースとの接続を確立します。 -
append(LoggingEvent event)
メソッドを実装します。このメソッドは、次のことを実行します。-
受信した各
LoggingEvent
オブジェクトをWLLog4jLogEvent
としてキャストします。 -
WLLog4jLogEvent
のメソッドを使用して、メッセージからデータを取得します。WLLog4jLogEvent
メソッドの詳細は、Oracle WebLogic Server Java APIリファレンスのweblogic.logging.log4j.WLLog4jLogEvent
クラスに関する説明を参照してください。 -
SQL
prepareStatement
を作成し、ロギング・イベントを受け取るたびにデータベースを更新します。この例で使用される表のスキーマは次のとおりです。
表4-2 サンプルLog4jアペンダのデータベース表のスキーマ
名前 Null? 型 SERVERNAME
n/a
CHAR(30)
MSGID
n/a
CHAR(20)
SEVERITYLEVEL
n/a
CHAR(20)
LOGGERNAME
n/a
CHAR
(100)MESSAGE
n/a
VARCHAR(2048)
TIMESTAMP
n/a
LONG
-
-
close
メソッドを実装して、データ・ソースとの接続を閉じます。
例4-3に、この節で説明したステップを示します。
例4-3: サンプルLog4jアペンダの起動
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(); } } } }
Javaロギング・ハンドラとJMXリスナーの比較
WebLogic Server 8.1より前のリリースで、WebLogicロギング・サービスからメッセージを受信するには、Java Management Extensions (JMX)リスナーを作成して、それをLogBroadcasterRuntimeMBean
に登録する方法しかありませんでした。WebLogic Server 8.1では、Javaロギング・ハンドラを使用してログ・メッセージを受信(サブスクライブ)することもできます。
Javaロギング・ハンドラでもJMXリスナーでも同様の結果が得られますが、JavaロギングAPIにはFormatter
クラスがあり、Handler
オブジェクトはこのクラスを使用して受信したメッセージをフォーマットできます。JMXでは、メッセージをフォーマットするこのようなAPIは提供されません。フォーマッタの詳細は、Formatter
クラスのAPIドキュメント(http://docs.oracle.com/javase/8/docs/api/java/util/logging/Formatter.html
)を参照してください。
さらに、JavaロギングHandler
APIはJMX APIよりも使いやすく、過程が直接的です。たとえば、次のコードはJavaロギングLogger
オブジェクトを取得し、ハンドラにそのオブジェクトをサブスクライブさせます。
Logger logger = LoggingHelper.getServerLogger(); Handler h = null; h = new MyJDBCHandler(); logger.addHandler(h)
JMXリスナーを登録して同様の結果を得るには、例4-4のようなコードを使用する必要があります。コードでMBeanHome
インタフェース、RemoteMBeanServer
インタフェース、およびLogBroadcasterRuntimeMBean
をルックアップし、それからリスナーを登録します。
ローカル・マシンのログ・メッセージのサブスクライブにはJavaロギング・ハンドラ、リモート・マシンからのログ・メッセージの受信にはJMXリスナーが適しています。すでにモニターにJMXを使用しており、ログ・メッセージのフォーマットを変更したりログ・メッセージを別の出力に転送したりせずに単純にリスニングする場合は、JMXリスナーを使用します。それ以外の場合は、Javaロギング・ハンドラを使用します。
例4-4 JMXリスナーの登録
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); } }