lsyncdで検知したファイル更新情報をfluentdに投げる


# lsyncd ってのは？

Linux上で動作する[lsyncd](https://github.com/axkibe/lsyncd "lsyncd")は，ファイルシステムのイベント(作成・削除・移動/リネーム・属性変更などなど)を通知してくれるLinuxカーネルAPIを利用し，ほぼリアルタイムにファイルの同期等を行ってくれる超便利なソフトウェアです．

変更のあったファイルやディレクトリ内容をrsyncを利用して別サーバや別ディレクトリにコピー/削除する，という使い方がスタンダード(?)かと思いますが，ファイル変更時にコマンドを起動することも可能なので，画像ファイルのサムネイルを作成/削除する，などの設定例もドキュメントに記載されています．

# fluentd ってのは？

「データコレクタ」「データログ収集ツール」などと呼ばれる，ログを始めとしたデータの集約・転送などを簡単に，安全に行ってくれる，これまた超便利なソフトウェアです．
input・output・filterなどといった入出力・フィルタリング機能はプラグイン式になっており，様々なデータソース・データストアに対応したプラグインが公開されていますし，いざとなれば自分で作ることも可能です．

# やりたいこと

* 特定のディレクトリに追加されたファイルの情報をプログラムに渡したい
* 全ての処理が完了した段階でファイルを削除・または移動／リネームしたい

# やってみる

lsyncd の設定は全て lua という言語で記述されています．
もしかして，lua 自体にsocketやhttp-requestみたいな機能があるんじゃね？と思って調べてみると，ありました．

* [LuaSocket](http://w3.impa.br/~diego/software/luasocket/home.html "LuaSocket")

Lua にSMTP,HTTP,FTP 等のネットワーク機能を追加してくれる拡張ライブラリです．

## lsyncdとLuaSocketのインストール

今回はDebian系OSだったので，apt-getで一発インストールでした．

```bash:command
$ sudo apt-get install lsyncd liblua5.1-socket2
```

LuaSocketのファイル群は /usr/share/lua/5.1/ 配下に展開されていました．

```bash:command
$ ls /usr/share/lua/5.1/
ltn12.lua  mime.lua  socket/  socket.lua
```

# lsyncd の設定

## 環境変数の設定

インストールしたLuaSocketを読み込めるよう，環境変数でパス等を指定してやります

```bash:command
$ sudo vi /etc/defaults/lsyncd
```

```file:/etc/defaults/lsyncd
#
LUA_INIT=
LUA_PATH='/usr/share/lua/5.1/?.lua;?.lua'
LUA_CPATH='/usr/share/lua/5.1/?.so;?.so'
```

## 設定ファイル lsyncd.conf.lua 

では，おもむろに lsyncd 設定ファイルを記述していきます．
今回は

* 監視するディレクトリ : /test/data/
* 新規作成イベントのみ監視
* fluentd は
	* host : 127.0.0.1
	* port : 8888
	* tag  : inotify.<ディレクトリ毎の識別子>
	* jsondata
		* dirpath : 監視ディレクトリパス
		* filename : 追加ファイル名

ということにします．

```lua:lsyncd.conf.lua
-- LuaSocketのhttpライブラリを読み込み
http = require("socket.http")

settings = {
-- 省略
}

--
-- function 定義
-- 追加されたファイル情報を fluentd に通知する
--
-- @param tag   振り分け用タグ(必須)
-- @param dir   監視ディレクトリパス
-- @param path  追加されたファイル名
-- @return true 通知成功(HTTPステータスコードが200) / false 通知失敗
--
fluentlog = function(tag, dir, path)
    local url  = "http://127.0.0.1:8888/inotify.".. tag
    local json = 'json={"dirpath":"'.. dir ..'","filename":"'.. path ..'"}'

    local d, c, h, s = http.request(url, json)
    -- HTTPステータスが200であればtrue，それ以外は失敗としてfalseを返却する
    if c == 200 then 
        return true
    else    
        return false
    end
end

--
-- action 定義
-- ディレクトリを監視し，追加されたファイル情報を通知する
--
fluentnotify = { 
    maxProcesses = 1,
    delay = 1,

    action = function(inlet)
    	local event = inlet.getEvent()

	    if event.isdir then 
    		-- ディレクトリは処理対象外
    		inlet.discardEvent(event)
        	return 
    	end

    	if event.etype == "Create" then
    		local config  = inlet.getConfig()
       		local path    = event.pathname 

        	if fluentlog(config.tag, event.source, path) then
        		inlet.discardEvent(event)
        		return 
	       else        
    	       return "again"                                  
        	end     
    	end
    	
    	inlet.discardEvent(event)
    end,
}

--
- 監視すべきディレクトリとタグを指定
--
sync {
    fluentnotify,       	-- actionを指定
    source="/test/data",	-- 監視するディレクトリ
    tag="testdata",      -- fluent へのタグ 
    recursive=false,    	-- 下位ディレクトリは監視しない
}
```

# fluentd 側の設定例

こんな感じでしょうか．
実際には，これからさらにmongo_tail とか out_exec などのプラグインを使って処理を行わせています．


```file:/etc/fluentd/fluentd.conf
  : 中略

#
# HTTP input source は必須．これが無いと受け止められない
# http://localhost:8888/<tag>?json=<json>
<source>
  @type http
  @id   http_input

  bind  127.0.0.1
  port  8888
</source>

  : 中略

<match inotify.**>
	@type	copy  # ストリームを2つにコピー
	
	<store>
		@type	file	# 1つはファイルに保存
		
		path	/var/log/fluentd/inotify.log
		compress	gz
	</store>
	
	<store>
		@type	mongo	# もう1つはMongoDBに保存
		
		database	testdb
		collection	testdatas
		
		host		mongohost
		port		27017
	</store>
</match>

```

# 最後に

ご覧いただきありがとうございます，何かのお役に立てたら幸いです．
間違い等ありましたらご指摘くださいm(_ _)m