Google Apps Scriptでのエラー処理とトラップ【GAS】
以前から、稀に発生していたのですが、HTML ServiceでUIを作成し、Google Sitesに貼り付けて運用してるフォームがあるわけですが、スプレッドシート上とは違い、なんらかのエラーが発生しても、そのままではUI上ではエラーが発生してる事をユーザや管理者が知る事が出来ません。特に問題なのは、UI側には問題がないのだけれど、書き込みやメール送信をしてるGoogleのサービス側で障害が起きていても、申請者等はきちんと処理されてると思い込んだり、表面上処理されているように見えて、実は処理出来ていなかったなんて事があります。
きちんとデバッグを行い、バグ修正を行った上で、上記のような現象が起きるケースは、その殆どがGoogleのサーバー側の問題です(正直、Google Appsに関して言えば、あまり信用してはいけません)。全く同じ内容で昨日まで動いていたのに、今日は動かないというのは、まさにこれに該当します。しかし、それでは困ります。申請フォームのような場合、申請者は申請してると思ってるし、管理者側は何も申請されていないと思ってる事になってしまうからです。
という事で、今回、この手の仕組みに必須のエラートラップを仕掛けてみたいと思います。
使用するメソッド、クラス等
- try…catch(e)…構文
- google.script.run.withSuccessHandler構文
概要
▶説明
try…catch…構文は元々、JavaScriptで装備されてる例外処理の為の仕組みです。tryの中に通常のプログラムを記述し、エラーが発生すると、catch文に移行してエラー処理を実行するというものです。更には、finallyをつなげれば、エラーが発生しようがしまいが、try…catchの後に実行される仕組みになっています。Google Apps Scriptでも同様の仕組みになっていますので、JavaScriptでの実装事例をそのまま利用できます。
▶exceptionの処理
catch(e)の部分のeはexceptionと呼び、サービス側から送信されるエラーメッセージ等が格納されています。よって、エラー処理内でこれらを利用してメッセージボックスやメールにして送ることが出来ます。例えば、e.messageで「エラーメッセージそのもの」、e.lineNumberでエラーが発生してる行番号、e.stackでスタック情報、e.fileNameでファイル名といった具合に取り出す事が出来ます。
▶より丁寧に
但し、これでは正直ユーザにとっては、意味不明なエラーメッセージとなるので、今回は処理ブロック毎にメッセージとブロック番号を用意して、どういうエラーが発生してるのかが分かるように、ブロック毎に事前にメッセージと番号を入れ、エラー発生時にそれらを利用してメッセージボックスやメールで通知をしています。また、入力内容等はメール等で送って上げると、消失せずエビデンスとする事が出来るので、自分はそれを添付して送ってあげています。
▶主なエラートラップの流れ
自分の場合の書き方は、try…catch(e)は1個だけ書きます。try以下にスクリプトの全てを書き、ブロック毎にエラー時のメッセージを上書きで変数に格納。エラー時にはそれを利用して、メール送信やUI側へreturnして上げて、alertで表示という流れです。申請者だけでなく管理者である自分にも同時にメールが送られるようにしてあります。基本の流れとしては、
- エラーをフォーム側に通知する
- エラー時にメールで通知する
- フォームなどの場合、入力内容をプールして、返して上げる
こんな感じです。せっかく書き込んだフォームの入力内容も送って上げるので、相手に取ってもコピペでもう一度送れるので手間が省けます。
ソースコード
GAS側
//HTML側からデータを受け取り処理をするルーチン
function sendman(selectid, approve){
//エラートラップ用の変数
var errorword = "";
var errorblock = "";
var sendmsg = "";
var mailman = "";
try{
//処理ブロック1
errorword = "IDの探索作業でエラーが発生しました。";
errorblock = "1";
//★★ここに処理ブロック1の処理を記述★★
//処理ブロック2
errorword = "スプレッドシートへの書き込みエラーが発生しました。";
errorblock = "2";
//★★ここに処理ブロック2の処理を記述★★
//処理ブロック3
errorword = "メール送信エラーが発生しました。";
errorblock = "3";
//★★ここに処理ブロック3の処理を記述★★
//完了通知をフォーム側へ送る
sendmsg = JSON.stringify(”承認完了”);
return sendmsg;
}catch(e){
//エラーメッセージを組み立てる
sendmsg += "【エラータイトル】:" + errorword + "<br>";
sendmsg += "【エラー番号】:" + errorblock + "<br>";
//エラー内容を承認者にメールで通知
MailApp.sendEmail({
to: GetUser(),
subject: "承認作業エラー通知",
htmlBody: sendmsg,
name:"承認作業ERROR",
});
//エラー通知をフォーム側へ送る
sendmsg = JSON.stringify(sendmsg);
return sendmsg;
}
}
//承認者のメールアドレスを取得する
function GetUser() {
var objUser = Session.getActiveUser();
Logger.log(objUser);
return objUser.getEmail();
}
HTML側
//データをGAS側へ送信する処理
function sendflow(){
var selectid = "";
var approve = "";
selectid = 1
approve = "承認"
//データを送信して結果を受け取り、errorhandlmsgへ渡す
google.script.run.withSuccessHandler(errorhandlmsg).sendman(selectid,approve);
}
//エラーハンドリングメッセージの表示
function errorhandlmsg(msg){
var result = JSON.parse(msg);
alert(result);
}
ポイント
- ブロック単位にtry…catch(e)…を囲んで、処理をしても問題ありません。
- スプレッドシートへの書き込み等の場合には、エラー発生時に同時にロールバック処理をするような処理もいれておくと丁寧です。でないと、書き込み成功後にメール送信エラーが発生すると、書き込みだけされてる状態なので、不都合な場合があります。
- catch文内では、変数等の文字色が青ではなく、黒のままで、未宣言のように見えますが問題ありません。
- HTML Service側から送信処理をする場合には、google.script.runで実行しっぱなしにするのではなく、google.script.run.withSuccessHandlerを利用して、GAS側からは成功時とエラー時にそれぞれメッセージや数値などを格納して、値をreturnするようにしましょう。これで、UI側でそれを受け取って、通知する事ができます。
- エラートラップでエラーが発生したからといって、即プログラムのバグという事にはなりません。Google側のサーバ障害やサービス障害であるケースが多々あります。
- また、サービス障害ではないけれど、サービス更新で特定のメソッドが利用できなくされたり、仕様変更の場合もありますので、まずはその辺から調べてからコードは弄るようにしましょう。取越苦労や二度手間などを招きますので。
- エラートラップにだけ頼るのではなく、UI側で事前にvalidation処理(入力チェック)を実装しておいて、可能な限りエラーにならないようにしておくのも重要な手段です。
- Googleサーバ障害が疑われるようなケースの場合、トリガーを利用して再処理の予約を入れておくのも良い方法です。但し、ソウでない場合、永遠に処理がされ続けることにもなるので、実装は慎重に。
- エラーを発生させる(エラーとする)場合には、throw文を使います。これでGASで起こるシステム的なエラーだけでなく、意図的にエラーとして、catch文へ引き継がせるというテクニックもあります。例えば、指定したIDがスプレッドシートで見つからない場合には、そこで処理をエラー扱いにしてしまえば、それ以降の処理を別に記述する必要がなくなります。
- トリガーで定期実行するようなものも、エラートラップを仕掛けておけば、設置者がエラー発生を知る事が出来るので、必須の仕組みと言えます。
