ヘッダーをスキップ
Oracle® Fusion Middleware Oracle WebLogic Serverログ・ファイルの構成とログ・メッセージのフィルタ処理
11g リリース1 (10.3.6)
B61637-05
  ドキュメント・ライブラリへ移動
ライブラリ
製品リストへ移動
製品
目次へ移動
目次

前
 
 

5 メッセージのサブスクライブ

WebLogic Serverメッセージ・カタログおよびNonCatalogLoggerでメッセージが生成されるとき、それらのメッセージはjava.util.logging.Loggerオブジェクトに配信されます。Loggerオブジェクトは、メッセージを説明するWLLogRecordオブジェクトを割り当て、Loggerをサブスクライブしているすべてのメッセージ・ハンドラにそのWLLogRecordをパブリッシュします。

以下の節では、メッセージ・ハンドラの作成とサブスクライブについて説明します。

WebLogic Serverのロガーおよびハンドラの詳細は、「ロガーとハンドラの役割」を参照してください。

メッセージ・ハンドラの概要

WebLogic Serverは、ログ・メッセージを受信して出力するメッセージ・ハンドラをインスタンス化し、サブスクライブさせます。独自のメッセージ・ハンドラを作成して、WebLogic ServerのLoggerオブジェクトをサブスクライブするようにもできます(図5-1を参照)。

図5-1 ハンドラのサブスクライブ

図5-1の説明が続きます
「図5-1 ハンドラのサブスクライブ」の説明

たとえば、アプリケーションがクライアントJVMで動作しており、そのアプリケーションがアプリケーションの生成するメッセージをリスニングするようにする場合は、ハンドラを作成し、そのハンドラをクライアントJVMのLoggerオブジェクトにサブスクライブすることができます。アプリケーションが特定サブシステムの障害を知らせるログ・メッセージを受信した場合、そのアプリケーションは以下のようなアクションを実行できます。

ハンドラの作成とサブスクライブ: 主な手順

作成し、Loggerオブジェクトをサブスクライブさせたハンドラでは、そのLoggerの重大度とフィルタ基準を満たすすべてのメッセージが受信されます。ハンドラでは、Loggerがパブリッシュする特定のメッセージだけに応答するように重大度とフィルタ基準を追加で指定することもできます。

ハンドラを作成してサブスクライブさせるには:

  1. 以下の最小限のインポート文が含まれるハンドラ・クラスを作成します。

    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;
    
  2. ハンドラ・クラスで、java.util.logging.Handlerを拡張します。

  3. ハンドラ・クラスで、Handler.publish(LogRecord record)メソッドを実装します。

    このメソッドは次のように機能します。

    1. 受信したLogRecordオブジェクトをWLLogRecordオブジェクトとしてキャストします。

    2. ハンドラで設定されているフィルタを適用します。

    3. WLLogRecordオブジェクトがフィルタの基準を満たしている場合は、WLLogRecordのメソッドを使用してメッセージからデータを取得します。

    4. 必要に応じて、メッセージのデータを1つまたは複数のリソースに書き込みます。

  4. ハンドラ・クラスで、Handler.flushメソッドとHandler.closeメソッドを実装します。

    リソースと連係して機能するハンドラはすべて、flushメソッド(バッファされた出力をフラッシュするため)およびcloseメソッド(開いているリソースを閉じるため)を実装する必要があります。

    Loggerオブジェクトが閉じると、すべてのハンドラでHandler.closeメソッドが呼び出されます。closeメソッドは、flushメソッドを呼び出してからそれ独自のロジックを実行します。

  5. Handlerオブジェクトが受信するメッセージのタイプを指定するフィルタ・クラスを作成します。「ロガーおよびハンドラのフィルタの設定」を参照してください。

  6. 次のいずれかのLoggingHelperメソッドを呼び出すクラスを作成します。

    • getClientLogger (現在のコンテキストがクライアントJVMである場合)。

    • getServerLogger (現在のコンテキストがサーバーJVMであり、サーバーのLoggerオブジェクトにハンドラをアタッチする必要がある場合)。

    • getDomainLogger (現在のコンテキストが管理サーバーであり、ドメインのLoggerオブジェクトにハンドラをアタッチする必要がある場合)。

      LoggingHelper.getDomainLogger()は、ドメイン・ログを管理するLoggerオブジェクトを取得します。カスタム・ハンドラにこのロガーをサブスクライブさせて、単一の場所にある全サーバーからのログ・メッセージを処理できます。

  7. このクラスで、Logger.addHandler(Handler myHandler)メソッドを呼び出します。

  8. 省略可能です。Logger.setFilter(Filter myFilter)メソッドを呼び出してフィルタを設定します。

例: サーバーJVMでのメッセージのサブスクライブ

この例では、JDBCデータ・ソースに接続し、メッセージをデータベース表に挿入するSQL文を発行するハンドラを作成します。この例では、以下のクラスを実装します。

例: ハンドラ・クラスの実装

