この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
最近 Fluentd をプロジェクトで導入しようと考えています。
Fluentd は、あらゆるデータのログを json を使ってシンプルに転送、集約させることができるツールです。
クラウド環境やビッグデータを扱う上で、今後需要が高くなりそうです。
ソーシャルゲーム業界などではかなり実績があるみたいですね。
今回お試しとして、Node.js のエラーログを Fluentd を使って MongoDB に保存するサンプルを作ってみます。
こんな感じ。とてもシンプルです。。
Input Output
+------------------------------------------------+
| |
| Node.js --------> Fluentd --------> MongoDB |
| |
+------------------------------------------------+
インストール
パッケージ形式の td-agent と gem 形式の Fluentd があるみたいですが、
今回はお試しなので、gem を使ってインストールしてみました。(ちなみに Mac OS 10.8 です)
大規模環境では td-agent を使う方がいいと思います。
Ruby の1.9.2以上が必要です。
$ gem install fluentd --no-ri --no-rdoc
起動確認
マニュアルに書いてある通りやってみます。
セットアップ
$ fluentd --setup ./fluent
Installed ./fluent/fluent.conf.
起動
$ fluentd -c ./fluent/fluent.conf -vv &
すんなり起動しました。
MongoDB Output Plugin をインストール
Fluentd はプラグインが豊富です。
今回は、MongoDB の プラグインを fluent-gem というのを使ってインストールします。(td-agentの場合は入っている)
$ fluent-gem install fluent-plugin-mongo
ログの設定
MongoDB のプラグインをインストールしたら、fluent.conf に追記します。
# Single MongoDB
<match mongo.**>
type mongo
host localhost
port 27017
database node
collection error
# for capped collection
capped
capped_size 1024m
# flush
flush_interval 10s
</match>
match mongo.** で受け取るタグを正規表現で指定できます。
この場合、mongo、mongo.x、mongo.x.x... の条件にマッチします。
type 〜 collection は、MongoDB の接続先と保存先のコレクションです。
capped collection というのは、MongoDB のサイズ固定のコレクションです。
これはロギング処理に向いていますので、Fluentd ではこれが推奨されています。
10秒間隔で MongoDB にフラッシュします。
Logger のインストール(Node.js)
続いて、Node.js 側です。
ちなみに express を使ってます。
以下のようにスケルトンを作成したら、package.json を編集します。
express fluentd-sample
「package.json」
{
"name": "application-name",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "node app"
},
"dependencies": {
"express": "3.0.3",
"fluent-logger": "latest",
"jade": "*"
}
}
fluentd の Logger である fluent-logger を使う指定をします。
続けて、npm start。
$ npm start
fluent-logger@0.2.1 node_modules/fluent-logger
└── msgpack@0.1.7
0.2.1がインストールされました。
例外処理のコード(Node.js)
例外処理を記述します。
ちなみに CoffeeScript です。
「app.coffee」
'use strict'
###
Module dependencies.
###
express = require 'express'
routes = require './routes'
path = require 'path'
logger = require 'fluent-logger'
logger.configure 'mongo', {host: 'localhost', port: 24224}
app.configure ->
app.set 'port', process.env.PORT or 3000
app.set 'views', __dirname + '/views'
app.set 'view engine', 'jade'
app.use express.favicon()
app.use express.logger 'dev'
app.use express.bodyParser()
app.use express.methodOverride()
app.use app.router
app.use express.static path.join(__dirname, 'public')
app.use (err, req, res, next) ->
logger.emit 'test', {error: err.message}
res.render 'error', {status: 500, title: '500 Internal Server Error', err: err}
app.get '/', routes.index
app.listen process.env.PORT || 3000, ->
console.log 'Express server listening on port ' + app.get 'port'
エラーをハンドリングしたら、Fluentd にログを飛ばし、その後エラー画面に遷移させています。
その際のタグとラベルの組み合わせが mongo.test と指定しているので、先ほどの fluent.conf の構文にマッチするというわけです。
routes/index では、テスト的にわざとエラーを発生させるようにします。
「routes/index.coffee」
'use strict'
###
Module dependencies.
###
exports.index = (req, res, next) ->
next new Error('fluentd test')
エラーを発生させる
Fluentd を起動しておいて、MongoDB も起動します。
$ sudo mongod
そして Node.js を起動し、curl でアクセスします。
$ node app.js
$ curl localhost:3000/
<!DOCTYPE html><html><head><title>500 Internal Server Error</title><link rel="stylesheet" href="/stylesheets/style.css"></head><body><h1>500 Internal Server Error</h1><h1>Error: Fluentd test</h1></body></html>
ログの確認
ちょっと待って MongoDB を確認してみます。
$ mongo
$ use node
$ db.error.find()
{ "_id" : ObjectId("5105183acaf52f08dd00000e"), "error" : "Fluentd test", "time" : ISODate("2013-01-27T12:06:15.513Z") }
ちゃんと出力されてますね。
まとめ
Fluentd と MongoDB はデータを json 形式で扱うため、高いスループットでの挿入が実現できます。
(もちろん Node.js も JavaScript なので json は親和性が高いです)
MongoDB の capped コレクションに保存すると、古いデータが消えていってしまうので、同時にS3にも保存した方がいいかもしれません。
同時にS3にも保存する方法は、以下のブログに書いてあります。
Amazon LinuxにFluentdをインストールしてS3とMongoDB連携する
あとは定期的に Amazon Glacier にアーカイブするなどもいいですね。