posted by rerofumi
2015/12/19 土曜日 17:55:42
Raspberry Pi 2 で気圧や温度が取れる様になったところで次はそれをデータとして蓄積して行きたいわけです。
単純にテキストファイルに追記していくだけでも良いのですが、ここのところ継続して計測したいという要求が多いので簡単なロガーサーバーを作ることにしました。名付けて「なんログ」です。
要求としては、
- ネットワーク上にあるサーバーに設置、幾つかの機器からのログをポストするだけで記録してくれる
- IoT的な機器から使える様にシンプルな HTTP API を備える
- どこからのログか分かるように分別タグを付けられる
- 複数機器からのアクセスでも大丈夫
- 最初は簡単にテキストファイルに記録することろから
システムとしては上図のようになる。
趣味の問題で RabbitMQ というキューイングシステムを使うことにした。これにより HTTP に対してほぼ同時にリクエストが来てもシリアライズして、store は自分のペースで一個ずつ処理することができる(=ロック機構が要らない)というメリットと、AMQP というプロトコルでもロガーにポストすることができるというメリットを持つ。
デメリットとしては今回の目的の割りに大規模なアプリというあたりか。
このへん部屋内サーバーのどっかにあれば他と使い回すものなので個人的にはOKである。
API は単純に HTTP で POST されたものを Queue に積むだけ。
ruby + sinatra で記述するのでコードも短い。
store は Queue からアイテムを取り出してログに書き込む部分だが、今回はテキストファイルに書き込んでいる。
ここを改変することで DB に記録したり、別な形で保存したり色々できる様になる。
使う ruby gem は RabbitMQ のアクセスライブラリである ‘rabbit’ と ‘sinatra’ の2つ。
—– nanlog_api.rb
require 'sinatra'
require 'bunny'
require 'yaml'
def queue(item)
path = File.expand_path(File.dirname(__FILE__))
conf = YAML.load_file(path+"/nanlog.conf")
server = "amqp://"+conf['mqlogin']+":"+conf['mqpasswd']+"@"+conf['mqserver']+":5672"
#
conn = Bunny.new(server)
conn.start
ch = conn.create_channel
queue = ch.queue("nandemo_logger")
ex = ch.default_exchange
ex.publish(item, :routing_key => queue.name)
conn.close
end
post '/*' do |path|
item = path+"\n"+request.body.read
if path.size > 0 then
queue(item)
end
end
—– nanlog_store.rb
require 'bunny'
require 'yaml'
require 'date'
require 'json'
require 'fileutils'
# ----- log
def store(dir, item)
item.gsub!(/\r\n/, "\r")
item.gsub!(/\n/, "\r")
chunk = item.split("\r")
return if chunk.size == 1
index = chunk[0]
chunk.delete_at(0)
item = chunk.join
date = DateTime.now
data = { "time" => date.to_s, "log" => item }
filename = sprintf("%4d%02d%02d.log", date.year, date.month, date.mday)
dir += "/"+index
if File.exist?(dir) == false then
FileUtils.mkdir_p(dir)
end
open(dir+"/"+filename, "a") do |fp|
fp << data.to_json
fp << "\n"
end
end
# ----- config
conf = YAML.load_file(ARGV[0])
server = "amqp://"+conf['mqlogin']+":"+conf['mqpasswd']+"@"+conf['mqserver']+":5672"
datadir = conf['storedir']
# ----- main
conn = Bunny.new(server)
conn.start
ch = conn.create_channel
queue = ch.queue("nandemo_logger")
queue.subscribe(:block => true) do |delivery_info, properties, body|
store(datadir, body)
end
conn.close
– – – – – – – – – –
Queue に積まれる文字列は 2行であると規定し、1行目を分類タグ、2行目がデータとして扱われる。
記録されるログは分類タグ名のディレクトリが掘られ、その中に西暦月日の YYYMMdd.log というファイルネームに JSON 型でタイムスタンプとデータの両方が記録されていく。
RabbitMQ にログインする必要があるため、ログイン名とパスワードを ‘nanlog.conf’ というファイルに書いておいてそれを読み込んでいる。
nanlog.conf の書式はこんな感じ
—– nanlog.conf
storedir: ./store
mqserver: rabbitmq.service.local
mqlogin: nanlog
mqpasswd: ********
– – – – – – – – – –
他はこれを Docker コンテナ化してからサーバーにしているんだけれども、そのへんは省略。
これで自分用の小規模ロガーが完成した。
あとは色んな機器での観測結果をじゃかじゃかポストしていくだけである。
溜まったログをどう解析して使うかはまた別のお話。

Leave a Reply