例5-1Handlerクラスの例では、次のようにしてデータベースにメッセージを書き込みます。

  1. java.util.logging.Handlerを拡張します。

  2. javax.naming.InitialContextオブジェクトを作成し、Context.lookupメソッドを呼び出してmyPoolDataSourceというデータ・ソースをルックアップします。

  3. javax.sql.DataSource.getConnectionメソッドを呼び出して、データ・ソースとの接続を確立します。

  4. setErrorManagerメソッドを実装します。このメソッドは、このハンドラのjava.util.logging.ErrorManagerオブジェクトを作成します。

    このハンドラでエラーが生じると、エラー・マネージャのerrorメソッドが呼び出されます。この例のerrorメソッドは次のように機能します。

    1. エラー・メッセージを標準エラーに出力します。

    2. LoggingHelper.getServerLogger().removeHandler(MyJDBCHandler.this)を呼び出してハンドラを無効にします。


      注意:

      独立したクラス・ファイルでErrorManagerクラスを定義するかわりに、この例ではErrorManagerが無名内部クラスとして内包されています。


    エラー・マネージャの詳細は、java.util.logging.ErrorManagerクラスのAPIドキュメント(http://docs.oracle.com/javase/6/docs/api/java/util/logging/ErrorManager.html)を参照してください。

  5. Handler.publish(LogRecord record)メソッドを実装します。このメソッドは、次のことを実行します。

    1. 受信した各LogRecordオブジェクトをWLLogRecordオブジェクトとしてキャストします。

    2. isLoggableメソッドを呼び出して、ハンドラに設定されているフィルタを適用します。isLoggableメソッドは、このハンドラ・クラスの最後に定義されています。

    3. WLLogRecordのメソッドを使用して、メッセージからデータを取得します。

      WLLogRecordメソッドの詳細は、Oracle WebLogic Server APIリファレンスweblogic.logging.WLLogRecordクラスに関する説明を参照してください。

    4. メッセージのデータをSQL prepareStatementとしてフォーマットし、データベースの更新を実行します。

      この例で使用される表のスキーマは次のとおりです。

    表5-1 サンプル・ハンドラのデータベース表のスキーマ

    名前 Null?
    MSGID
    

    n/a

    CHAR(25)
    
    LOGLEVEL
    

    n/a

    CHAR(25)
    
    SUBSYSTEM
    

    n/a

    CHAR(50)
    
    MESSAGE
    

    n/a

    CHAR(1024)
    

  6. flushメソッドを呼び出して、接続をフラッシュします。

  7. Handler.closeメソッドを実装して、データ・ソースとの接続を閉じます。

    Loggerオブジェクトが閉じると、Handler.closeメソッドが呼び出されます。このメソッドは、Handler.flushメソッドを呼び出してからそれ独自のロジックを実行します。

例5-1に、この節で説明した手順を示します。

例5-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);
      }
   }
}

例: ロガー・クラスのサブスクライブ

例5-2Loggerクラスの例では、以下のことを行います。

  1. LoggingHelper.getServerLoggerメソッドを呼び出して、Loggerオブジェクトを取得します。

  2. Logger.addHandler(Handler myHandler)メソッドを呼び出します。

  3. Logger.getHandlersメソッドを呼び出して、Loggerオブジェクトのすべてのハンドラを取得します。

  4. myHandlerが見つかるまで配列を検索します。

  5. Handler.setFilter(Filter myFilter)メソッドを呼び出します。

サーバーが起動するたびにハンドラとフィルタがサーバーのLoggerオブジェクトをサブスクライブするようにする場合は、このクラスをWebLogic Server起動クラスとしてデプロイします。起動クラスの詳細は、Oracle WebLogic Server管理コンソール・オンライン・ヘルプカスタム・クラスを使用したサーバーの構成に関する項を参照してください。

例5-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アペンダ・クラスの実装

例5-3Appenderクラスの例では、JDBCデータ・ソースに接続し、メッセージをデータベース表に挿入するSQL文を発行します。

  1. AppenderSkeltonを拡張します。

  2. javax.naming.InitialContextオブジェクトを作成し、Context.lookupメソッドを呼び出してMyDataSourceというデータ・ソースをルックアップします。

  3. javax.sql.DataSource.getConnectionメソッドを呼び出して、データ・ソースとの接続を確立します。

  4. append(LoggingEvent event)メソッドを実装します。このメソッドは、次のことを実行します。

    1. 受信した各LoggingEventオブジェクトをWLLog4jLogEventとしてキャストします。

    2. WLLog4jLogEventのメソッドを使用して、メッセージからデータを取得します。

      WLLog4jLogEventメソッドの詳細は、Oracle WebLogic Server APIリファレンスweblogic.logging.log4j.WLLog4jLogEventクラスに関する説明を参照してください。

    3. SQL prepareStatementを作成し、ロギング・イベントを受け取るたびにデータベースを更新します。

      この例で使用される表のスキーマは次のとおりです。

    表5-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
    

  5. closeメソッドを実装して、データ・ソースとの接続を閉じます。

例5-3に、この節で説明した手順を示します。

例5-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/6/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リスナーを登録して同様の結果を得るには、例5-4のようなコードを使用する必要があります。コードでMBeanHomeインタフェース、RemoteMBeanServerインタフェース、およびLogBroadcasterRuntimeMBeanをルックアップし、それからリスナーを登録します。

ローカル・マシンのログ・メッセージのサブスクライブにはJavaロギング・ハンドラ、リモート・マシンからのログ・メッセージの受信にはJMXリスナーが適しています。すでにモニターにJMXを使用しており、ログ・メッセージのフォーマットを変更したりログ・メッセージを別の出力に転送したりせずに単純にリスニングする場合は、JMXリスナーを使用します。それ以外の場合は、Javaロギング・ハンドラを使用します。

例5-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);
    }
}