はじめてのLog4js

社内環境で再現しない不具合が残ってしまったため、ログを取得して障害再発時に解析のネタを得ようとしています。怪しいのはクライアントサイドのJavaScriptです。車輪の再発明を避けるべく、ありがたく先人達の成果を使わせていただきます。

Log4jsを入手する - Object汚染問題対策版(勝手に命名)

Log4jsを使う環境下でfor-in構文を使うと意図せずLog4jsが追加したメソッド・プロパティまでループが実行されたり*1prototype.jsと競合したり*2するそうなので、Object汚染を回避する必要があるそうです。

この対策が取られたバージョンのソースが下記より入手できます。
http://js.nice-777.com/log4js/memo.html

一応、Log4jsのObject汚染問題は影響が無いはずですが、安全サイドに振って対策版を使うことにします。

for-inは凶悪らしいので、for-inを使っていなかったのは
#自然な結果のようです。

基本的な使い方

とかにあります。fnyaさんのエントリをよーく見て自分のコーディングミスに気づきました。orz

Could not run the listener function(){return __method.apply(object,arguments);}.
[object error]

とか(たぶん)やらかしてました。headタグの後にloggerのインスタンスを作るとダメっぽい。なおってConsoleのウィンドウが出てきたときはちょっと感動しますた。

Log4jsの資料のぐぐりかたは

概要とか基本的な使い方とか入手のしかたは普通に高度なhackingの成果もそれで得られます。

ただし、ちょっとだけ基本的じゃない使い方をするときはそれでは見つかりませんでした。*3 そんなときは元ネタのLog4jぐぐると良いようです。google:Log4j PatternLayout

レガシーなVB6 + IEコンポーネントなアプリとの相性は

ConsoleAppenderでは微妙なようです。window.open()まわりがちゃんと実装されて無いせいかもしれませんが。業務系アプリなのでFileAppenderで逃げますた。orz

FileAppenderの場合はあらかじめIEのセキュリティ設定で穴をあけておく必要があります。私の場合は http://d.hatena.ne.jp/babydaemons/20080520/1211255011 で開けておいたので結果オーライでした。

バージョンは1.0-RC1だけど・・・

細かいバグが残ってます。

  • Logger.setDateFormat()で設定した書式がBasicLayoutで出力される日付文字列に反映されない。*4
  • DateFormatter.formatDate()でミリ秒が出力できない。*5
  • PatternLayoutで日付を出力すると落ちる。内部でSimpleDateFormatクラスのインスタンスを作っているのですが、その定義がないです。

バグ改修(?)&ミリ秒精度のログ出力拡張hacking

んでもってパッチを作りました。先の対策版との差分です。所要工数およそ1人日w
私が考えてる仕様が正しくて改修も正しいなら、RC2に取り込んでもらうのが吉だと思うのですが・・・ お持ち帰りはこちらから。

--- log4js.js.orig	2008-06-12 19:16:58.000000000 +0900
+++ log4js.js	2008-06-27 13:38:21.166956800 +0900
@@ -18,7 +18,14 @@
  * KenjiNagao
  * ---------------------------------------------------------------------*/
 
-/*jsl:option explicit*/
+/* ---------------------------------------------------------------------
+ * To acquire the log in the accuracy of the millisecond,
+ * this script was customized. 
+.* This comment can be removed if you want.
+ * babydaemons(at)gmail.com
+ * ---------------------------------------------------------------------*/
+ 
+ /*jsl:option explicit*/
 
 /**
  * Object extention (OO) methods if no prototype avaliable.
@@ -1952,7 +1959,8 @@
 	 * @type String
 	 */
 	format: function(loggingEvent) {
-		return loggingEvent.categoryName + "~" + loggingEvent.startTime.toLocaleString() + " [" + loggingEvent.level.toString() + "] " + loggingEvent.message + this.LINE_SEP;
+//		return loggingEvent.categoryName + "~" + loggingEvent.startTime.toLocaleString() + " [" + loggingEvent.level.toString() + "] " + loggingEvent.message + this.LINE_SEP;
+		return loggingEvent.categoryName + "~" + loggingEvent.logger.getFormattedTimestamp(loggingEvent.startTime) + " [" + loggingEvent.level.toString() + "] " + loggingEvent.message + this.LINE_SEP;
 	},
 	/** 
 	 * Returns the content type output by this layout. 
@@ -2473,15 +2481,16 @@
 	formatDate : function(vDate, vFormat) {
 	  var vDay = this.addZero(vDate.getDate());
 	  var vMonth = this.addZero(vDate.getMonth()+1);
-	  var vYearLong = this.addZero(vDate.getFullYear());
+	  var vYearLong = this.addZero(vDate.getFullYear(), "0000");
 	  var vYearShort = this.addZero(vDate.getFullYear().toString().substring(3,4));
 	  var vYear = (vFormat.indexOf("yyyy")>-1?vYearLong:vYearShort);
 	  var vHour  = this.addZero(vDate.getHours());
 	  var vMinute = this.addZero(vDate.getMinutes());
 	  var vSecond = this.addZero(vDate.getSeconds());
+	  var vMilliseconds = this.addZero(vDate.getMilliseconds(), "000");
 	  var vTimeZone = this.O(vDate);
 	  var vDateString = vFormat.replace(/dd/g, vDay).replace(/MM/g, vMonth).replace(/y{1,4}/g, vYear);
-	  vDateString = vDateString.replace(/hh/g, vHour).replace(/mm/g, vMinute).replace(/ss/g, vSecond);
+	  vDateString = vDateString.replace(/hh/g, vHour).replace(/mm/g, vMinute).replace(/ss/g, vSecond).replace(/SSS/g, vMilliseconds);
 	  vDateString = vDateString.replace(/O/g, vTimeZone);
 	  return vDateString;
 	},
@@ -2490,8 +2499,11 @@
 	 * @private
 	 * @static
 	 */
-	addZero : function(vNumber) {
-	  return ((vNumber < 10) ? "0" : "") + vNumber;
+	addZero : function(vNumber, vFormat) {
+//	  return ((vNumber < 10) ? "0" : "") + vNumber;
+	  function right(str, len) { return (new String(str)).substr(str.length - len, len); }
+	  if (!vFormat) vFormat = "00";
+	  return right(vFormat + vNumber, vFormat.length);
 	},
 	
 	/**
@@ -2510,6 +2522,22 @@
 	}
 };
 
+Log4js.SimpleDateFormat = function(dateFormat)
+{
+	this.dateFormat = dateFormat;
+	this.formatter = new Log4js.DateFormatter();
+};
+
+Log4js.SimpleDateFormat.prototype = {
+	/** 
+	 * Format input date by Log4js.DateFormatter.formatDate(). 
+	 * @return formatted date string.
+	 * @type String
+	 */
+	format : function (date) {
+		return this.formatter.formatDate(date, this.dateFormat);
+	}
+};
 
 /**
  * internal Logger to be used

*1:今回はfor-in構文は使ってないけど。

*2:うちの会社はWeb-0.9くらいなので使ってないけど。

*3:私の場合はLog4js.PatternLayout

*4:仕様かも

*5:これも仕様かも