ヘッダーをスキップ
Oracle® Fusion Middleware Oracle WebLogic Server ログ ファイルのコンフィグレーションとログ メッセージのフィルタ処理
11g リリース 1 (10.3.1)
B55548-01
 

目次
目次

戻る
戻る
 
 

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 が無名内部クラスとして内包されています。

    エラー メッセージの詳細については、Sun API のドキュメントの java.util.logging.ErrorManager の項 (http://java.sun.com/javase/6/docs/api/index.html) を参照してください。

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

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

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

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

      WLLogRecord メソッドの詳細については、『Oracle Fusion Middleware Oracle WebLogic Server API Reference』の weblogic.logging.WLLogRecord クラスに関する説明を参照してください。

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

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

表 5-1 サンプル Handler のデータベース テーブルのスキーマ

名前 Null 可/不可
MSGID

なし

CHAR(25)
LOGLEVEL

なし

CHAR(25)
SUBSYSTEM

なし

CHAR(50)
MESSAGE

なし

CHAR(1024)

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

  2. 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());
              //このハンドラのインスタンスをすべて削除
              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 Fusion Middleware Oracle WebLogic Server の Administration Console オンライン ヘルプの「カスタム クラスを使用したサーバのコンフィグレーション」を参照してください。

コード リスト 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 Fusion Middleware Oracle WebLogic Server API Reference』の weblogic.logging.log4j.WLLog4jLogEvent クラスに関する説明を参照してください。

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

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

表 5-2 サンプル Log4j アペンダのデータベース テーブルのスキーマ

名前 Null 可/不可
SERVERNAME

なし

CHAR(30)
MSGID

なし

CHAR(20)
SEVERITYLEVEL

なし

CHAR(20)
LOGGERNAME

なし

CHAR(100)
MESSAGE

なし

VARCHAR(2048)
TIMESTAMP 

なし

LONG

  1. 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;
/**
 * このクラスにより、JDBC アペンダがログ イベントに対するサーバ ロガーの
 * リスナとしてコンフィグレーションされる
 */
public class Log4jAppenderExampleStartup {
  public static void main(String[] args) {
    try {
      System.out.println("Invoked the appender example startup class");
      Logger serverLogger = Log4jLoggingHelper.getLog4jServerLogger();
      // JDBC アペンダをコンフィグレーションする
      MyJDBCAppender jdbcAppender = new MyJDBCAppender();
      // ここで JDBC アペンダをサーバ ロガーに追加する
      serverLogger.addAppender(jdbcAppender);
      // ここでフィルタをテストする
      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();
      // テーブル スキーマ作成 SQL コマンド
      // 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);
        }
        // 実行メソッドをオーバーライドする
        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 は提供されません。フォーマッタの詳細については、Sun API のドキュメントの Formatter の項 (http://java.sun.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;
//ドメイン変数
String url = "t3://localhost:7001";
String serverName = "Server1";
String username = "weblogic";
String password = "weblogic";
//MBeanHome を使用して MBeanServer を取得try {
    Environment env = new Environment();
    env.setProviderUrl(url);
    env.setSecurityPrincipal(username);
    env.setSecurityCredentials(password);
    Context ctx = env.getInitialContext();
    //管理 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 {
    //リスナ クラスをインスタンス化MyListener listener = new MyListener();
    MyFilter filter = new MyFilter();
    //サーバのログ ブロードキャスタの WebLogicObjectName を
    //作成    WebLogicObjectName logBCOname = new
             WebLogicObjectName("TheLogBroadcaster",
           "LogBroadcasterRuntime", domainName, serverName);
    //MBean の名前とリスナ クラスを MBeanServer の
    //addNotificationListener メソッドに渡す
    rmbs.addNotificationListener(logBCOname, listener, filter, null);
    } catch(Exception e) {
        System.out.println("Exception: " + e);
    }
}