昔Perlで書いたやつのRuby1.9版をとりあえず作りました
ログファイルを指定すると、前回実行からの差分を出力してくれます
$ echo 111 > test.log $ ./logtail.rb test.log 111 $ echo 222 >> test.log $ echo 333 >> test.log $ ./logtail.rb test.log 222 333
#!/usr/local/bin/ruby # ログの差分出力スクリプト # 使用例 # 前回実行からの追記分を出力する # $ logtail.rb access_log # # ログのローテートに対応させる場合はオプションでローテートファイルを指定する # $ logtail.rb access_log -r access_log.1 # # デフォルトでは/tmp/<ログファイル名>.logtail.offsetに前回出力情報が保存される。 # 日付つきファイルなどファイル名が変わる場合、 # 同じファイルを複数バッチから処理する場合はこのファイルパスを個別に指定すること # $ logtail.rb access_log.20130602 -r access_log.20130601 -o /tmp/test_access_log.logtail.offset # # TODO:オフセットファイルの作成、書き込み権限のチェック # TODO:ファイル名が同じだとデフォルトのオフセットファイル名が重複する。フルパスのハッシュ値とかつけたほうがいいかな require 'optparse' require 'pp' options = {} OptionParser.new do |opt| opt.on('-r', '--rotated_log=VAL') {|v| options[:rotated_log] = v} opt.on('-o', '--offset_file=VAL') {|v| options[:offset_file] = v} opt.parse! end log = ARGV.shift exit 1 unless log class LogTail def initialize(log, options) @log = log @rotated_log = options[:rotated_log] @offset_file = options[:offset_file] || "/tmp/#{File.basename(log)}.logtail.offset" @offset = load_offset end def load_offset return nil unless File.exist? @offset_file line = File.read(@offset_file) ino, pos = line.split(',') { ino: ino.to_i, pos: pos.to_i} end def save_offset(ino, pos) File.write(@offset_file, "#{ino},#{pos}") end def tail_print(log, pos) file = File.open(log) file.seek(pos) file.each{|line| puts line} end_pos = file.pos file.close return end_pos end def tail_rotated_log return unless @offset && @rotated_log && File.exists?(@rotated_log) stat = File.stat(@rotated_log) if @offset[:ino] == stat.ino && @offset[:pos] < stat.size end_pos = tail_print(@rotated_log, @offset[:pos]) save_offset(stat.ino, end_pos) end end def tail_log return unless @log && File.exists?(@log) stat = File.stat(@log) if @offset && @offset[:ino] == stat.ino if @offset[:pos] < stat.size start_pos = @offset[:pos] elsif @offset[:pos] > stat.size save_offset(stat.ino, stat.size) return else return end else start_pos = 0 end end_pos = tail_print(@log, start_pos) save_offset(stat.ino, end_pos) end def execute tail_rotated_log tail_log end end LogTail.new(log, options).execute
muninやnagiosとかと連携して、各種ログの監視、集計結果のグラフ化などに使えます。
例えばこういうふうにApacheの平均レスポンスタイムをmuninでグラフ化したい場合、
httpd.confのLogFormatにレスポンスタイムを追加して、
# %Dを追加 LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %D" combined # cronologやrotatelogsで日付ファイルに出力してる場合 CustomLog "|/usr/sbin/cronolog -S /var/log/httpd/access_log.latest /var/log/httpd/access_log.%Y%m%d" combined
こういうmuninプラグインを作ればOKです
#!/bin/bash today=$(date +%Y%m%d) yesterday=$(date +%Y%m%d -d "1 days ago") log=/var/log/httpd/access_log.$today rotated_log=/var/log/httpd/access_log.$yesterday offset_file=/tmp/munin_app_response_time.logtail.offset if [ "$1" = "config" ]; then echo 'graph_title response time' echo 'graph_args --base 1000 -l 0' echo 'graph_vlabel msec' echo 'graph_scale no' echo 'graph_category app' echo 'avg.label avg' exit 0 fi # TODO:muninをがしばらく停止すると次回実行時に大きな値が出てしまうので、オフセットファイルのタイムスタンプをチェックする if [ -e "$offset_file" ];then avg_msec=$(/usr/local/bin/logtail.rb $log -r $rotated_log -o $offset_file | perl -alne '$c++;$s+=$F[-1];END{print int(($s/$c)/1000)}') else # 初回は集計せずに捨てる /usr/local/bin/logtail.rb $log -r $rotated_log -o $offset_file > /dev/null avg_msec='N' fi echo "avg.value $avg_msec"
仕組みや実装上、あまり厳密な集計には向かないですがこういうグラフ作成とかログ監視には充分かな