ログ ファイルのコンフィグレーションとログ メッセージのフィルタ処理

     前  次    新しいウィンドウで目次を開く     
ここから内容の開始

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

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

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

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

 


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

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

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

ハンドラのサブスクライブ


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

注意 : ユーザ独自のメッセージ ハンドラを作成する場合は、サーバの初期化が完了して実行状態になる前に、WebLogic Server プロセスで実行されるカスタム コードを実行しないように注意します。場合によっては、初期化中のサーバ サービスにカスタム コードが干渉する場合があります。たとえば、IIOP サーバ サービスの初期化の前に PortableRemoteObject を使用する発信 RMI 呼び出しを行うカスタム ログ ハンドラは、サーバの起動が失敗する原因になる場合があります。

 


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

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

ハンドラを作成してサブスクライブさせるには、次の手順に従います。

  1. 以下の最小限のインポート文が含まれるハンドラ クラスを作成します。
  2. 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;

  3. ハンドラ クラスで、java.util.logging.Handler を拡張します。
  4. ハンドラ クラスで、Handler.publish(LogRecord record) メソッドを実装します。
  5. このメソッドは次のように機能します。

    1. 受信した LogRecord オブジェクトを WLLogRecord オブジェクトとしてキャストします。
    2. ハンドラで設定されているフィルタを適用します。
    3. WLLogRecord オブジェクトがフィルタの基準を満たしている場合は、WLLogRecord のメソッドを使用してメッセージからデータを取得します。
    4. 必要に応じて、メッセージのデータを 1 つまたは複数のリソースに書き込みます。
  6. ハンドラ クラスで、Handler.flush メソッドと Handler.close メソッドを実装します。
  7. リソースと連係して機能するハンドラはすべて、flush メソッド (バッファされた出力をフラッシュするため) および close メソッド (開いているリソースを閉じるため) を実装する必要があります。

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

  8. Handler オブジェクトが受信するメッセージのタイプを指定するフィルタ クラスを作成します。「ロガーとハンドラのフィルタの設定」を参照してください。
  9. 以下の LoggingHelper メソッドのいずれかを呼び出すクラスを作成します。
    • getClientLogger (現在のコンテキストがクライアント JVM である場合)。
    • getServerLogger (現在のコンテキストがサーバ JVM であり、サーバの Logger オブジェクトにハンドラをアタッチする必要がある場合)。
    • getDomainLogger (現在のコンテキストが管理サーバであり、ドメインの Logger オブジェクトにハンドラをアタッチする必要がある場合)。
    • LoggingHelper.getDomainLogger() は、ドメイン ログを管理する Logger オブジェクトを取得します。カスタム ハンドラにこのロガーをサブスクライブさせて、単一の場所にある全サーバからのログ メッセージを処理できます。

  10. このクラスで、Logger.addHandler(Handler myHandler) メソッドを呼び出します。
  11. 省略可能。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 オブジェクトを作成します。
  5. このハンドラでエラーが生じると、エラー マネージャの error メソッドが呼び出されます。この例の error メソッドは次のように機能します。

    1. エラー メッセージを標準エラーに出力します。
    2. LoggingHelper.getServerLogger().removeHandler(MyJDBCHandler.this) を呼び出してハンドラを無効にします。
    3. 注意 : 独立したクラス ファイルで ErrorManager クラスを定義する代わりに、この例では ErrorManager が無名内部クラスとして内包されています。

      エラー マネージャの詳細については、Sun API のドキュメントで java.util.logging.ErrorManager を参照してください。

  6. Handler.publish(LogRecord record) メソッドを実装します。このメソッドは、次のことを実行します。
    1. 受信した各 LogRecord オブジェクトを WLLogRecord オブジェクトとしてキャストします。
    2. isLoggable メソッドを呼び出して、ハンドラに設定されているフィルタを適用します。isLoggable メソッドは、このハンドラ クラスの最後に定義されています。
    3. WLLogRecord のメソッドを使用して、メッセージからデータを取得します。
    4. WLLogRecord メソッドの詳細については、WLLogRecordJavadoc を参照してください。

    5. メッセージのデータを SQL prepareStatement としてフォーマットし、データベースの更新を実行します。
    6. この例で使用されるテーブルのスキーマは次のとおりです。

      表 5-1 Handler 例のデータベース テーブルのスキーマ
      名前
      Null 可/不可
      MSGID
       
      CHAR(25)
      LOGLEVEL
       
      CHAR(25)
      SUBSYSTEM
       
      CHAR(50)
      MESSAGE
       
      CHAR(1024)

    7. flush メソッドを呼び出して、接続をフラッシュします。
  7. Handler.close メソッドを実装して、データ ソースとの接続を閉じます。
  8. 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 起動クラスとしてデプロイします。起動クラスについては、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 のメソッドを使用して、メッセージからデータを取得します。
    3. WLLog4jLogEvent メソッドの詳細については、WLLog4jLogEventJavadoc を参照してください。

    4. SQL prepareStatement を作成し、ロギング イベントを受け取るたびにデータベースを更新します。
    5. この例で使用されるテーブルのスキーマは次のとおりです。

      表 5-2 サンプル Log4j アペンダのデータベース テーブルのスキーマ
      名前
      Null 可/不可
      SERVERNAME
       
      CHAR(30)
      MSGID
       
      CHAR(20)
      SEVERITYLEVEL
       
      CHAR(20)
      LOGGERNAME
       
      CHAR(100)
      MESSAGE
       
      VARCHAR(2048)
      TIMESTAMP
       
      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;
/**
 * このクラスは、ログ イベントに対するサーバ ロガーのリスナとして
 * Log4j アペンダを設定する
 */
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/j2se/1.5.0/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);
    }
}

ページの先頭       前  次