ExceptionNotification::Slacky

Rails とかで例外が発生したときに Slack に通知するやつ作った。 exception_notification という便利 gem のプラグインとして動く。

morygonzalez/exception_notification-slacky

morygonzalez/exception_notification-slacky

Notify exception to slack channel. Contribute to morygonzalez/exception_notification-slacky development by creating an account on GitHub.

github.com

実は ExceptionNotification 本体に SlackNotifier あるんだけど、通知内容があっさりしてて自分たちのユースケースには合わなかった。 IRC 使ってた頃は同僚のスーパープログラマー @udzura さんが作った exception_notification-ikachan を使ってて、それと同程度のエラー通知が来るのを Slack で実現したかった。

世の中には Airbrake のような便利なサービスあって、エラーの統計情報とか取ってまとめて通知してくれたりするものもあるけど、甘えてはいけない。エラーが即 Slack に通知されることで、バグを放っておくと Slack のチャンネルがてんやわんや状態になって業務に支障が出るのでバグを直すインセンティブが生まれ、ソフトウェアの品質が向上していくのである。

Pull Request 、レビューしないといけないものが貯まってしまってつらいと感じることが多かった。そういうつらみを解消するための ruboty プラグイン作った。

morygonzalez/ruboty-check_pr_please

ruboty-cron と組み合わせて

@ruboty [GitHub の Issue のラベル名]のPull Request

という感じで job 登録しておくと、毎時決まった時間になったら Bot が Pull Request のレビューを促す。

便利。

使い方

  1. ruboty の Gemfile に gem 'ruboty-check_pr_please', github: 'morygonzalez/ruboty-check_pr_please' と追加する
    • 定期的に動かすためには ruboty-cron もいる
  2. ruboty の .env に GITHUB_ACCESS_TOKENGITHUB_REPOSITORY='owner/repo' を書く
  3. ruboty をデプロイする

開発時に苦労した点

ruboty 、 rubory とタイポしてしまうこと多くてつらかった。

九州新幹線

関わっているサイトの Rails のバージョンが 3.2.20 から 4.1.8 に上がった。自分は割と傍観していて他の人が主にバージョンアップしてたんだけど、いくつかはまりポイントがあって自分も Pull Request 送ったりしたのでやったことを書いときます。

1. session に注意

Rails 4 から Flash メッセージ(ログインしましたとか)を格納する session のオブジェクトが普通の Hash になってる。 Rails 3 ではこれは FlashHash とかいうやつ。

Rails 3 から Rails 4 へのアップグレードで一旦 Rails 4 を出してやっぱりやめて Rails 3 に戻したりとか、ロードバランサーに Rails 3 と Rails 4 で動くサーバーを混ぜてリクエスト捌いたりするとまずいことになる。

Rails 4 のサーバーで session 出来た人が次にリクエストしたときに Rails 3 に当たるとログイン後とかに session に残っているメッセージを消そうとする処理とかで NoMethodError が発生して落ちてしまう。しかもたちが悪いことに Rack 層で死んでしまったりするから皆さんよく使ってると思われる ExceptionNotification とかで気づくことが出来ない。これはつらい。

対処法としては Hash クラスをオープンしてモンキーパッチするというのがある。こういうの。

Rails 4 FlashHash Upgrade Gotcha | Jason Neylon's Blog

↑のだと #alert とか #notice が呼ばれたときにエラーになるので自分は以下のようにした。

# NOTE Rails 4 と Rails 3 を混ぜて使うと Hash#sweep が見つからなくてエラーに
# なるようなのでモンキーパッチします。
# 参照: http://jasonneylon.wordpress.com/2014/08/27/rails-4-flashhash-upgrade-gotcha/

class Hash
  def now
    Rails.logger.warn "Stubbing now during upgrade"
    {}
  end

  def keep
    # stub keep for upgrade purposes
    Rails.logger.warn "Stubbing keep during upgrade"
  end

  def sweep
    # stub sweep for upgrade purposes
    Rails.logger.warn "Stubbing sweep during upgrade"
  end

  def alert
    Rails.logger.warn "Stubbing alert during upgrade"
    self[:alert]
  end

  def notice
    Rails.logger.warn "Stubbing notice during upgrade"
    self[:notice]
  end
end

