jsのエラーのログの取りこぼし

この記事はだいぶ前に書かれたものなので情報が古いかもしれません
この画像、ちょっと気に入ってるんで使い回して行きます

おまとめ三行

IEEEEEEEE!!
俺はエンジニアを止めるぞジョジョー!!
カーズみたいに考えるのを止めたりしない方が良い

思えば、今ままでどうしてずっと放置していたのか。自社サービスをリリースしてから一年半が経とうとしているのに……。

私は自分に問いたい。なぜベストを尽くさなかったのかと。なぜ普段からもっとちゃんと考えて開発しないのかと。

普段からもっとちゃんと冷静に考えていれば、素数を数えて常に落ち着いてさえいれば、こんな取りこぼしはなかったかもしれないのに……。



何を言っているか分からねーと思うが

何の話かってーと、javascriptのエラーログに関する話です。ウェブサービス上で何かしらシステムエラーがあったとき、PHPが吐き出すエラーはちゃんとログに残して何かあればチェックしていたのに、javascriptの方のエラーは完全にほったらかしてたって話です。

PHPに比べると、javascriptってブラウザによって挙動が違ったりするじゃないですか。エンジニアだったら(たぶん)IE8をメインブラウザとして使用することはないと思いますが、ユーザーさんはそうも行かない。IE8とかバリバリ使って来る。てゆーか、ぶっちゃけ仕事で運用してる自社サービスを使っているユーザーさんの使用ブラウザを見てみると、IE8が一番多い。圧倒的に多い。DIOが「WRYYYYYYY!!」って叫ぶところを思わず「IEEEEEEEE!!」って叫んじゃいそうなくらい多い。

僕だって開発者の端くれですから、もちろんIE8の挙動もチェックします。IE9もチェックします。10も11もチェックする。そして一応、バグがないことはちゃんと確認してから本番環境にアップします。

でもいざユーザーさんがサービスを利用してみると、おそらくこれはjavascriptのエラーが出てるせいだなっていう感じのクレームが来る。

これを、今までの僕は、ずっと放置してた……というか、たぶんjavascriptが正常に動いてないせいだからってことで、もしIEの互換モードをONにしているならOFFにしてくださいとか、その程度の対応しかしてなかった。「おそらくjavascriptのエラーが出てると思うので、どんなエラーが出てるかインスペクタを見てエラー内容を送って下さい。F12を押してチェックしてください」って言えれば楽なんだけど、さすがにそういうわけにもいかない。

互換モードを切るとか、その程度の対応で解決するならまだいい。でもそれじゃ動くようにならないこともある。一応、考えられそうな原因を一通り試してもらったりするんだけど、何回もやり取りして、それでも動かないとさすがにユーザーさんもイライラしてくる。で、結局最後まで解決できなくて、パッタリと連絡が来なくなったりしたときに、このユーザーさんサービス使うのやめちゃったのかなって、少し反省しつつ、これ以上はどうすることもできなかったって開き直ったりする。幸い、面と向かってキレられたことはないけれど、たぶん画面の向こうでプッツンしていた人は、過去にいたと思う。この場を借りてお詫びします。申し訳ありませんでした。

で、そんなときに思うわけですよ。「あ〜あ、どんなエラーが出てるか分かればすぐに対処できるのに……ってかマジでどんなエラーが出てんだよぉ。全然わかんねーよ!! くそがぁっ!! ああもう開発とかめんどくせえ!! 俺はエンジニアを止めるぞジョジョー!!」って。

でもその一方で、普段は数えることのない素数を考えて気持ちを落ち着けたら、こんなこともふと思ったわけですよ。「javascriptのエラーログって、取る方法あるんじゃねえの?」って。

なぜ今の今までその考えに至らなかったのか。PHP側の方は取らなくて良いとこまで取っているおかげで、ログファイルのフォルダが大変なことになっているというのに。

そんなわけで、javascriptのエラーログを取るようにしました。



具体的な方法が知りたい。そう思った。あなたならどうする……?

僕はCakePHPで開発しているので、それ前提の話になってしまいますが、CakePHPの場合、PHPが出すWarningエラーとかは、Cakeさんが勝手にログファイルに書き込んでくれます。自分でそういうのをオフにするような設定にしない限りは。

一般的なエラー以外でも、自分でここはログが欲しいなって思うことがあれば、わりと簡単にログを吐き出す処理を実装できます。

//error.logに書き込まれる
$this->log('ありのまま起こったことを書き込むぜ');

