GWT Remote Loggingで出力されるログを読みやすくする

Google Web Toolkit にはJavaのLoggingインタフェースを利用したロギング機構が用意されています。

中でも強力なのはRemote Loggingが標準でサポートされていることです。
クライアントのエラーをサーバーに集められるため迅速なトラブルシューティングが可能になります。

しかし、GWTのデフォルトのコンパイルスタイル OBFUSCATED だとスタックトレースは以下のように、最も難読化されたモードのまま出力されてしまいます。
例えば、GWT2.5.0のsamplesに含まれるLogExampleを実行して、”Trigger Exception”ボタンをクリックして出力されるスタックトレースは以下のようになります。
[text]
Sat Nov 02 10:15:15 GMT+900 2013 SEVERE: Fake Null Exception Hit
java.lang.NullPointerException: null
at Unknown.Ec(StackTraceCreator.java:174)
at Unknown.jI(StackTraceCreator.java:501)
at Unknown.Rv(OneLoggerController.java:70)
at Unknown.Of(ClickEvent.java:55)
at Unknown.Gh(GwtEvent.java:76)
at Unknown.vh(HandlerManager.java:127)
at Unknown.Sr(Widget.java:129)
at Unknown.Bf(DomEvent.java:116)
at Unknown._r(UIObject.java:557)
at Unknown.ay(DOM.java:1307)
at Unknown.anonymous(DOMImplStandard.java:171)
at Unknown.Xb(Impl.java:189)
at Unknown.$b(Impl.java:242)
at Unknown.anonymous(Impl.java:70)
[/text]
これでは、なかなかエラーを追うのが大変なため、以下のように元のクラス名に変換して出力ように設定していきます。
[text]
2013/11/02 10:15:15 com.google.gwt.logging.server.RemoteLoggingServiceUtil logOnServer
SEVERE: Fake Null Exception Hit
java.lang.Throwable
at com.google.gwt.core.client.impl.StackTraceCreator$CollectorEmulated.$fillInStackTrace(StackTraceCreator.java:174)
at java.lang.NullPointerException.NullPointerException(NullPointerException.java:501)
at com.google.gwt.sample.logexample.client.OneLoggerController_MyUiBinderImpl$Widgets$2.onClick(OneLoggerController_MyUiBinderImpl.java:70)
at com.google.gwt.event.dom.client.ClickEvent.dispatch(ClickEvent.java:55)
at com.google.web.bindery.event.shared.SimpleEventBus.$doFire(SimpleEventBus.java:76)
at com.google.gwt.event.shared.HandlerManager.$fireEvent(HandlerManager.java:127)
at com.google.gwt.user.client.ui.Widget.$fireEvent(Widget.java:129)
at com.google.gwt.event.dom.client.DomEvent.fireNativeEvent(DomEvent.java:116)
at com.google.gwt.user.client.ui.Widget.onBrowserEvent(Widget.java:557)
at com.google.gwt.user.client.DOM.dispatchEvent(DOM.java:1307)
at Unknown.anonymous(DOMImplStandard.java:171)
at com.google.gwt.core.client.impl.Impl.apply(Impl.java:189)
at com.google.gwt.core.client.impl.Impl.entry0(Impl.java:242)
at Unknown.anonymous(Impl.java:70)
[/text]

1. Remote Loggingを有効にする

gwt.xmlで以下のプロパティを設定するとサーバーにログが送信されるようになります。
[xml]
<set-property name="gwt.logging.simpleRemoteHandler" value="ENABLED" />
[/xml]
LogExampleの場合は、既に設定済みですので、samples/LogExample/src/com/google/gwt/sample/logexample/LogExample.gwt.xml を確認してください。

2. コンパイル時にsymbolMapsをwarに含める

symbolMapsは、コンパイルされたJavascriptのメソッド名と実際のクラス/メソッドのマップを保持するファイルを含みます。
デフォルトでは、コンパイル時に -war に指定されたディレクトリの WEB-INF/deplay/<モジュール名>/symbolMaps ディレクトリに出力されるので、そのままwarファイルに含まれます。

3. symbolMapsを読み込むRemoteLoggingServiceを実装する

サーバーでクライアントのログを受けるためには、RemoteLoggingServiceImpl を web.xml に追加します。
samples/LogExample/war/WEB-INF/web.xml で確認できます。

いくつかのブログや掲示板では、この RemoteLoggingServiceImpl の init-param に symbolMaps のパスを渡すとスタックトレースが実際のJavaのクラス/メソッドに変換されて出力される記述されていますが RemoteLoggingServiceImp はinit-paramをハンドリングしません。

そのため、RemoteLoggingServiceImp を拡張するクラスを実装する必要があります。
Removing obfuscation from a stack trace.

LogExampleを用いて確認するために、src/com/google/gwt/sample/logexample/server/MyRemoteLoggingService.java を以下のように記述しました。
[java]
package com.google.gwt.sample.logexample.server;

import com.google.gwt.logging.server.RemoteLoggingServiceImpl;
import javax.servlet.*;
public class MyRemoteLoggingService extends RemoteLoggingServiceImpl {

@Override
public void init() throws ServletException {
super.init();
setSymbolMapsDirectory(getServletContext().getRealPath("/WEB-INF/deploy/logexample/symbolMaps"));
}

}
[/java]
web.xml のサーブレット設定も RemoteLoggingServiceImpl から MyRemoteLoggingService へ変更します。
[xml]
<servlet>
<servlet-name>remoteLoggingServlet</servlet-name>
<servlet-class>com.google.gwt.sample.logexample.server.MyRemoteLoggingService</servlet-class>
<init-param>
<param-name>symbolMaps</param-name>
<param-value>WEB-INF/deploy/logexample/symbolMaps</param-value>
</init-param>
</servlet>
[/xml]

以上を設定して実行すると、上記のJavaのクラス・メソッド名に変換されたスタックトレースが出力されるようになります。

参考:
7 Tips for Exception Handling in GWT
Using the GWT Compiler for Better Builds

MavenでGWTプロジェクトを作成/管理する

[bash]
$ mvn archetype:generate

Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains): 314: gwt-maven-plugin
Choose archetype:
1: remote -> org.codehaus.mojo:gwt-maven-plugin (Maven plugin for the Google Web Toolkit.)
Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains): : 1
Choose org.codehaus.mojo:gwt-maven-plugin version:
1: 1.1
2: 1.2
3: 2.1.0
4: 2.1.0-1
5: 2.4.0
6: 2.5.0-rc1
7: 2.5.0-rc2
8: 2.5.0
9: 2.5.1-rc1
10: 2.5.1
Choose a number: 10: 10
Downloading: http://repo1.maven.org/maven2/org/codehaus/mojo/gwt-maven-plugin/2.5.1/gwt-maven-plugin-2.5.1.jar
Downloaded: http://repo1.maven.org/maven2/org/codehaus/mojo/gwt-maven-plugin/2.5.1/gwt-maven-plugin-2.5.1.jar (181 KB at 78.2 KB/sec)
Downloading: http://repo1.maven.org/maven2/org/codehaus/mojo/gwt-maven-plugin/2.5.1/gwt-maven-plugin-2.5.1.pom
Downloaded: http://repo1.maven.org/maven2/org/codehaus/mojo/gwt-maven-plugin/2.5.1/gwt-maven-plugin-2.5.1.pom (21 KB at 44.4 KB/sec)
Define value for property ‘groupId’: : com.example
Define value for property ‘artifactId’: : HelloGWT2.5.1
Define value for property ‘version’: 1.0-SNAPSHOT: :
Define value for property ‘package’: com.example: :
Define value for property ‘module’: : helloworld
Confirm properties configuration:
groupId: com.example
artifactId: HelloGWT2.5.1
version: 1.0-SNAPSHOT
package: com.example
module: helloworld
Y: : y
[INFO] —————————————————————————-
[INFO] Using following parameters for creating project from Archetype: gwt-maven-plugin:2.5.1
[INFO] —————————————————————————-
[INFO] Parameter: groupId, Value: com.example
[INFO] Parameter: artifactId, Value: HelloGWT2.5.1
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] Parameter: package, Value: com.example
[INFO] Parameter: packageInPathFormat, Value: com/example
[INFO] Parameter: package, Value: com.example
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] Parameter: module, Value: helloworld
[INFO] Parameter: groupId, Value: com.example
[INFO] Parameter: artifactId, Value: HelloGWT2.5.1
[INFO] project created from Archetype in dir: /Users/hrendoh/workspace/HelloGWT2.5.1
[INFO] ————————————————————————
[INFO] BUILD SUCCESS
[INFO] ————————————————————————
[INFO] Total time: 1:29.365s
[INFO] Finished at: Fri Nov 01 16:48:36 JST 2013
[INFO] Final Memory: 7M/81M
[INFO] ————————————————————————
[/bash]

[bash]
$ cd HelloGWT2.5.1
$ mvn gwt:run
[/bash]
ant devmodeした場合と同じく、”GWT Development Mode”が起動します。

参考:
Compile GWT application into JavaScript

MavenでWebアプリケーションプロジェクト HelloWorld jettyプラグインで実行

Mavenは、Rubyで言うところのrakeにあたる、Javaのプロジェクト管理ツールです。
おそらくデファクトスタンダードなはずですが、antから乗り換える動機が無いためあまりちゃんと勉強したことが無かったので改めてまとめてみました。

で、一番良く使うシンプルなWebアプリプロジェクトを作成してみます。

Mavenのインストール

Macだとhomebrewとかmacportsでお手軽にインストールできます。

Mavenプロジェクトの生成

[bash]
$ mvn archetype:generate -DarchetypeArtifactId=maven-archetype-webapp

[INFO] Generating project in Interactive mode
Define value for property ‘groupId’: : com.example
Define value for property ‘artifactId’: : HelloMavenWebApp
Define value for property ‘version’: 1.0-SNAPSHOT: :
Define value for property ‘package’: com.example: :
Confirm properties configuration:
groupId: com.example
artifactId: HelloMavenWebApp
version: 1.0-SNAPSHOT
package: com.example
Y: : y
….
[INFO] ————————————————————————
[INFO] BUILD SUCCESS
[INFO] ————————————————————————
[INFO] Total time: 45.392s
[INFO] Finished at: Fri Nov 01 14:27:49 JST 2013
[INFO] Final Memory: 7M/81M
[INFO] ————————————————————————
[/bash]

Maven プロジェクトを生成するコマンドは mvn archetype:generate です。
パラメータ archetypeArtifactId に archetype(Mavenプロジェクトのテンプレート)を指定します。
archetypeを指定しない場合は、コマンドライン上で選択できますが、数が膨大なので目的のテンプレートを探すのは困難です。
今回利用した maven-archetype-webapp はMavenの公式archetype(groupIdがorg.apache.maven.archetypes)です。
Introduction to Archetypes で確認できます。

必要なプロパティは、すべてプロプントで聞かれるので簡単にプロジェクトを生成できます。

  • groupId は、Javaでいつも使っているパッケージを指定します(例:com.co-meeting)。groupId は archetype を検索する際に条件にも利用できます。
  • artifactId は、プロジェクトの名前を指定します。
  • version は、今回はデフォルトのまま、Enter。
  • package は、通常groupIdと同じになるので、Enter。

プロジェクトをビルドして実行

[bash]
$ cd HelloMavenWebApp/
$ mvn compile war:war
[/bash]
http://maven.apache.org/plugins/maven-war-plugin/usage.html

pom.xmlのbuildの下にpluginsを追加します。
[xml]
<build>
<finalName>HelloMavenWebApp</finalName>
  <plugins>
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
[/xml]

注意: 似た名前の maven-jetty-plugin は古いので Jetty7以上を使う場合は jetty-maven-plugin をしていすること

jettyプラグインを実行
[bash]
$ mvn jetty:run
[/bash]

http://localhost:8080/ にアクセスして “Hello World!” と表示されれば成功です。

Jetty9 を利用する場合は、org.eclipse.jettyのjetty-maven-pluginを利用する必要があります。

ただし、org.eclipse.jettyのプラグインはデフォルトのリポジトリには含まれていないため Maven の settings.xml に追加する必要があります。
settings.xmlについての説明は Settings Reference を参照してください。

.m2/settings.xml を以下のように記述します。
[xml]
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
http://maven.apache.org/xsd/settings-1.0.0.xsd">
<localRepository/>
<interactiveMode/>
<usePluginRegistry/>
<offline/>
<pluginGroups>
<pluginGroup>org.eclipse.jetty</pluginGroup>
</pluginGroups>
<servers/>
<mirrors/>
<proxies/>
<profiles/>
<activeProfiles/>
</settings>
[/xml]

pom.xmlのpluginsを以下の用に書き換えます。
[xml]
<build>
<finalName>HelloMavenWebApp</finalName>
  <plugins>
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
[/xml]
実行方法は同じです。
[bash]
$ mvn jetty:run
[/bash]

GWT (Google Web Toolkit) 2.5.0 Hello World

1. ダウンロードサイトからgwtを入手する
https://code.google.com/p/google-web-toolkit/downloads/detail?name=gwt-2.5.0.zip
2. 解凍
[bash]
$ unzip gwt-2.5.1.zip
[/bash]
3. webAppCreatorを使ってプロジェクトを生成
[bash]
$ ./webAppCreator -out ../HelloWorldGWT com.example.HelloWorldGWT
[/bash]
以下のパラメータが必須

  • -out: 出力先ディレクトリ
  • プロジェクト名 “fully-qualified” java的なパッケージ名を指定する必要があります。

4. 開発モードで動作確認
GWTはJavaでコードを記述してJavascriptにコンパイルする開発ツールですが、毎回コンパイルしながら開発しなくても良いように開発用ツールが用意されています。まずは開発ツールを利用して動作を確認します。
[bash]
$ cd ../HelloWorldGWT/
$ ant devmode
[/bash]
開発ツール”GWT Development Mode”が開くので Development Mode タブで [Launch Default Browser]をクリックするか、[Copy Clip Board]をクリックしてURLをコピーしてブラウザのアドレスバーにペーストしてテストサーバーにアクセスできます。

この内容は本家サイトで確認できます。
http://www.gwtproject.org/gettingstarted.html

5. コンパイル
次にJavascriptにコンパイルして動作させてみます。
[bash]
$ ant war
[/bash]
生成された、HelloWorldGWT.warをTomcatやJettyにデプロイします。