ただこれもパーフェクトではなくて、何もしないように上書きしているだけなのでログイン後のメッセージとか削除後のメッセージが消せなくなったりする。それでも 500 エラーになるよりかはましなのでどうしても Rails 3 と Rails 4 を混ぜて投入したいみたいときなんかは有効。

2. 絵文字に注意

Rails 3 の頃は ActiveRecord が絵文字を DB に保存することが基本的になかった。ユーザーが POST してきたフォームの中に絵文字が含まれてたら絵文字のところでテキストをぶった切って DB に保存するような挙動だった。しかし Rails 4 からは ActiveRecord は絵文字を素通りさせるようになってしまったので困ったことになる。

絵文字を DB に保存するためには、 MySQL の場合は DB のテキストエンコーディングを utf8mb4 というやつにしてないといけない。ただの utf8 だと保存時に Mysql2::Error: Incorrect string value というエラーが出て DB に保存できない。emojimmy のような gem を使えば utf8mb4 でない DB でも使えるけど、 stores_emoji_characters :column_name を忘れずにモデルに定義しないといけない。たとえば購入時に購入した製品のスナップショットを注文テーブルに取るような DB 設計だと、製品テーブルのカラムは stores_emoji_characters してたとしても注文テーブルのカラムを stores_emoji_characters し忘れていて死亡、というような悲劇が起こり得る。

いまはスマートフォンの時代で、ユーザーが入力してくるフィールドには必ず絵文字が含まれると思っておいた方がいい。スマートフォンをメインで使ってる人たちは開発者が想定しないようなフィールド(名前の敬称とか)に平気で絵文字を使ってくる。下手すりゃ住所や名前にも絵文字を入れて送ってくるかも知れない。アスキー文字しか受け付けないようなフィールドは JavaScript やサーバーサイドでバリデーション行ってると思うけど、マルチバイト文字列を受け付けるフィールドの場合はせいぜい長さくらいしかチェックしてないと思う。チェックを入れて絵文字を弾くことも可能だけど、スマートフォンの時代の流れに反しているしユーザーを失うことになりそう。これから新規でサービスを作ってデータベースに MySQL を使う場合はエンコーディングは utf8mb4 にしておいた方がいい。

他にも script/rails が bin/rails に変わってること忘れてて rails runner なバッチ処理が動いてなかったとか、 paranoia.gem の Rails 4 対応バージョンで物理削除のときに呼び出すメソッド名が変わっててはまったとかいろいろあったけど大きなところは上の session と絵文字だった。開発環境で使ってるときには気がつかず本番に出すまで気がつきにくいという意味で非常にやっかいな現象だと思う。

これから Rails 4 に上げる皆さんは頑張ってください。応援しています。

Lokka 、フォームの DOM が bot で解析しやすいのか大量にスパムコメントが登録される。スパムを一括削除する機能は自分で作って Lokka にパッチ送って取り込まれたけど、 Lokka の ORM の DataMapper は一括削除するときに馬鹿っぽい SQL 投げて一括削除は処理が重いし、そもそもスパムコメントを定期的に消すという行為自体が面倒くさい。

むかし P_BLOG でブログ書いてた頃もある頃からスパムコメントが大量に来るようになったので reCAPTHA を導入してみたらとんとスパム来なくなった。

reCAPTCHA

また reCAPTCHA 使おうとして久しぶりに調べてみたら Google のプロジェクトになってた。

Coming Soon: reCAPTCHA 2

設置してから二日くらい経つけどとりあえずスパムコメントは来なくなった。便利。念のためプラグイン化しておいた。

morygonzalez/lokka-recaptcha

Rails の E2E テスト、ずっと Capybara + Poltergeist (PhantomJS)でやろうとしてたけど結局うまくいかなかった。

つらいポイントいろいろあって、 SSL 通信が必要なアクションでリダイレクトが発生してうまく動かないとか、 RSpec の use_transactional_fixtures オプションが利用できなくなるため Database Cleaner とか使う必要あったりとか、Parallel Test するときだけ通らないテストケースあったりとか、高速なマシンを使うときだけ落ちる、 Jenkins でだけ落ちる、といった問題が出てきたりする。

この手の問題、 sleep を入れたりとか mutex 化したりとか場当たり的な対応でテスト通すようにできることはできるけど、 unit テストのようにずーっと通る状態を維持し続けることが難しい(上に書いたように特定の端末でだけ落ちたりする)。おまけに何をやっても RackTest を使う場合に比べてテストが遅くなる。 Pull Request ビルダーでテストが実行し終わるまでに 15 分とかかかったりする。良くない。

かといって JavaScript のテスト書かない訳にはいかない。 JavaScript いっぱい使っててテストがないのはやっぱり不安が大きい。結局、JavaScript のコードを DOM べったりな状態から切り離して、サーバーサイドの Rails アプリケーションの挙動は request spec なり controller spec なりでテストして、 JavaScript のテストは JavaScript で行うのが良いような気がした。どうしてもエンドツーエンドテストやりたかったら Selenium 使うしかない気がする。これはスピードはあきらめて、 Pull Request ビルダーとかでビルドするときはテストやらずに、 master ブランチの定期ビルドとかでぶっ壊れてないか試す、みたいな方法が良いと思う。

もうこれ以上、エクセルにスクリーンショット貼り続ける訳にはいかないんだ。

Rails のログファイルを tail -f で見たいんだけど余計なものはフィルタリングして表示されないようにしたかった。最初は以下のようにしてみた。

⚡ tail -f log/development.log | grep -v -e ‘asset|Cache|Rendered’

これだと条件にマッチする行は表示されなくなるけど改行が削除されずに空行がたくさん表示される。これでは見やすいとは言えない。以下のように sed で空行を削除するようにしてみた。

⚡ tail -f log/development.log | grep -v -e ‘asset|Cache|Rendered’ | sed -e ‘/^$/d’

しかしこうすると必要な情報まで表示されなくなってしまう。 tail ではなく cat とかでやると望んだ通りになる。 tail -> grep -> sed の流れだとうまくいかないぽかった。

“tail grep sed” でググったら以下のような記事を発見したので試しに grep に —line-buffered オプションを渡してみた。

» grep, awk, sed でバッファしない方法 TECHSCORE BLOG

⚡ tail -f log/development.log | grep -v -e 'asset|Cache|Rendered' --line-buffered | sed -e '/^$/d'

これで望んだ通りの出力になった。便利。

Go 言語でなにかやってみようと思っていたところ、先々週末くらいにアンチポップさんが gyo という、 Go 言語で Yo するやつを作ってたので、 gyo とそのサンプルコードを利用して gyowitter というのを作った。 Yo の MORYGONZALEZ が Yo を受け取ったら Yo を返しつつ Twitter に「また @wyinoue さんから Yo がありました。この人仕事してるんですかね?」とか Post するやつ。一部の異常者から10連続で Yo とか来て軽くうざいと思うことがあったので、 Twitter のこの人こんなに Yo してきて異常者ですよ、というのを知らしめたくて作った。

Go 言語、初めて書いてみたけど簡単なことしかしてないので大した感想述べられるほどは分からない。 JSON で設定ファイル書いてその内容を読み込む対応を昨日今日くらいでやってたんだけど、静的型付けなためか、読もうとする JSON がどういう構造になってるかを分かってないといけなくて、 Ruby とかだととりあえず parse したあと Hash にして好きに扱える感じだけど、読み込む前にどの階層のなんという key のバリューが map になってるとか配列になってるとか分かってないといけないのが窮屈に感じた。

コンパイルしたらバイナリが一つ出来てそれを置けばデプロイ完了(環境構築とかいらない)のは大変便利だと思った。 Mac で書いて Linux 用にクロスコンパイルして scp してデプロイ完了(Linux 側には Go インストールする必要ない)というのはスクリプト言語にはない感じなので新鮮だった。

ただビルドしたバイナリのサイズが結構でかくて、 gyowitter は大したことしてないけど 6MB くらいあって、細い回線でアップロードしようとすると結構つらい。

percol 流行ったときに会社で使ってる 3 年前の MacBook Pro (HDD のやつ)に入れて使ってみたらあまりにもレスポンス悪くて使うの諦めてたんだけど、 Go 言語版の peco を使い始めたら普通に使えて快適だったので、 Go 言語には夢がひろがりんぐな印象を持ってる。新しい言語勉強するの新鮮で頭が若返りするような感覚あるので引き続き勉強してみようと思います。