//sample.logに書き込まれる
$this->log('500エラーとかじゃ断じてねえ', 'sample');

第二引数を省略すると「error.log」というファイルに自動的にログが書き込まれます。任意のファイル名にしたい場合は第二引数にファイル名を指定する。CakePHPのバージョンが2.4だか2.5以降は、error.logやdebug.log以外のログファイルを使いたい場合は、ファイル名の指定をbootstrap.phpでやらなきゃいけないっぽいような話を聞きましたが……ごめんなさい、そこはちゃんと確認してません。何でそんな仕様になったんだろ?

ともあれ、こんな感じのことがjavascriptでもできれば良いわけですね。

じゃあ、どんな風に実装しましょーかっていう話なんですけど、CakePHPとのコンボなら、こんな感じでいけることが分かりました。

//ビュー
window.onerror = function(msg, url, line) {
  var query = '?msg='+encodeURIComponent(msg)+'&url='+encodeURIComponent(url)+'&line='+line;
  new Image().src = '<?php echo $this->Html->url('/error_log/index/') ?>'+query;
}

//コントローラー
class ErrorLogController extends AppController {
  public $name = 'ErrorLog';
  public $uses = array();

  public function index() {
    $this->log($this->request->query, 'js');
  }
}

雑に書くとこんな感じ。GETパラメータにエラーの内容をつけて、任意のURLを呼び出す感じですね。GETパラメータの内容は「$_GET」でも取れますけど、CakePHPが気を利かせて「$this->request->query」ってとこに$_GETの内容を入れてるっぽいので、そっちを使っても良い。

どうやらjavascriptにはエラー発生時に処理が走る「window.onerror」ってのがあるらしく、そこにログ書き込み用の処理を書いておけば、ログファイルにエラーの内容が書き込まれるっていうすんぽーです。

上記のように書いた場合、window.onerrorの第一引数はエラーの内容、URLはページのURL、lineはエラーが発生した行番号が入って来るようです。ただ、lineはブラウザによって0になっちゃったりするので、あまり使えないかもしれない。まあ、どのページを開いたときにどんなエラーが出ているか分かるだけでも、十分に対応は可能でしょう。

「new Image().src = ・・・」は、画像の参照URLにエラーログの書き込み処理用URLを突っ込んでるんですね。書き込み処理のページは画像じゃないんで、実際は何の画像も参照できないわけですけど、こうすることでシステムが画像を読み込もうとして、エラーログの書き込み処理を行ってくれるわけですね。

もちろん、Ajaxを使ってログを書き込んだりしても結果は同じだと思います。

window.onerror = function(msg, url, line) {
  var query = '?msg='+encodeURIComponent(msg)+'&url='+encodeURIComponent(url)+'&line='+line;

  $.ajax({
    url:'<?php echo $this->Html->url('/error_log/index/') ?>'+query, 
    success: function({});
  });
}

こんな感じですかね。

何となく、処理的にはこっちの方が正しいことやってそうですけどね。Image().srcの方は画像じゃないものを画像として読み込もうとしてるわけだし。

じゃあ何で画像参照の方でやってるかっていうと、いくつかの大手サイトさんが実際にそうやってたからです。はてブとか。何か分かんないけど、大手のサイトでも採用してるんなら、俺もそっちにしとこうかなーみたいな感じ。Ajaxよりも画像の方がサーバーの負荷が少なかったりするんですかね……?






とまあ、こんな感じで先日からjavascriptのエラーログも取るようになったわけなんですけど、いやぁ、出てるねー、エラーが。バンバン出てるね。そしてやっぱり大半はIE8で出てるね。パチンコ店が新台入荷したときのようにジャンジャン出てるね。まあパチンコやらないんでよく分かんないけど。

僕に分かるのは、何か問題に直面したとき、それを解決する方法が本当にないのかもっとよく考えた方が良い、カーズみたいに考えるのを止めたりしない方が良いってことと、実はジョジョってちゃんと読んだことないのに、そんな僕みたいなやつにとってもジョジョのセリフは使い勝手が良いんだから、ジョジョってのは本当にすごい作品なんだなってことですかね。

何はともあれ、これでユーザーからのクレームも怖くねえっす。何かjavascriptのエラーで動かないっぽいクレームが来ても「さっさと対応しやがれ」「君が! 泣くまで! バグ取りを止めない!!」って返事してやります。

まだコメントはいただけてないみたい……
もしかしたら何か関連しているかも?