ボクココ

サービス開発を成功させるまでの歩み

Heroku ログと Webhook を使った応用例

ども、@kimihom です。

f:id:cevid_cpp:20180906210024j:plain

先日の Heroku Meetup で、Heroku ログ処理について語ってきた。今回はそれについて語らせてもらう。

speakerdeck.com

Heroku ログ処理の便利さ

実際に自前でサーバー運用していると、当然複数のサーバーがあるわけで、ログはそれら全部のサーバーに吐かれているログを一括管理できる形にしないといけない。ログ収集専用のソフトウェアを各サーバーにインストールしたり、それぞれ定期的に自動で取ってきてどっかに保存するような処理を書いている方もいることだろう。それのインストールや学習、セットアップにどれだけ時間を使わなければならないのだろうか。最終的にやりたいことは、ログを一括で管理して、対象のログが出た時に通知したり、特定のコードを実行させたいといったゴールのはずである。

Heroku の Addon である Papertrail 等を使えば、これらの処理をインストール(heroku addons:add)し、Web 上のインターフェスで簡単に設定するだけで実現できる。手間の排除と、管理が不要になることのメリットは Heroku を利用することの魅力の一つである。

せっかく Heroku を利用するのなら、こうした既に実現された便利なテクノロジーを有効活用すべきだ。自前でサーバー構築して自由自在にできるようになるのと比べて、すぐに使ってすぐに活用できる Heroku のメリットを存分に享受したいところである。

AWS Lambda 上で処理するコード

Heroku じゃなくて Node.js のお話になるけど、補足として以下に記しておく。

本資料に書かれている Node.js のソースコードは、以下のような形となっている。

exports.handler = (event, context, callback) => {
  let body = event.body;
  // Papertrail 通知から JSON 以外の文字列を削除
  let log = JSON.parse(decodeURIComponent(body.slice(8, body.length)));
  
  log.events.forEach(evt) => {
    // Papertrail 側での文字対応
    let json = JSON.parse(evt.message.replace(/^[^{]*|¥+$/g, ""));

    // Enjoy!
  }
}

ログにマッチして Papertrail から送られてくる Webhook のデータは1行のみとは限らない。例えば、1分間にマッチするログが5件くることもある。そのため、Papertrail からくる Webhook データは配列形式で送られてくることになる。

このサンプルでは、シンプルに forEach を使って回している。このサンプルコードの forEach 内で非同期処理を実装してしまった時点で、AWS Lambda の終了条件である callback を呼ぶことは不可能になる点に注意してほしい。あくまで上記サンプルは、forEach 内で同期処理だけをした場合のサンプルコード である。でもログ処理において、Node.js で非同期じゃない処理だけで終わるなんてケースはほとんどないよね。

てことでもし実践的にやるなら、Promiseasync/awaitAsync などを使う必要がある。Node.js 経験者なら当たり前の話なんだけど、フロントエンドでくらいしか JavaScript を触ったことのない方は確実にはまるので気をつけよう。

例えば、以下のような記事が今回のケースに似ている。 async/awaitを、Array.prototype.forEachで使う際の注意点、という話

上記の記事を読めばわかるとは思うが、forEach での非同期処理ってだけでも Promise や Asnyc/Await だと複雑で読みづらい感じになってしまう。んで、私自身 AWS Lambda x Node.js を使った経験から、ぶっちゃけ今でも Async ライブラリは便利だと思っている。Async ライブラリを使えば、waterfall ってのをつかって配列の関数を定義するだけで実現できてしまう。実際には慣れの問題かもしれないが、コードの読みやすさ的に私は Async が気に入っている。

async.waterfall([
  (next) => {
     // some codes..
     // 第一引数はエラーオブジェクト。以降は次の関数に渡す任意の引数
     next(null, arg1, arg2); 
  }, (arg1, arg2, next) {
    // some codes..
    next();
  }
], (err) => {
  console.log("done!");
  callback(err);
})

終わりに

今回の Meetup では、Heroku を使った新しい活用方法の話だったり、そもそも Heroku の何がいいのかについての話もあった。引き続き、Heroku Meetup は Heroku を使っている方やこれから使おうとする方にとって有益な情報共有の場にしていきたいと思っている。

それでは、また次の Heroku Meetup で。 ciao !