コメを噛め

コメを噛め

rerofumi の電子工作メモ

hatena bookmark

Raspberry Pi 2 で気圧や温度が取れる様になったところで次はそれをデータとして蓄積して行きたいわけです。
単純にテキストファイルに追記していくだけでも良いのですが、ここのところ継続して計測したいという要求が多いので簡単なロガーサーバーを作ることにしました。名付けて「なんログ」です。

要求としては、

  • ネットワーク上にあるサーバーに設置、幾つかの機器からのログをポストするだけで記録してくれる
  • IoT的な機器から使える様にシンプルな HTTP API を備える
  • どこからのログか分かるように分別タグを付けられる
  • 複数機器からのアクセスでも大丈夫
  • 最初は簡単にテキストファイルに記録することろから

といったあたりで。
2015-12-19_054015

システムとしては上図のようになる。

趣味の問題で 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