GAEのコンソール画面の日付のタイムゾーンってどこのタイムゾーンが適応されているのかな?と思ったので調べてみるとどうやらPST:太平洋標準時間らしい。

これをどうにかして日本時間に出来ないかなと思ってLog4jを使ってみたけどコンソール自体のタイムスタンプは変わらないみたいだし、”[appname/*.***************].<stdout>:”みたいなのがくっついてかっこ悪いし、見難い(もしかしたらやり方が間違ってる?)。

そこでjava.util.LoggerのFormatterをSimpleFormatterから独自のものにするため、/war/WEB-INF/logging.propertiesを修正してみる。

handlers= java.util.logging.ConsoleHandler

.level = INFO

java.util.logging.ConsoleHandler.level = INFO

java.util.logging.ConsoleHandler.formatter = test.TestFormatter

これでローカルではちゃんと動いてる。しかし、デプロイするとなぜかFormatterが適応されない。どうもLogger内部でクラスローダが使われてて、Formatterを指定してもちゃんと読み込んでくれないみたい。

JDK1.4 ロギングAPI の使用法について -

http://www.atmarkit.co.jp/bbs/phpBB/viewtopic.php?topic=12606&forum=12

原因は、LogManagerがクラスをロードする際SystemClassLoaderを利用するので 独自のカスタムFormatterをロードする事出来ないためです。 (LogManager/FileHandlerのソースから判断) したがって、webアプリの中などのカスタムFormatterはロードできません。

2011.03.09 追記1:

よく考えたらjava.util.logging.ConsoleHandler自体がGAEホワイトリストに追加されてないから動かないのも当然ですね。恐らくクラスローダ関係のせいではないと考えています。jarファイルを作成してそちらを参照しても結局動きませんでした。

追記終わり

んー詰んだ。LogManagerはホワイトリストにないし、どーしたものか。

んじゃもうLoggerに通知される時間を無理やり書き換えてしまえ!という結論に至ったのでサクサクコーディング♪具体的な内容は以下の通り。

  1. JSTのGMTオフセットとPSTのGMTオフセットを取得。
  2. それぞれの絶対値を足す。
  3. 足し合わせた時間をグリニッチ標準時間に足す。
  4. Logger#setFilter
  5. Filter#isLoggableが呼ばれたら3で作成した時間をLogRecord#setMillisで登録する。

無理やりすぎて笑えないけどタイムゾーンを指定できない仕様にしてるGoogleさんが悪いんだからねっ!と心に念じてやりすごs

結論的にはこの方法で全てのタイムスタンプを日本時間にすることは不可能っぽいです。自分で作成したロガーだけ日本時間になります。

具体的には以下のFilterを任意のロガーでLogger#setFilterしてあげればOK。

package test;

import java.util.Date;
import java.util.TimeZone;
import java.util.logging.Filter;
import java.util.logging.LogRecord;

public class CustomFilter implements Filter
{
	private Filter oldFilter;

	public CustomFilter(Filter oldFilter)
	{
		this.oldFilter = oldFilter;
	}

	public boolean isLoggable(LogRecord record)
	{
		record.setMillis(this.getOffset());

		if(oldFilter == null)
		{
			return true;
		}

		return oldFilter.isLoggable(record);
	}

	private long getOffset()
	{
		Date now = new Date();
		long offset = Math.abs(TimeZone.getTimeZone("JST").getOffset(now.getTime()) + Math.abs(TimeZone.getTimeZone("PST").getOffset(now.getTime())));

		return now.getTime() + offset;
	}
}

できた!明らかにほかの時間はPSTだけどtest.TestServlet doGetは日本時間!

console-jst.png

早くタイムゾーンが指定できるようになればいいんですがねぇ。

2011.03.09 追記2:

GAE専用のファクトリクラスとフィルタークラスを作成しました。以下にそのソースコードを示します。

import java.util.logging.Logger;

/**
 * Google App Engine用のロガーを提供します。
 * @author zerippe
 */
public class GAELogger
{
	protected GAELogger() {}

	public static Logger getLogger(String name)
	{
		Logger logger = Logger.getLogger(name);
		logger.setFilter(new GAELoggerFilter(logger.getFilter()));
		return logger;
	}

	public static Logger getLogger(String name, String resourceBundleName)
	{
		Logger logger = Logger.getLogger(name, resourceBundleName);
		logger.setFilter(new GAELoggerFilter(logger.getFilter()));
		return logger;
	}
}
import java.util.Date;
import java.util.TimeZone;
import java.util.logging.Filter;
import java.util.logging.LogRecord;

/**
 * GAE用のLoggerハックフィルターを提供します。
 * @author zerippe
 */
public class GAELoggerFilter implements Filter
{
	protected static TimeZone userZone = TimeZone.getTimeZone("JST");
	protected static TimeZone serverZone = TimeZone.getTimeZone("PST");

	private Filter oldFilter;

	public GAELoggerFilter(Filter oldFilter)
	{
		this.oldFilter = oldFilter;
	}

	public boolean isLoggable(LogRecord record)
	{
		record.setMillis(this.getMillis());

		if(this.oldFilter == null)
		{
			return true;
		}

		return this.oldFilter.isLoggable(record);
	}

	protected long getMillis()
	{
		long now = (new Date()).getTime();
		return Math.abs(userZone.getOffset(now) + Math.abs(serverZone.getOffset(now))) + now;
	}
}
import java.io.IOException;
import java.util.logging.Logger;

import javax.servlet.ServletException;
import javax.servlet.http.*;

@SuppressWarnings("serial")
public class TestServlet extends HttpServlet
{
	private static Logger logger = GAELogger.getLogger(TestServlet.class.getSimpleName());

	public void doGet(HttpServletRequest request, HttpServletResponse response)
		throws ServletException, IOException
	{
		logger.info("test");
	}
}

関連性がある記事

Disqusでコメント