Skip to content

Instantly share code, notes, and snippets.

@susatadahiro
Last active September 25, 2015 01:48
Show Gist options
  • Save susatadahiro/843281 to your computer and use it in GitHub Desktop.
Save susatadahiro/843281 to your computer and use it in GitHub Desktop.
munin plugins: rails log time(total, view, db) at rails3.0
munin-plugin: rails_response_time
# original source: http://blog.tkmr.org/tatsuya/show/420-munin-rails
# rails_log_chopper
$ ruby ./script/rails_log_monitor.rb -l log/rails_log_monitor.log
# munin_plugin
$ sudo ln -s /usr/share/munin/plugins/rails_response_time /etc/munin/plugins/avg_response_time
$ sudo ln -s /usr/share/munin/plugins/rails_response_time /etc/munin/plugins/max_response_time
$ sudo /etc/init.d/munin-node restart
check process rails_log with pidfile /home/ubuntu/apps/dtr/shared/pids/rails_log_monitor.pid
start program = "/home/ubuntu/apps/dtr/shared/script/rails_log start" with timeout 60 seconds
stop program = "/home/ubuntu/apps/dtr/shared/script/rails_log stop"
group rails
#!/usr/bin/env ruby
# munin plugin to render rails response time graphs
# link to /etc/munin/plugins/avg_response_time and /etc/munin/plugins/max_response_time
require 'open-uri'
PORT = ENV['PORT'] || "8888"
def config
title = File.basename($0).split('_').map{|s| s.capitalize }.join(' ')
upper_limit = "3000"
upper_limit = "1000" if title.downcase.match(/^avg/)
upper_limit = "3000" if title.downcase.match(/^max/)
config=<<__END_CONFIG__
graph_title #{title}
graph_vlabel response time [ms]
graph_category rails
graph_args --lower-limit 0 --upper-limit #{upper_limit}
db.label db
db.draw AREA
view.label view
view.draw STACK
total.label total
total.draw LINE1
__END_CONFIG__
puts config
end
def get_data(read_only=false)
qs = read_only ? '?debug' : ''
puts open("http://127.0.0.1:#{PORT}/#{File.basename($0)}#{qs}").read
end
case ARGV.first
when 'config'
config
when 'debug'
get_data(true)
else
get_data
end
#!/usr/bin/env ruby
# munin plugin to render rails response time graphs
# link to /etc/munin/plugins/avg_response_time and /etc/munin/plugins/max_response_time
require 'open-uri'
PORT = ENV['PORT'] || "8888"
def config
title = File.basename($0).split('_').map{|s| s.capitalize }.join(' ')
upper_limit = "3000"
upper_limit = "1000" if title.downcase.match(/^avg/)
upper_limit = "3000" if title.downcase.match(/^max/)
config=<<__END_CONFIG__
graph_title #{title}
graph_vlabel response time [ms]
graph_category rails
graph_args --lower-limit 0 --upper-limit #{upper_limit}
db.label db
db.draw AREA
view.label view
view.draw STACK
total.label total
total.draw LINE1
__END_CONFIG__
puts config
end
def get_data(read_only=false)
qs = read_only ? '?debug' : ''
puts open("http://127.0.0.1:#{PORT}/#{File.basename($0)}#{qs}").read
end
case ARGV.first
when 'config'
config
when 'debug'
get_data(true)
else
get_data
end
#!/bin/bash
PID_FILE=/home/ubuntu/apps/dtr/shared/pids/rails_log_monitor.pid
SH_LOG=/home/ubuntu/apps/dtr/shared/log/rails_log_monitor.log
case $1 in
start)
echo $$ > ${PID_FILE};
exec /usr/local/rvm/bin/rvm-shell 1.9.2 -c "ruby /home/ubuntu/apps/dtr/shared/script/rails_log_monitor.rb -d -e production" >> ${SH_LOG} 2>&1
;;
stop)
kill `cat ${PID_FILE}` ;;
*)
echo "usage: rails_log {start|stop}" ;;
esac
exit 0
#!/usr/bin/env ruby
# daemon to scrape response times from rails logs
#
# Completed in 521ms (View: 142, DB: 364) | 200 OK [https://xxx.com/]
#
require 'rubygems'
require 'file/tail'
require 'mongrel'
require 'yaml'
require 'kconv'
RAILS_ENV = ENV['RAILS_ENV'] || "production"
PORT = ENV['PORT'] || "8888"
IGNORE_PATTERNS = %r{heartbeat}.freeze
class Accumulator
def initialize
@values = Array.new()
@max = 0
end
def add( n )
@values << n
@max = n if n > @max
end
def average(read_only=false)
return_value = if @values.length == 0
nil
else
@values.inject(0) {|sum,value| sum + value } / @values.length
end
@values = Array.new() unless read_only
return_value
end
def max(read_only=false)
return_value = @max
@max = 0 unless read_only
return_value
end
def count
@values.length
end
alias_method :length, :count
alias_method :size, :count
end
LOGFILE = File.join(File.dirname(__FILE__), '..', 'log', "#{RAILS_ENV}.log")
$response_data = { :total => Accumulator.new(),
# :rendering => Accumulator.new(),
:view => Accumulator.new(),
:db => Accumulator.new() }
Thread.abort_on_exception = true
logtail = Thread.new do
File::Tail::Logfile.tail(LOGFILE) do |line|
if line.toutf8 =~ /^Completed/
# STDOUT.puts line
line.match(/^Completed .* in ([0-9.]+)ms \(Views: ([0-9.]+)ms \| ActiveRecord: ([0-9.]+)ms\)/)
total = $1
view = $2
db = $3
# STDOUT.puts "---"
# STDOUT.puts total
# STDOUT.puts view
# STDOUT.puts db
if total and view and db
$response_data[:total].add(total.to_f)
$response_data[:view].add(view.to_f)
$response_data[:db].add(db.to_f)
end
end
end
end
class ResponseTimeHandler < Mongrel::HttpHandler
def initialize(method)
@method = method
end
def process(request, response)
response.start(200) do |head,out|
debug = Mongrel::HttpRequest.query_parse(request.params["QUERY_STRING"]).has_key? "debug"
head["Content-Type"] = "text/plain"
output = $response_data.map do |k,v|
value = v.send(@method, debug)
formatted = value.nil? ? 'U' : sprintf('%.5f', value)
"#{k}.value #{formatted}"
end.join("\n")
output << "\n"
out.write output
end
end
end
h = Mongrel::HttpServer.new("127.0.0.1", PORT)
h.register("/avg_response_time", ResponseTimeHandler.new(:average))
h.register("/max_response_time", ResponseTimeHandler.new(:max))
h.run.join
#!/usr/bin/env ruby
# using webrick not mongrel
# daemon to scrape response times from rails logs
require 'rubygems'
require 'file/tail'
require 'webrick'
require 'yaml'
RAILS_ENV = ENV['RAILS_ENV'] || "production"
PORT = ENV['PORT'] || "8888"
IGNORE_PATTERNS = %r{heartbeat}.freeze
#STDOUT.puts "--starting"
class Accumulator
def initialize
@values = Array.new()
@max = 0
end
def add( n )
@values << n
@max = n if n > @max
end
def average(read_only=false)
return_value = if @values.length == 0
nil
else
@values.inject(0) {|sum,value| sum + value } / @values.length
end
@values = Array.new() unless read_only
return_value
end
def max(read_only=false)
return_value = @max
@max = 0 unless read_only
return_value
end
def count
@values.length
end
alias_method :length, :count
alias_method :size, :count
end
#STDOUT.puts "--loggile"
LOGFILE = File.join(File.dirname(__FILE__), '..', 'log', "#{RAILS_ENV}.log")
#STDOUT.puts LOGFILE
$response_data = { :total => Accumulator.new(),
# :rendering => Accumulator.new(),
:view => Accumulator.new(),
:db => Accumulator.new() }
#STDOUT.puts "--tailing"
Thread.abort_on_exception = true
logtail = Thread.new do
File::Tail::Logfile.tail(LOGFILE) do |line|
line = line.force_encoding('UTF-8')
if line =~ /^Completed/
# STDOUT.puts line
line.match(/^Completed .* in ([0-9.]+)ms \(Views: ([0-9.]+)ms \| ActiveRecord: ([0-9.]+)ms\)/)
total = $1
view = $2
db = $3
if total and view and db
$response_data[:total].add(total.to_f)
$response_data[:view].add(view.to_f)
$response_data[:db].add(db.to_f)
end
end
end
end
srv = WEBrick::HTTPServer.new({ #:DocumentRoot => '/dev/shm',
:BindAddress => '127.0.0.1',
:Port => PORT})
srv.mount_proc('/avg_response_time'){|req, res|
output = $response_data.map do |k,v|
if req.query["debug"]
debug = true
else
debug = false
end
value = v.send(:average, debug)
# value = v.average
formatted = value.nil? ? 'U' : sprintf('%.5f', value)
"#{k}.value #{formatted}"
end.join("\n")
output << "\n"
res.body = output
res.content_length = res.body.size
}
srv.mount_proc('/max_response_time'){|req, res|
output = $response_data.map do |k,v|
if req.query["debug"]
debug = true
else
debug = false
end
value = v.send(:max, debug)
formatted = value.nil? ? 'U' : sprintf('%.5f', value)
"#{k}.value #{formatted}"
end.join("\n")
output << "\n"
res.body = output
res.content_length = res.body.size
}
Signal.trap(:INT){ srv.shutdown }
srv.start
@susatadahiro
Copy link
Author

/home/ubuntu/apps/dtr/shared/script/rails_log_monitor.rb:61:in `block (2 levels) in

': invalid byte sequence in US-ASCII (ArgumentError)

@susatadahiro
Copy link
Author

rails_log_monitor.rb:61:in block (2 levels) in <main>': undefined methodtoutf8' for "\n":String (NoMethodError)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment