home > 投稿 > FlashPlayer10.1でバグレポート
2010/03/03

FlashPlayer10.1でバグレポート


「開発時にはクラッシュレポートみたいなのを表示して、エラーが出た状況を書き込んでもらえれば、ありがたい。そういうライブラリを誰か作ってほしい。」

などと、弊社のババが呟きました。

ローカルマシンでは正しく動作するのに、別マシンで動かしたら「ActionScriptエラーが発生しました」というポップアップ…。そんなエラーが発生したクライアントの動作環境が分かれば、環境依存するような問題を解決しやすくなります。

Flash Player 10.1からグローバルなエラー処理が可能となります。
つまり、クライアントで発生したエラー内容を記録する仕組みを実現できるようになりました。

今回は、この仕組みを AS3 + GAE/J(GoogleAppEngin for Java)を利用して簡単に構築してみようと思います。
※開発には、FlashPlayer10.1 beta 3(2010/03/02現在)をインストールする必要があります。
GAE/Jを開発する環境も別途必要になります。

AS3 : UncaughtErrorMonitorクラス

package{

	import flash.display.DisplayObject;
	import flash.events.ErrorEvent;
	import flash.events.UncaughtErrorEvent;
	import flash.external.ExternalInterface;
	import flash.net.URLRequest;
	import flash.net.URLRequestMethod;
	import flash.net.URLVariables;
	import flash.net.sendToURL;
	import flash.system.Capabilities;

	/**
	 * エラー補足モニタークラス
	 * 
	 * グローバルエラーを監視し、エラーが発生した場合に内容をポストします
	 * 
	 * @author maegawa@bascule
	 */	
	public class UncaughtErrorMonitor{
		
		/**
		 * コンストラクタ
		 * @param clazz
		 */		
		public function UncaughtErrorMonitor( clazz:PrivateClass ){
			// インスタンスは生成させない
		}
		/**
		 * 初期化する
		 * @param root	:ドキュメントクラス
		 */		 
		public static function init( root:DisplayObject ):void{
			
			if( root && root.loaderInfo ){
				root.loaderInfo.uncaughtErrorEvents.addEventListener( UncaughtErrorEvent.UNCAUGHT_ERROR, function( e:UncaughtErrorEvent ):void{
				
					var errorID	:uint = 0;
					var type	:String = "";
					var message	:String = "";
					var location:String = "";
					
					if( e.error is Error ){	// Errorを捕捉した場合
						var error:Error = e.error as Error;
						errorID = error.errorID;
						type = error.name;
						message = error.message;
						location = error.getStackTrace();
					}else
					if( e.error is ErrorEvent ){	// ErrorEventを捕捉した場合
						var event:ErrorEvent = e.error as ErrorEvent;
						errorID = event.errorID;
						type = event.type; 
						message = event.text;
					}else{
						return;	// Error、ErrorEventを補足した場合は処理を中断
					}
					
					var url:String = "http://GAEアプリID.appspot.com/regist";
					
					var variables:URLVariables = new URLVariables;
						variables.errorID = errorID;		// エラーID
						variables.type = type;			// エラータイプ
						variables.message = message;		// エラーメッセージ
						variables.location = location;		// エラー発生場所
						variables.swf = Capabilities.version;	// SWFのバージョン判定
						variables.userAgent = getUserAgent();	// UserAgent判定
						
					var request:URLRequest = new URLRequest( url );
						request.data = variables;
						request.method = URLRequestMethod.POST;
					
					try{
						sendToURL( request );
					}catch( e:Error ){
					}
				} );
			}
		}
		/**
		 * ユーザーエージェントを取得する
		 * @return 
		 */		
		private static function getUserAgent():String{
			try{
				if( ExternalInterface.available ){
					return ExternalInterface.call( "function(){ return navigator.userAgent; }" );
				}
			}catch( e:Error ){}
			
			return "unknow";
		}
	}
}
class PrivateClass{}

続いて、サーバー側を準備します。

GAE : UncaughterrorServletクラス

package jp.bascule;

import java.io.IOException;
import java.util.Date;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.Transaction;

@SuppressWarnings("serial")
public class RegistUncaughtErrorServlet extends HttpServlet {
	
	public void doGet( HttpServletRequest req, HttpServletResponse res ) throws IOException {
		res.sendError( 400 );
	}
	
	public void doPost( HttpServletRequest req, HttpServletResponse res ) throws IOException {
		
		try {
			String errorID = req.getParameter( "errorID" );// エラーID
			String type = req.getParameter( "type" );// エラータイプ
			String message = req.getParameter( "" );// エラーメッセージ
			String location = req.getParameter( "location" );// エラー発生場所
			String swf = req.getParameter( "swf" );// SWFのバージョン判定
			String userAgent = req.getParameter( "userAgent" );// UserAgent判定
			
			Date date = new Date();
			long time = date.getTime();
			//
			Entity entity = new Entity( "UncaughtError" );
			entity.setProperty( "appkey", appkey );
			entity.setProperty( "error_id", errorID );
			entity.setProperty( "error_type", type );
			entity.setProperty( "error_message", message );
			entity.setProperty( "error_location", location );
			entity.setProperty( "swf_version", swf );
			entity.setProperty( "user_agent", userAgent );
			entity.setProperty( "datetime", time );
			
			
			DatastoreService service = DatastoreServiceFactory.getDatastoreService();
			Transaction transaction = service.beginTransaction();
			try {
				service.put(transaction, entity);
				transaction.commit();
			} finally {
				if (transaction.isActive())
					transaction.rollback();
			}

			res.getWriter().print( "success" );
			
		} catch (Exception e) {
			res.getWriter().print( "error" );
			e.printStackTrace();
		}
	}
}

GAE:web.xml

<?xml version="1.0" encoding="utf-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
	<servlet>
		<servlet-name>RegistUncaughtError</servlet-name>
		<servlet-class>jp.bascule.RegistUncaughtErrorServlet</servlet-class>
	</servlet>
	<servlet-mapping>
		<servlet-name>RegistUncaughtError</servlet-name>
		<url-pattern>/regist</url-pattern>
	</servlet-mapping>
	<welcome-file-list>
		<welcome-file>index.html</welcome-file>
	</welcome-file-list>
</web-app>

この内容をGAEにデプロイすれば完了です。

AS3:Testクラス

package{

	import flash.display.Sprite;
	import flash.events.ErrorEvent;
	
	/**
	 * Testクラス
	 * 
	 * UncaughtErrorMonitorクラスの動作をチェックします。
	 * 意図的にError、ErrorEventをスローさせます。
	 */	
	public class Test extends Sprite{
		
		public function Test(){
			// モニタリングを開始
			UncaughtErrorMoniter.init( this );
			// Errorの場合
			throw new Error( "エラーが発生!" );
			// ErrorEventの場合
			//throw new ErrorEvent( ErrorEvent.ERROR, false, false, "エラーイベントが発生!" );
		}
	}
}


こんな感じで呼び出しておけば、クライアント側でエラーが発生した場合に勝手に内容がポストされます。
いくつかのプロジェクトで仕組みを使いまわすのであれば、アプリケーション識別子を引数に加えてあげればいいだけです。さらに、画面サイズ、ネットワーク環境など、その他の情報も記録しておけば、よりスムーズに問題を解決できそうです。

GAEの管理画面にはDatastoreViewerというデータを確認する画面があるので、一覧表示する画面を用意する必要もありません。GAEは無料で使用可能ですが、データ保存容量には限界があります。とくに情報を残す必要がないのであれば、cronを利用して定期的に内容を削除すればよいと思います。

トラックバックURL

http://faces2.bascule.co.jp/mt/mt-tb.cgi/602

コメントを投稿

(コメントには承認が必要になることがあります。承認されるまではコメントは表示されません。)