原文はこちら。
https://blogs.oracle.com/WebLogicServer/entry/dynamic_debug_patches_in_weblogic
加えて、このようなデバッグパッチをデプロイすることに伴う別の重要な問題が発生することがあります。Java EE環境でパッチを適用すると、サーバーおよびドメインの再起動もしくは少なくともアプリケーションの再デプロイが必要です。ミッションクリティカルな環境では、すぐにパッチ適用ができない可能性があります。サーバを再起動すると、状態が失われるため、メモリ内の重要な障害データが失われる可能性があります。また、サーバの再起動後、断続的な障害が長時間現れず、迅速な診断が困難になる場合があります。
もう一つ、サーバの起動コマンド内で以下のオプションを使ってdebugpatch インストルメンテーション・エージェントとともにサーバを起動する必要がありますが、これはWebLogic Server 12.2.1ドメイン用に作成した起動スクリプトで自動的に追加されます。
ビルドしたWebアプリケーションをブラウザから http://localhost:7001/factorial/factorial?n=4 のような感じで呼び出します。ブラウザに結果が表示され、サーバの標準出力ウィンドウには以下のような文字が現れるはずです。
まず、デバッグパッチがドメインで利用可能かどうかを確認しましょう。これはlistDebugPatchesコマンドを使って実現できます。
では、activateDebugPatchコマンドを使ってデバッグパッチを有効化しましょう。
それではリクエストをアプリケーションに送信しましょう。
しかしながら、問題は、新しいデバッグコードが時として状態を維持する必要があるかもしれないということです。例えば、Mapのデータを収集し、ある閾値でダンプ出力したい、という場合があるとしましょう。形状変化に関するJDKの制限により、こうした場合に問題が発生します。Dynamic Debug Patch機能はDebugPatchHelperユーティリティクラスを提供しており、このクラスがそうした問題解決の手助けになります。後続の記事でこの内容を説明しますので、ご期待ください。
https://blogs.oracle.com/WebLogicServer/entry/dynamic_debug_patches_in_weblogic
Introduction
好むと好まざるとに関わらず、完全なソフトウェアはありません。開発者の最善の努力にもかかわらずバグが発生しますし、さらに悪いことに、多くの状況で、予期しない形で現れます。また、あるケースでは再現することが難しく、断続的に発生することがあります。このような場合では、製品が十分に根本的な原因を明らかにするための機能が備わっていないと、問題の本質を理解するための情報が不足することが多々あります。お客様の本番環境への直接アクセスは通常は選択できず、根本的な原因をより理解するために、デバッグ用パッチを作成し、そのパッチを適用したアプリケーションを実行して、願わくばより多くの洞察が得られることを期待するわけですが、これはトライ&エラー方式で、本当の原因に到達するまでに何度かの繰り返し作業になる可能性があります。デバッグパッチを作成する人たち(通常はソフトウェアプロバイダのサポートや開発チームです)やアプリケーションを使うお客様はほぼ異なるグループで、別の会社に属していることが多々あります。そのため、デバッグパッチの作成、お客様へのパッチの提供、顧客環境へのパッチ適用、結果の取得・返送という繰り返し作業の各々でかなりの時間がかかる可能性があります。結果として、問題解決に時間がかかります。加えて、このようなデバッグパッチをデプロイすることに伴う別の重要な問題が発生することがあります。Java EE環境でパッチを適用すると、サーバーおよびドメインの再起動もしくは少なくともアプリケーションの再デプロイが必要です。ミッションクリティカルな環境では、すぐにパッチ適用ができない可能性があります。サーバを再起動すると、状態が失われるため、メモリ内の重要な障害データが失われる可能性があります。また、サーバの再起動後、断続的な障害が長時間現れず、迅速な診断が困難になる場合があります。
Dynamic Debug Patches
WebLogic Server 12.2.1では、Dynamic Debug Patchesと呼ばれる新しい機能が導入されました。これは迅速な問題解決のため、診断データを取得するプロセスを簡素化することを目的としています。Oracle® Fusion Middleware Configuring and Using the Diagnostics Framework for Oracle WebLogic Server 12c (12.2.1)この機能を使用すると、デバッグパッチを動的にアクティベートできます。このときにサーバまたはクラスタの再起動や、WebLogicドメインへのアプリケーションの再デプロイは不要です。これは、JDKのインスツルメンテーション機能を利用し、ランタイムWLSTコマンドを使用して、指定されたデバッグパッチからクラスをホット・スワップします。
Using Debug Patches
http://docs.oracle.com/middleware/1221/wls/WLDFC/using_debug_patches.htm#WLDFC585
java.lang.instrument Interface InstrumentationWLSTコマンド(後述)を発行して、一つ以上のデバッグパッチを選択されたサーバ、クラスタ、パーティションおよびアプリケーションの範囲内でアクティベートできます。サーバの再起動やアプリケーションの再デプロイが不要なので、関連したロジスティックの障害は問題にはなりません。一例としては、アプリケーションやサービスは実行し続けるので、こうしたパッチを本番環境で有効にすることの障壁が低くなります。また、状態の損失もありません。したがって、新たにアクティベートされたデバッグパッチのインストルメントコードが新しくアクティブ化されたデバッグパッチでインストルメントコードがまずい過渡状態を明らかにし、意味のある診断情報を提供するチャンスが増えます。
http://docs.oracle.com/javase/7/docs/api/java/lang/instrument/Instrumentation.htmlhttp://docs.oracle.com/javase/jp/7/api/java/lang/instrument/Instrumentation.html
Prerequisites
Dynamic Debug Patchは、デバッグログやステートメントの表示などといった追加のインストルメントコードを持つパッチ適用済みのクラスを含んだ通常のjarファイルです。通常は製品開発もしくはサポートチームがこれらのパッチjarファイルを作成し、システム運用チームが現場でのアクティベーションのために利用できるようにします。WebLogic ServerのDynamic Debug Patch機能を利用できるようにするには、システム管理者がドメイン内の特定のディレクトリにコピーする必要があります。デフォルトでは、このディレクトリは、ドメインルートの下のdebug_patchesというディレクトリですが、DebugPatchesMBeanのDebugPatchDirectory属性を再設定することで変更できます。もう一つ、サーバの起動コマンド内で以下のオプションを使ってdebugpatch インストルメンテーション・エージェントとともにサーバを起動する必要がありますが、これはWebLogic Server 12.2.1ドメイン用に作成した起動スクリプトで自動的に追加されます。
-javaagent:${WL_HOME}/server/lib/debugpatch-agent.jar
Using Dynamic Debug Patches Feature
シンプルなアプリケーションでデバッグパッチを有効化・無効化しながら、この機能の使い方を説明します。The Application
最小限のWebアプリケーションを使います。これは入力された整数の階乗値を計算してブラウザに表示するというものです。ServletはFactorialシングルトンに階乗値を計算させています。Factorialクラスは以前計算した値のMapを最適化のため保持しています。これはDynamic Debug Patchを有効化・無効化しながら、ステートフルな情報を保持していることの説明として役立つでしょう。FactorialServlet.java:
package example;
import java.io.IOException;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;
import java.util.Map;
import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;
/**
* A trivial servlet: Returns addition of two numbers.
*/
@WebServlet(value="/factorial", name="factorial-servlet")
public class FactorialServlet extends GenericServlet {
public void service(ServletRequest request, ServletResponse response)
throws ServletException, IOException {
String n = request.getParameter("n");
System.out.println("FactorialServlet called for input=" + n);
int result = Factorial.getInstance().factorial(n);
response.getWriter().print("factorial(" + n + ") = " + result);
}
}
Factorial.java:
package example;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
class Factorial {
private static final Factorial SINGLETON = new Factorial();
private Map<String, Integer> map = new ConcurrentHashMap<String, Integer>();
static Factorial getInstance() {
return SINGLETON;
}
public int factorial(String n) {
if (n == null) {
throw new NumberFormatException("Invalid argument: " + n);
}
n = n.trim();
Integer val = map.get(n);
if (val == null) {
int i = Integer.parseInt(n);
if (i < 0)
throw new NumberFormatException("Invalid argument: " + n);
int fact = 1;
while (i > 0) {
fact *= i;
i--;
}
val = new Integer(fact);
map.put(n, val);
}
return val;
}
}
Building and Deploying the Application
factorial.war Webアプリケーションをビルドするために、FactorialServlet.javaとFactorial.javaファイルを上述のように空のディレクトリに作成し、以下のコマンドを使ってアプリケーションwarファイルをビルドします。WLSTもしくはWebLogic Server管理コンソールを使ってアプリケーションをデプロイします。mkdir -p WEB-INF/classes
javac -d WEB-INF/classes FactorialServlet.java Factorial.java
jar cvf factorial.war WEB-INF
上の説明では、アプリケーションを管理サーバにデプロイしていることにご注意ください。実際の現場では別の管理対象サーバもしくはクラスタにデプロイされていることがあります。複数の管理対象サーバ全体に対してデバッグパッチを有効化・無効化する方法は後で説明します。$MW_HOME/oracle_common/common/bin/wlst.sh
Initializing WebLogic Scripting Tool (WLST) ...
Welcome to WebLogic Server Administration Scripting Shell
Type help() for help on available commands
connect(username, password, adminUrl) # e.g. connect('weblogic', 'weblogic', 't3://localhost:7001')
Connecting to t3://localhost:7001 with userid weblogic ...
Successfully connected to Admin Server "myserver" that belongs to domain "mydomain".
Warning: An insecure protocol was used to connect to the server.
To ensure on-the-wire security, the SSL port or Admin port should be used instead.
deploy('factorial', 'factorial.war', targets='myserver')
ビルドしたWebアプリケーションをブラウザから http://localhost:7001/factorial/factorial?n=4 のような感じで呼び出します。ブラウザに結果が表示され、サーバの標準出力ウィンドウには以下のような文字が現れるはずです。
FactorialServlet called for input=4
The Debug Patch
先ほど作成したアプリケーションはたくさんのログを出力しないので、機能がわかりづらくなっています。おそらく、そこに問題があり、このアプリケーションを実行した場合、より多くの情報が必要です。アプリケーションコードからデバッグパッチを作成し、システム管理者に提供して、実行中のサーバー/アプリケーション上でそれを有効化することができます。上記コードを変更し、追加情報を取得するため追加のprint文を入れましょう(以下のMYDEBUGが付いている行)。デバッグパッチをビルドしますが、これはプレーンなjarファイルでありアプリケーションアーカイブとしてビルドしないことにご注意ください。また、(毀損することはないでしょうが)アプリケーション全体をコンパイルする必要はないことにご注意ください。デバッグパッチjarファイルには変更されたクラス(この場合、Factorialクラス)のみが含まれる必要があります。Updated (version 1) Factorial.java:
class Factorial {
private static final Factorial SINGLETON = new Factorial();
private Map<String, Integer> map = new ConcurrentHashMap<String, Integer>();
static Factorial getInstance() {
return SINGLETON;
}
public int factorial(String n) {
if (n == null) {
throw new NumberFormatException("Invalid argument: " + n);
}
n = n.trim();
Integer val = map.get(n);
if (val == null) {
int i = Integer.parseInt(n);
if (i < 0)
throw new NumberFormatException("Invalid argument: " + n);
int fact = 1;
while (i > 0) {
fact *= i;
i--;
}
val = new Integer(fact);
System.out.println("MYDEBUG> saving factorial(" + n + ") = " + val);
map.put(n, val);
} else {
System.out.println("MYDEBUG> returning saved factorial(" + n + ") = " + val);
}
return val;
}
}
mkdir patch_classes
javac -d patch_classes Factorial.java
jar cvf factorial_debug_01.jar -C patch_classes
Activating Debug Patches
現実のシナリオでは、作成者(開発者)とデバッグパッチのアクティベータ(システム管理者)が異なることがほとんどでしょう。説明のために、今回は同一人物が複数の役割を持つことにします。デバッグパッチディレクトリの場所としてデフォルト設定を利用する前提としますが、まだ存在しない場合は、<Domain_Home>の下にdebug_patchesディレクトリを作成してください。debug_patchesディレクトリにfactorial_debug_01.jarというデバッグパッチのjarファイルをコピーします。上記のようにWLSTを使用してサーバに接続します。まず、デバッグパッチがドメインで利用可能かどうかを確認しましょう。これはlistDebugPatchesコマンドを使って実現できます。
Oracle® Fusion Middleware WLST Command Reference for WebLogic Server 12c (12.2.1)[ヒント]利用可能な診断コマンドを確認するには、 help('diagnostics') とコマンドを発行します。特定コマンドの情報を知るためには、help(コマンド名) を発行します。例えば、 help('activateDebugPatch') という感じです。
listDebugPatches
http://docs.oracle.com/middleware/1221/wls/WLSTC/reference.htm#WLSTC3694
factorial_debug_01.jar は新規作成されたデバッグパッチです。app2.0_patch01.jar と app2.0_patch02.jar は過去に別のアプリケーションでの問題を調査するために作成されたものでした。上記のリストはこれまでどれも有効化されなかったので「アクティブ」なパッチはありません。wls:/mydomain/serverConfig/> listDebugPatches()
myserver:
Active Patches:
Available Patches:
factorial_debug_01.jar
app2.0_patch01.jar
app2.0_patch02.jar
では、activateDebugPatchコマンドを使ってデバッグパッチを有効化しましょう。
Oracle® Fusion Middleware WLST Command Reference for WebLogic Server 12c (12.2.1)
activateDebugPatch
http://docs.oracle.com/middleware/1221/wls/WLSTC/reference.htm#WLSTC3670
コマンドは、起動コマンドの進行状況を監視するために使用できるタスクの配列を返します。該当する場合は、複数の管理対象サーバやクラスタをターゲットとして指定することができます。該当する各ターゲットサーバに対応して、返されたタスクの配列中にタスクがあります。コマンドを使ってサーバやミドルウェアレベルでデバッグパッチを有効化することもできます。このようなパッチを通常Oracle Supportが作成することもあるでしょう。上記の listDebugPatches() コマンドの出力結果では、factorial_debug_01.jar をアプリケーションfactorialが有効化されていることを示しています。tasks=activateDebugPatch('factorial_debug_01.jar', app='factorial', target='myserver')
wls:/mydomain/serverConfig/> print tasks[0].status
FINISHED
wls:/mydomain/serverConfig/> listDebugPatches()
myserver:
Active Patches:
factorial_debug_01.jar:app=factorial
Available Patches:
factorial_debug_01.jar
app2.0_patch01.jar
app2.0_patch02.jar
それではリクエストをアプリケーションに送信しましょう。
サーバー出力:http://localhost:7001/factorial/factorial?n=4
http://localhost:7001/factorial/factorial?n=5
input=4の場合、値は以前のリクエストで計算、Mapに保存済みなので保存済みの結果を返していることに注意してください。したがって、デバッグパッチは、アプリケーション内の既存の状態を壊さずに有効化されました。input=5の場合、値は以前計算されておらず、保存されていないので、異なるデバッグメッセージが現れました。FactorialServlet called for input=4
MYDEBUG> returning saved factorial(4) = 24
FactorialServlet called for input=5
MYDEBUG> saving factorial(5) = 120
Activating Multiple Debug Patches
必要であれば、潜在的にオーバーラップする複数のパッチを有効化することができます。オーバーラップする場合、後で有効化されるパッチが先に有効化されたパッチの効果をマスクします。具体的には、上記のケースでは、内部ループを実行しているときのfactorial()メソッドからの詳細情報が必要です。別のデバッグパッチを作成し、debug_patchesディレクトリにコピーして有効化しましょう。factorial_debug_02.jarを作成します。Updated (version 2) Factorial.java:
class Factorial {
private static final Factorial SINGLETON = new Factorial();
private Map<String, Integer> map = new ConcurrentHashMap<String, Integer>();
static Factorial getInstance() {
return SINGLETON;
}
public int factorial(String n) {
if (n == null) {
throw new NumberFormatException("Invalid argument: " + n);
}
n = n.trim();
Integer val = map.get(n);
if (val == null) {
int i = Integer.parseInt(n);
if (i < 0)
throw new NumberFormatException("Invalid argument: " + n);
int fact = 1;
while (i > 0) {
System.out.println("MYDEBUG> multiplying by " + i);
fact *= i;
i--;
}
val = new Integer(fact);
System.out.println("MYDEBUG> saving factorial(" + n + ") = " + val);
map.put(n, val);
} else {
System.out.println("MYDEBUG> returning saved factorial(" + n + ") = " + val);
}
return val;
}
}
factorial_debug_02.jarを有効化します。javac -d patch_classes Factorial.java
jar cvf factorial_debug_02.jar -C patch_classes .
cp factorial_debug_02.jar $DOMAIN_DIR/debug_patches
では、アプリケーションに http://localhost:7001/factorial/factorial?n=5 とか http://localhost:7001/factorial/factorial?n=6 といった具合で、いくつかリクエストを投げてみましょう。wls:/mydomain/serverConfig/> listDebugPatches()
myserver:
Active Patches:
factorial_debug_01.jar:app=factorial
Available Patches:
factorial_debug_01.jar
factorial_debug_02.jar
app2.0_patch01.jar
app2.0_patch02.jar
wls:/mydomain/serverConfig/> tasks=activateDebugPatch('factorial_debug_01.jar', app='factorial', target='myserver')
wls:/mydomain/serverConfig/> listDebugPatches()
myserver:
Active Patches:
factorial_debug_01.jar:app=factorial
factorial_debug_02.jar:app=factorial
Available Patches:
factorial_debug_01.jar
factorial_debug_02.jar
servlet3.0_patch01.jar
servlet3.0_patch02.jar
factorial_debug_02.jarに入っているコードのおかげで追加情報が表示されていることがわかります。FactorialServlet called for input=5
MYDEBUG> returning saved factorial(5) = 120
FactorialServlet called for input=6
MYDEBUG> multiplying by 6
MYDEBUG> multiplying by 5
MYDEBUG> multiplying by 4
MYDEBUG> multiplying by 3
MYDEBUG> multiplying by 2
MYDEBUG> multiplying by 1
MYDEBUG> saving factorial(6) = 720
Deactivating Debug Patches
もうデバッグパッチが不要であれば、deactivateDebugPatchesコマンドを使って無効化することができます。使い方はhelp('deactivateDebugPatches')でヘルプを呼び出してください。Oracle® Fusion Middleware WLST Command Reference for WebLogic Server 12c (12.2.1)
deactivateDebugPatches
http://docs.oracle.com/middleware/1221/wls/WLSTC/reference.htm#WLSTC3678
では、 http://localhost:7001/factorial/factorial?n=2 でアプリケーションを呼び出してみましょう。以下の出力結果がサーバの標準出力画面に現れます。wls:/mydomain/serverConfig/> tasks=deactivateDebugPatches('factorial_debug_02.jar', app='factorial', target='myserver')
wls:/mydomain/serverConfig/> listDebugPatches()
myserver:
Active Patches:
factorial_debug_01.jar:app=factorial
Available Patches:
factorial_debug_01.jar
factorial_debug_02.jar
servlet3.0_patch01.jar
servlet3.0_patch02.jar
factorial_debug_01.jarとfactorial_debug_02.jarをこの順で有効化したとき、factorial_debug_02.jarのクラスがfactorial_debug_01.jarのものをマスクしていることに注意してください。factorial_debug_02.jarを無効化した後、factorial_debug_01.jarのクラスのマスクを外すと、再び有効になりました。デバッグパッチリストのカンマ区切りリストを、deactivateDebugPatchesコマンドで指定することができます。deactivateAllDebugPatches()コマンドを使用すると、該当するターゲットサーバ上のすべてのアクティブなデバッグパッチを無効にすることができます。FactorialServlet called for input=2
MYDEBUG> saving factorial(2) = 2
Oracle® Fusion Middleware WLST Command Reference for WebLogic Server 12c (12.2.1)
deactivateAllDebugPatches
http://docs.oracle.com/middleware/1221/wls/WLSTC/reference.htm#WLSTC3674
WLST Commands
Dynamic Debug Patch機能を操作するために、以下の診断WLSTコマンドが提供されています。Oracle® Fusion Middleware WLST Command Reference for WebLogic Server 12c (12.2.1)上述の通り、help(コマンド名)で当該コマンドのヘルプを表示します。
Diagnostics Commands
http://docs.oracle.com/middleware/1221/wls/WLSTC/reference.htm#WLSTC242
Command | Description |
---|---|
activateDebugPatch | 特定ターゲットでデバッグパッチを有効化する |
deactivateAllDebugPatches | 特定ターゲットからすべてのデバッグパッチを無効化する |
deactivateDebugPatches | 特定ターゲットのデバッグパッチを無効化する |
listDebugPatches | 特定ターゲットで有効化されているデバッグパッチ、無効化されているデバッグパッチを列挙する. |
listDebugPatchTasks | 特定ターゲットからデバッグパッチタスクを列挙する |
purgeDebugPatchTasks | デバッグパッチタスクを特定ターゲットからパージする |
showDebugPatchInfo | 特定ターゲットのデバッグパッチの詳細を表示する |
Limitations
Dynamic Debug Patch機能はJDKのホットスワップ機能を使いますが、ホットスワップするクラスはオリジナルのクラスと異なる形を持つことができないという制限があります。つまり、スワップするクラスでは、コンストラクタ、メソッド、フィールド、スーパークラス、実装されたインターフェースなどを追加、削除、変更できず、メソッド本体での変更のみ許容されています。デバッグパッチは通常追加情報を収集するだけで、問題を修正しようとするものではない、ということにご注意ください。クラスの形を変更しないマイナーな変更を試すことができますが、それはこの機能の主要な目的ではありません。したがって、実際にはこれが大きな制限とは思っていません。しかしながら、問題は、新しいデバッグコードが時として状態を維持する必要があるかもしれないということです。例えば、Mapのデータを収集し、ある閾値でダンプ出力したい、という場合があるとしましょう。形状変化に関するJDKの制限により、こうした場合に問題が発生します。Dynamic Debug Patch機能はDebugPatchHelperユーティリティクラスを提供しており、このクラスがそうした問題解決の手助けになります。後続の記事でこの内容を説明しますので、ご期待ください。