サーバサイドスクリプトのデバッグ

XPagesのJavaScriptは、ブラウザで動くクライアントサイドスクリプトと、サーバ上で動作するサーバサイドスクリプトに 分かれる。

クライアントサイドスクリプトは、FirefoxでFirebugを使えば簡単にデバッグができる。

サーバサイドスクリプトは、今のところprintデバッグしかできない(と思う)。print "ujauja"と書いておけばサーバコンソールに書き出されるそうだ。

そこでsessionScopeを使うのはどうだろう。

XPage上に複数行編集ボックスを貼り付けておく。 その複数行編集ボックスのデータバインド先をsessionScope.debugにしておく。 sessionScopeを使ったデバッグ

これでボタン内のスクリプトで、sessionScope.debugに文字列を書き込めばこの編集ボックスに文字列が書き込まれる。

複数行にしているので、sessionScope.debug += ("\nitem.length = " + item.length); のように書いておくと、前回書き込まれた内容に 追加されていく。

クリアしたいときは、編集ボックス上で表示テキストを削除すればいい(文書が編集モードの場合だけ)。

オブジェクトの中身を表示したいときには、以下の関数を使うといいかも。ネタ元は、0502 - eto.com/dの「JavaScript debug」。 これをカスタマイズしてみた。

  function my_inspect(obj, indent) {
    if (indent == null) {
      indent = "";
    }
    if (typeof obj == "number") {
      // 数値の場合
      return obj;
    } else if (typeof obj == "string") {
      // 文字列の場合
      return "\"" + obj + "\"";
    } else if (typeof obj == "function") {
      // 関数の場合
      return obj;
    } else if ((typeof obj == "java.util.ArrayList") ||
               (typeof obj == "java.util.Vector")) {
      // 配列タイプ
      var pre_part = ( "<" + typeof(obj) + ">:[\n" );
      var analist = [];
      for(var index =0; index < obj.length; index++) {
        var item = obj[index];
        analist.push(indent + "  " + my_inspect(item, indent + "  "));
      }
      var after_part = ("\n" + indent + "]");
      return (pre_part + analist.join(",\n") + after_part);
    } else if ((typeof(obj) == "object" ) || 
               (typeof(obj) == "com.sun.faces.context.SessionMap")) {
      // オブジェクトの場合
      var pre_part = "<" + typeof(obj) + ">:{\n";
      var analist = [];
      for (var key in obj) {
        // 区切り文字
        var value = obj[key];
        if (!value) {
          // キーがあるが値がない場合
          analist.push( indent + "  " + key + " = undefined");
          continue;
        }
        analist.push(indent + "  " + key + " = " + my_inspect(value, indent + "  "));
      }
      var after_part =  "\n" + indent + "}";
      return (pre_part + analist.join(",\n") + after_part);
    } else {
      return "<<"+(typeof obj)+">>:{"+ obj + "}" ;
    }
  }
  

出力結果のサンプル。

  var obj = {}
  obj.member_string = "testtest";
  obj.member_value = 3
  obj.member_array = [1,2,3];
  sessionScope.my_obj = obj;
  sessionScope.debug_rep = ("sessionScope.my_obj  = " + my_inspect(sessionScope.my_obj));
  
  <出力結果>
  sessionScope.my_obj  = <object>:{
    member_string = "testtest",
    member_value = 3,
    member_array = <object>:{
      0 = 1,
      1 = 2,
      2 = 3
    }
  }
  

printを使ったデバッグ

サーバにおいたDBならprint文を使ったデバッグが有効。

sessionScopeの何かをKeyにして、必要に応じてログを出力できるようにするとより便利かも。

こんな感じで使用する。mydebug("item_list=" + inspect_oneline(item_list));

inspect_oneline()は、先ほどのmy_inspectの1行版。print()でコンソールログに吐き出す場合、1行の方が扱いやすいので。

  function mydebug(message) {
    if (sessionScope.debug_flag) {
      print ("[MYAPP]" + message);
    }
  }
  
  function inspect_oneline(obj, indent) {
      if (!sessionScope.debug_flag) {
         return;
       }
      if (typeof obj == "number") {
        // 数値の場合
        return obj;
      } else if (typeof obj == "string") {
        // 文字列の場合
        return "\"" + obj + "\"";
      } else if (typeof obj == "function") {
        // 関数の場合
        return obj;
      } else if ((typeof obj == "java.util.ArrayList") ||
                 (typeof obj == "java.util.Vector")) {
        // 配列タイプ
        var pre_part = ( "<" + typeof(obj) + ">:[" );
        var analist = [];
        for(var index =0; index < obj.length; index++) {
          var item = obj[index];
          analist.push(my_inspect(item));
        }
        return (pre_part + analist.join(",") + "]");
      } else if ((typeof(obj) == "object" ) || 
                 (typeof(obj) == "com.sun.faces.context.SessionMap")) {
        // オブジェクトの場合
        var pre_part = "<" + typeof(obj) + ">:{";
        var analist = [];
        for (var key in obj) {
          // 区切り文字
          var value = obj[key];
          if (!value) {
            // キーがあるが値がない場合
            analist.push( key + "=undefined");
            continue;
          }
          analist.push(key + "=" + inspect_oneline(value));
        }
        return (pre_part + analist.join(",") + "}");
      } else {
        return "<<"+(typeof obj)+">>:{"+ obj + "}" ;
      }
    }
  

ただ、コンソールログの場合、1行の長さがながいと、適当なところで改行されてしまう。 また、いくつものアプリを運用していると、複数のアプリのログが重なって表示されてしまうかもしれない。

このあたりは、以下のRubyスクリプトでログを整形することで解決できる。