entombedvirus-munin_manager 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Manifest +16 -0
- data/README.markdown +0 -0
- data/Rakefile +22 -0
- data/bin/munin_manager +91 -0
- data/bin/runner +18 -0
- data/ext/string.rb +8 -0
- data/lib/munin_manager/acts_as_munin_plugin.rb +74 -0
- data/lib/munin_manager/log_reader.rb +85 -0
- data/lib/munin_manager/plugins/haproxy_response_time.rb +87 -0
- data/lib/munin_manager/plugins/rails_response_time.rb +91 -0
- data/lib/munin_manager/plugins.rb +24 -0
- data/lib/munin_manager.rb +9 -0
- data/munin_manager.gemspec +33 -0
- data/test/haproxy_response_time_test.rb +37 -0
- data/test/log_reader_test.rb +31 -0
- data/test/rails_response_time_test.rb +38 -0
- data/test/test_helper.rb +32 -0
- metadata +88 -0
data/Manifest
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
README.markdown
|
|
2
|
+
bin/munin_manager
|
|
3
|
+
bin/runner
|
|
4
|
+
lib/munin_manager.rb
|
|
5
|
+
lib/munin_manager/log_reader.rb
|
|
6
|
+
lib/munin_manager/plugins/haproxy_response_time.rb
|
|
7
|
+
lib/munin_manager/plugins/rails_response_time.rb
|
|
8
|
+
lib/munin_manager/plugins.rb
|
|
9
|
+
lib/munin_manager/acts_as_munin_plugin.rb
|
|
10
|
+
test/haproxy_response_time_test.rb
|
|
11
|
+
test/log_reader_test.rb
|
|
12
|
+
test/test_helper.rb
|
|
13
|
+
test/rails_response_time_test.rb
|
|
14
|
+
ext/string.rb
|
|
15
|
+
Rakefile
|
|
16
|
+
Manifest
|
data/README.markdown
ADDED
|
File without changes
|
data/Rakefile
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
require 'echoe'
|
|
2
|
+
Echoe.new('munin_manager', '0.0.1') do |s|
|
|
3
|
+
s.description = "Tool to maintain and install munin plugins written in Ruby"
|
|
4
|
+
s.author = "Rohith Ravi"
|
|
5
|
+
s.email = "entombedvirus@gmail.com"
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
#spec = Gem::Specification.new do |s|
|
|
9
|
+
# s.name = "MuninManager"
|
|
10
|
+
# s.version = "0.0.1"
|
|
11
|
+
# s.platform = Gem::Platform::RUBY
|
|
12
|
+
# s.files = FileList["{bin,lib,ext}/**/*"].to_a
|
|
13
|
+
# s.require_path = "lib"
|
|
14
|
+
# s.autorequire = "munin_manager"
|
|
15
|
+
# s.test_files = FileList["{test}/**/*test.rb"].to_a
|
|
16
|
+
# s.has_rdoc = false
|
|
17
|
+
# s.extra_rdoc_files = ["README.markdown"]
|
|
18
|
+
#end
|
|
19
|
+
#
|
|
20
|
+
#Rake::GemPackageTask.new(spec) do |pkg|
|
|
21
|
+
# pkg.need_tar = true
|
|
22
|
+
#end
|
data/bin/munin_manager
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require 'rubygems'
|
|
4
|
+
require 'ruby-debug'
|
|
5
|
+
|
|
6
|
+
require 'optparse'
|
|
7
|
+
require 'ostruct'
|
|
8
|
+
require "#{File.dirname(__FILE__)}/../lib/munin_manager"
|
|
9
|
+
|
|
10
|
+
options = OpenStruct.new
|
|
11
|
+
options.plugin_dir = "/etc/munin/plugins"
|
|
12
|
+
|
|
13
|
+
parser = OptionParser.new do |opts|
|
|
14
|
+
opts.banner = "Usage: munin_manager <command> [<plugin_name>[:<symlink_name>]] [options]"
|
|
15
|
+
opts.separator ""
|
|
16
|
+
|
|
17
|
+
opts.separator "Commands:"
|
|
18
|
+
|
|
19
|
+
opts.on("-l", "--list", "List available plugins to install") do
|
|
20
|
+
buffer = []
|
|
21
|
+
buffer << "Available Plugins"
|
|
22
|
+
buffer << "================="
|
|
23
|
+
buffer << ""
|
|
24
|
+
|
|
25
|
+
MuninManager::Plugins.each do |plugin|
|
|
26
|
+
buffer << plugin.plugin_name
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
buffer << ""
|
|
30
|
+
puts buffer.join("\n")
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
opts.on("-s", "--show PLUGIN_NAME", "Shows details about a plugin") do |name|
|
|
34
|
+
begin
|
|
35
|
+
puts MuninManager::Plugins[name].help_text
|
|
36
|
+
rescue NoMethodError
|
|
37
|
+
STDERR.puts "'%s' plugin was not found" % name
|
|
38
|
+
rescue
|
|
39
|
+
STDERR.puts "No additional information is available about this plugin."
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
opts.on("-i", "--install PLUGIN_NAME [PLUGIN_NAME]", Array,
|
|
44
|
+
"Installs a plugin by creating a symlink in '%s'" % options.plugin_dir) do |names|
|
|
45
|
+
if options.plugins = MuninManager::Plugins[*names]
|
|
46
|
+
options.action = :install
|
|
47
|
+
options.plugin_names = names
|
|
48
|
+
else
|
|
49
|
+
puts "'%s' plugin not found" % names.join
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
opts.on("-u", "--uninstall PLUGIN_NAME [PLUGIN_NAME]", Array,
|
|
54
|
+
"Removes plugins from '%s'" % options.plugin_dir,
|
|
55
|
+
"if it is a symlink") do |names|
|
|
56
|
+
|
|
57
|
+
if options.plugins = MuninManager::Plugins[*names]
|
|
58
|
+
options.action = :uninstall
|
|
59
|
+
options.plugin_names = names
|
|
60
|
+
else
|
|
61
|
+
puts "'#{names}' plugin not found"
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
opts.separator ""
|
|
66
|
+
opts.separator "Options:"
|
|
67
|
+
|
|
68
|
+
opts.on("--plugin-dir DIR", "Directory where symlinks will be created when a plugin is installed",
|
|
69
|
+
"(default is '%s'" % options.plugin_dir) do |dir|
|
|
70
|
+
if File.directory?(dir) && File.writable?(dir)
|
|
71
|
+
options.plugin_dir = dir
|
|
72
|
+
else
|
|
73
|
+
STDERR.puts "'%s' does not exist or is not writable" % options.plugin_dir
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
opts.on("--force", "Forces the installation or uninstallation of plugins") do
|
|
78
|
+
options.force = true
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
parser.parse!(ARGV)
|
|
85
|
+
options.freeze!
|
|
86
|
+
|
|
87
|
+
case options.action
|
|
88
|
+
when :uninstall, :install
|
|
89
|
+
plugins = Array(MuninManager::Plugins[*options.plugin_names])
|
|
90
|
+
plugins.each {|p| p.send(options.action, options)}
|
|
91
|
+
end
|
data/bin/runner
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
# This file will serve as the target for the symlinks in /etc/munin/plugins.
|
|
4
|
+
# It's the responsibility of this script to run the plugin indicated by the
|
|
5
|
+
# symlink name.
|
|
6
|
+
|
|
7
|
+
this_file = File.symlink?(__FILE__) ? File.readlink(__FILE__) : __FILE__
|
|
8
|
+
require "#{File.dirname(this_file)}/../lib/munin_manager"
|
|
9
|
+
|
|
10
|
+
plugin_name = File.basename($0)
|
|
11
|
+
|
|
12
|
+
if plugin = MuninManager::Plugins[plugin_name]
|
|
13
|
+
plugin.run
|
|
14
|
+
exit(0)
|
|
15
|
+
else
|
|
16
|
+
STDERR.puts "'%s' plugin was not found" % plugin_name
|
|
17
|
+
exit(1)
|
|
18
|
+
end
|
data/ext/string.rb
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
require 'fileutils'
|
|
2
|
+
|
|
3
|
+
module MuninManager
|
|
4
|
+
module ActsAsMuninPlugin
|
|
5
|
+
def self.included(klass)
|
|
6
|
+
klass.send(:include, InstanceMethods)
|
|
7
|
+
klass.extend(ClassMethods)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
module ClassMethods
|
|
11
|
+
def run
|
|
12
|
+
raise "This is the entry point of the plugin when invoked by munin. Needs implementation by included class"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def plugin_name
|
|
16
|
+
# Name of the plugin. Must not contain spaces or special chars
|
|
17
|
+
|
|
18
|
+
# Default is underscorized version of the class name
|
|
19
|
+
self.name.split('::').last.
|
|
20
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
|
21
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
|
22
|
+
tr("-", "_").
|
|
23
|
+
downcase
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def help_text
|
|
27
|
+
# Any general info concerning the plugin. Should be overriden by included class
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def install(options)
|
|
31
|
+
symlink = File.join(options.plugin_dir, plugin_name)
|
|
32
|
+
runner = File.join(File.dirname(__FILE__), "..", "..", "bin", "runner")
|
|
33
|
+
runner = File.expand_path(runner)
|
|
34
|
+
|
|
35
|
+
if File.exists?(symlink) && !options.force
|
|
36
|
+
STDERR.puts "'%s' already exists. Please specify --force option to overwrite" % symlink
|
|
37
|
+
return
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
STDOUT.puts "Installing '%s' at '%s'" % [plugin_name, symlink]
|
|
41
|
+
FileUtils.ln_sf(runner, symlink)
|
|
42
|
+
|
|
43
|
+
STDOUT.puts help_text
|
|
44
|
+
|
|
45
|
+
rescue Errno::EACCES
|
|
46
|
+
STDERR.puts "ERROR: Permission denied while attempting to install to '%s'" % symlink
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Default uninstaller. Override in included classes if the default is not sufficient
|
|
50
|
+
def uninstall(options)
|
|
51
|
+
symlink = File.join(options.plugin_dir, plugin_name)
|
|
52
|
+
|
|
53
|
+
unless File.exists?(symlink)
|
|
54
|
+
STDERR.puts "'%s' does not seem to exist. Aborting..." % symlink
|
|
55
|
+
return
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
unless File.symlink?(symlink) || options.force
|
|
59
|
+
STDERR.puts "'%s' does not appear to be a symlink. Please specify --force option to remove" % symlink
|
|
60
|
+
return
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
STDOUT.puts "Removing '%s'..." % symlink
|
|
64
|
+
File.unlink(symlink)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
module InstanceMethods
|
|
69
|
+
def config
|
|
70
|
+
raise "This is invoked by munin to get graph details. Needs implementation by included class"
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
module MuninManager
|
|
2
|
+
# A LogReader that continues from where it left off.
|
|
3
|
+
# Ex:
|
|
4
|
+
# class RailsLogReader < LogReader
|
|
5
|
+
# def scan(log_file)
|
|
6
|
+
# @req_counter = 0
|
|
7
|
+
# loop do
|
|
8
|
+
# line = log_file.readline
|
|
9
|
+
# @req_counter += 1 if line =~ /Completed in/
|
|
10
|
+
# end
|
|
11
|
+
# end
|
|
12
|
+
#
|
|
13
|
+
# def process!
|
|
14
|
+
# # Do nothing
|
|
15
|
+
# end
|
|
16
|
+
#
|
|
17
|
+
# def print_data
|
|
18
|
+
# "num_requests.value #{@req_counter}"
|
|
19
|
+
# end
|
|
20
|
+
# end
|
|
21
|
+
#
|
|
22
|
+
# Usage:
|
|
23
|
+
#
|
|
24
|
+
# reader = RailsLogReader.new("log/development.log")
|
|
25
|
+
# reader.collect!
|
|
26
|
+
# reader.print_data
|
|
27
|
+
#
|
|
28
|
+
class LogReader
|
|
29
|
+
attr_accessor :file_name, :me, :state_dir, :state_file
|
|
30
|
+
|
|
31
|
+
def initialize(file_name)
|
|
32
|
+
@file_name = file_name
|
|
33
|
+
@me = File.basename($0)
|
|
34
|
+
@state_dir = ENV['MUNIN_PLUGSTATE'] || '/var/lib/munin/plugin-state'
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def state_file
|
|
38
|
+
@state_file ||= File.join(@state_dir, @me)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def collect!(options = {})
|
|
42
|
+
options = {
|
|
43
|
+
:save_state => true
|
|
44
|
+
}.merge(options)
|
|
45
|
+
|
|
46
|
+
File.open(@file_name, "r") do |f|
|
|
47
|
+
load_saved_state(f)
|
|
48
|
+
|
|
49
|
+
begin
|
|
50
|
+
scan(f)
|
|
51
|
+
rescue EOFError
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
process!
|
|
55
|
+
save_state(f) if options[:save_state]
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def load_saved_state(log_file)
|
|
60
|
+
return unless File.exists?(state_file) && !(state = File.read(state_file)).nil?
|
|
61
|
+
pos, last_file_size = Marshal.load(state)
|
|
62
|
+
|
|
63
|
+
# Check for log rotation
|
|
64
|
+
return if File.size(@file_name) < last_file_size
|
|
65
|
+
|
|
66
|
+
log_file.pos = pos
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def scan(log_file)
|
|
70
|
+
# Only subclasses know how to process each type of logfile
|
|
71
|
+
raise "Needs to be implemented by subclasses"
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def process!
|
|
75
|
+
raise "Needs to be implemented by subclasses"
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def save_state(log_file)
|
|
79
|
+
File.open(state_file, "w") do |f|
|
|
80
|
+
f.write(Marshal.dump([log_file.pos, File.size(log_file)]))
|
|
81
|
+
f.flush
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
module MuninManager
|
|
2
|
+
class Plugins::HaproxyResponseTime < LogReader
|
|
3
|
+
include ActsAsMuninPlugin
|
|
4
|
+
|
|
5
|
+
def data
|
|
6
|
+
@data ||= Hash.new {|h, k| h[k] = Array.new}
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def scan(log_file)
|
|
10
|
+
loop do
|
|
11
|
+
line = log_file.readline
|
|
12
|
+
chunks = line.split(/\s+/)
|
|
13
|
+
|
|
14
|
+
timers = chunks[9].split("/") rescue []
|
|
15
|
+
data[:client_connect] << timers[0].to_f
|
|
16
|
+
data[:waiting_in_queue] << timers[1].to_f
|
|
17
|
+
data[:server_connect] << timers[2].to_f
|
|
18
|
+
data[:server_response] << timers[3].to_f
|
|
19
|
+
data[:rails_action] << line.match(/\{([0-9.]+)\}/).captures[0].to_f rescue 0
|
|
20
|
+
data[:total] << timers[4].to_f
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def process!
|
|
25
|
+
data.each do |k, v|
|
|
26
|
+
data[k] = data[k].inject(0) {|sum, i| sum + i} / data[k].length rescue 0
|
|
27
|
+
data[k] = formatted(data[k] / 1000)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def config
|
|
32
|
+
<<-LABEL
|
|
33
|
+
graph_title HAProxy Response Breakdown
|
|
34
|
+
graph_vlabel time (secs)
|
|
35
|
+
graph_category Haproxy
|
|
36
|
+
client_connect.label Client Connect
|
|
37
|
+
waiting_in_queue.label Waiting In Queue
|
|
38
|
+
server_connect.label Server Connect
|
|
39
|
+
server_response.label Server Response
|
|
40
|
+
rails_action.label Rails Controller Action
|
|
41
|
+
total.label Total Response Time
|
|
42
|
+
LABEL
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def values
|
|
46
|
+
data.map {|k, v| "#{k}.value #{v}"}.join("\n")
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def self.run
|
|
50
|
+
log_file = ENV['log_file'] || "/var/log/haproxy.log"
|
|
51
|
+
allowed_commands = ['config']
|
|
52
|
+
|
|
53
|
+
haproxy = new(log_file)
|
|
54
|
+
|
|
55
|
+
if cmd = ARGV[0] and allowed_commands.include? cmd then
|
|
56
|
+
puts haproxy.send(cmd.to_sym)
|
|
57
|
+
else
|
|
58
|
+
haproxy.collect!
|
|
59
|
+
puts haproxy.values
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def self.help_text
|
|
64
|
+
%Q{
|
|
65
|
+
#{plugin_name.capitalize} Munin Plugin
|
|
66
|
+
===========================
|
|
67
|
+
|
|
68
|
+
Please remember to add something like the lines below to /etc/munin/plugin-conf.d/munin-node
|
|
69
|
+
if the haproxy log file is not at /var/log/haproxy.log
|
|
70
|
+
|
|
71
|
+
[#{plugin_name}]
|
|
72
|
+
env.log_file /var/log/custom/haproxy.log
|
|
73
|
+
|
|
74
|
+
Also, make sure that the '/var/lib/munin/plugin-state' is writable by munin.
|
|
75
|
+
|
|
76
|
+
$ sudo chmod 777 /var/lib/munin/plugin-state
|
|
77
|
+
|
|
78
|
+
}
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
private
|
|
82
|
+
|
|
83
|
+
def formatted(num)
|
|
84
|
+
"%.10f" % num
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
module MuninManager
|
|
2
|
+
class Plugins::RailsResponseTime < LogReader
|
|
3
|
+
include ActsAsMuninPlugin
|
|
4
|
+
|
|
5
|
+
def data
|
|
6
|
+
@data ||= Hash.new {|h, k| h[k] = Array.new}
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def scan(log_file)
|
|
10
|
+
current_action = nil
|
|
11
|
+
loop do
|
|
12
|
+
line = log_file.readline
|
|
13
|
+
|
|
14
|
+
if line.starts_with?("Processing ")
|
|
15
|
+
cols = line.split(/\s+/)
|
|
16
|
+
current_action = cols[1]
|
|
17
|
+
elsif line.starts_with?("Completed in ") && !current_action.nil?
|
|
18
|
+
cols = line.split(/\s+/)
|
|
19
|
+
data[current_action] << cols[2].to_f
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def process!
|
|
26
|
+
data.each_pair do |action_name, response_times|
|
|
27
|
+
data[action_name] = response_times.inject(0) {|sum, i| sum + i} / data[action_name].length rescue 0
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def config
|
|
32
|
+
configs = {
|
|
33
|
+
"graph_title" => "Rails Response Times (by Controller->Action)",
|
|
34
|
+
"graph_vlabel" => "time (secs)",
|
|
35
|
+
"graph_category" => "Performance"
|
|
36
|
+
}
|
|
37
|
+
self.collect!(:save_state => false)
|
|
38
|
+
self.data.each do |action_name, respose_time|
|
|
39
|
+
configs["#{format_for_munin(action_name)}.label"] = action_name.sub("#", "->")
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
configs["graph_order"] = self.data.to_a.
|
|
43
|
+
sort {|lhs, rhs| rhs[1] <=> lhs[1]}.
|
|
44
|
+
collect {|tuple| format_for_munin(tuple[0])}
|
|
45
|
+
|
|
46
|
+
configs.map {|key_value_pair| key_value_pair.join(" ")}.join("\n")
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def values
|
|
50
|
+
data.map {|k, v| "#{format_for_munin(k)}.value #{"%.10f" % v}"}.join("\n")
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def self.run
|
|
54
|
+
log_file = ENV['log_file'] || "/var/log/rails.log"
|
|
55
|
+
allowed_commands = ['config']
|
|
56
|
+
|
|
57
|
+
rails = new(log_file)
|
|
58
|
+
|
|
59
|
+
if cmd = ARGV[0] and allowed_commands.include? cmd then
|
|
60
|
+
puts rails.send(cmd.to_sym)
|
|
61
|
+
else
|
|
62
|
+
rails.collect!
|
|
63
|
+
puts rails.values
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def self.help_text
|
|
68
|
+
%Q{
|
|
69
|
+
#{plugin_name.capitalize} Munin Plugin
|
|
70
|
+
===========================
|
|
71
|
+
|
|
72
|
+
Please remember to add something like the lines below to /etc/munin/plugin-conf.d/munin-node
|
|
73
|
+
if the rails log file is not at /var/log/rails.log
|
|
74
|
+
|
|
75
|
+
[#{plugin_name}]
|
|
76
|
+
env.log_file /var/log/custom/rails.log
|
|
77
|
+
|
|
78
|
+
Also, make sure that the '/var/lib/munin/plugin-state' is writable by munin.
|
|
79
|
+
|
|
80
|
+
$ sudo chmod 777 /var/lib/munin/plugin-state
|
|
81
|
+
|
|
82
|
+
}
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
private
|
|
86
|
+
|
|
87
|
+
def format_for_munin(str)
|
|
88
|
+
str.to_s.gsub(/[^A-Za-z0-9_]/, "_")
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module MuninManager
|
|
2
|
+
module Plugins
|
|
3
|
+
extend self
|
|
4
|
+
extend Enumerable
|
|
5
|
+
|
|
6
|
+
def each(&block)
|
|
7
|
+
registered_plugins.each(&block)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def registered_plugins
|
|
11
|
+
@registered_plugins ||= constants.
|
|
12
|
+
map {|c| const_get(c) }.
|
|
13
|
+
select {|const| const.is_a?(Class) && const < MuninManager::ActsAsMuninPlugin}
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def [](*names)
|
|
17
|
+
if names.length == 1
|
|
18
|
+
return detect {|plugin| plugin.plugin_name == names.first}
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
names.map {|name| self[name]}
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
munin_manager = File.join(File.dirname(__FILE__), "munin_manager")
|
|
2
|
+
|
|
3
|
+
%w(../../ext/string log_reader plugins acts_as_munin_plugin).each do |f|
|
|
4
|
+
require File.join(munin_manager, f)
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
Dir[File.join(munin_manager, "plugins", "*")].each do |file|
|
|
8
|
+
require file
|
|
9
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
Gem::Specification.new do |s|
|
|
4
|
+
s.name = %q{munin_manager}
|
|
5
|
+
s.version = "0.0.1"
|
|
6
|
+
|
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
|
8
|
+
s.authors = ["Rohith Ravi"]
|
|
9
|
+
s.date = %q{2009-03-09}
|
|
10
|
+
s.description = %q{Tool to maintain and install munin plugins written in Ruby}
|
|
11
|
+
s.email = %q{entombedvirus@gmail.com}
|
|
12
|
+
s.executables = ["munin_manager", "runner"]
|
|
13
|
+
s.extra_rdoc_files = ["README.markdown", "bin/munin_manager", "bin/runner", "lib/munin_manager.rb", "lib/munin_manager/log_reader.rb", "lib/munin_manager/plugins/haproxy_response_time.rb", "lib/munin_manager/plugins/rails_response_time.rb", "lib/munin_manager/plugins.rb", "lib/munin_manager/acts_as_munin_plugin.rb", "ext/string.rb"]
|
|
14
|
+
s.files = ["README.markdown", "bin/munin_manager", "bin/runner", "lib/munin_manager.rb", "lib/munin_manager/log_reader.rb", "lib/munin_manager/plugins/haproxy_response_time.rb", "lib/munin_manager/plugins/rails_response_time.rb", "lib/munin_manager/plugins.rb", "lib/munin_manager/acts_as_munin_plugin.rb", "test/haproxy_response_time_test.rb", "test/log_reader_test.rb", "test/test_helper.rb", "test/rails_response_time_test.rb", "ext/string.rb", "Rakefile", "Manifest", "munin_manager.gemspec"]
|
|
15
|
+
s.has_rdoc = true
|
|
16
|
+
s.homepage = %q{}
|
|
17
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Munin_manager", "--main", "README.markdown"]
|
|
18
|
+
s.require_paths = ["lib", "ext"]
|
|
19
|
+
s.rubyforge_project = %q{munin_manager}
|
|
20
|
+
s.rubygems_version = %q{1.3.1}
|
|
21
|
+
s.summary = %q{Tool to maintain and install munin plugins written in Ruby}
|
|
22
|
+
s.test_files = ["test/haproxy_response_time_test.rb", "test/log_reader_test.rb", "test/test_helper.rb", "test/rails_response_time_test.rb"]
|
|
23
|
+
|
|
24
|
+
if s.respond_to? :specification_version then
|
|
25
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
|
26
|
+
s.specification_version = 2
|
|
27
|
+
|
|
28
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
|
29
|
+
else
|
|
30
|
+
end
|
|
31
|
+
else
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + "/test_helper"
|
|
2
|
+
|
|
3
|
+
class HaproxyResponseTimeTest < Test::Unit::TestCase
|
|
4
|
+
include MuninManager::Plugins
|
|
5
|
+
|
|
6
|
+
def setup
|
|
7
|
+
@reader = HaproxyResponseTime.new("#{TEST_DIR}/logs/haproxy.log")
|
|
8
|
+
@reader.state_dir = "#{TEST_DIR}/tmp"
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def teardown
|
|
12
|
+
File.unlink(@reader.state_file)
|
|
13
|
+
rescue
|
|
14
|
+
# Do nothing
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def test_parsing
|
|
18
|
+
@reader.collect!
|
|
19
|
+
|
|
20
|
+
expected_values = {
|
|
21
|
+
:rails_action => "0.0005463880",
|
|
22
|
+
:total => "0.5552000000",
|
|
23
|
+
:client_connect => "0.0000000000",
|
|
24
|
+
:waiting_in_queue => "0.0000000000",
|
|
25
|
+
:server_connect => "0.0000000000",
|
|
26
|
+
:server_response => "0.5547000000"
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
expected_values.each do |field, val|
|
|
30
|
+
assert_equal(val, @reader.data[field], "Value mismatch in #{field}")
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def test_has_help_text
|
|
35
|
+
assert !HaproxyResponseTime.help_text.empty?
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + "/test_helper"
|
|
2
|
+
|
|
3
|
+
class LogReaderTest < Test::Unit::TestCase
|
|
4
|
+
def setup
|
|
5
|
+
@reader = RailsLogReader.new("#{HERE}/logs/rails.log")
|
|
6
|
+
|
|
7
|
+
File.open(@reader.file_name, 'w') do |f|
|
|
8
|
+
10.times {f.puts("Completed in")}
|
|
9
|
+
20.times {f.puts("Some other stuff")}
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def teardown
|
|
14
|
+
File.unlink(@reader.state_file)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def test_remembers_last_position_in_log
|
|
18
|
+
@reader.collect!
|
|
19
|
+
assert_equal 10, @reader.num_requests
|
|
20
|
+
|
|
21
|
+
File.open(@reader.file_name, 'a+') do |f|
|
|
22
|
+
10.times {f.puts("Completed in")}
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
@new_reader = RailsLogReader.new(@reader.file_name)
|
|
26
|
+
@new_reader.collect!
|
|
27
|
+
|
|
28
|
+
assert_equal 10, @new_reader.num_requests
|
|
29
|
+
assert_equal 40, File.new(@new_reader.file_name).readlines.length
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + "/test_helper"
|
|
2
|
+
|
|
3
|
+
class RailsResponseTimeTest < Test::Unit::TestCase
|
|
4
|
+
include MuninManager::Plugins
|
|
5
|
+
|
|
6
|
+
def setup
|
|
7
|
+
@reader = RailsResponseTime.new("#{TEST_DIR}/logs/rails.log")
|
|
8
|
+
@reader.state_dir = "#{TEST_DIR}/tmp"
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def teardown
|
|
12
|
+
File.unlink(@reader.state_file)
|
|
13
|
+
rescue
|
|
14
|
+
# Do nothing
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def test_parsing
|
|
18
|
+
@reader.collect!
|
|
19
|
+
assert @reader.data.keys.length > 0
|
|
20
|
+
|
|
21
|
+
@reader.data.each_pair do |k, v|
|
|
22
|
+
assert v > 0, "Value mismatch for #{k}"
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def test_config_parses_log
|
|
27
|
+
assert @reader.config.split("\n").select {|line| not line.starts_with?("graph_")}.length > 0
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def test_config_does_not_save_state
|
|
31
|
+
@reader.config
|
|
32
|
+
assert !File.exists?(@reader.state_file)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def test_has_help_text
|
|
36
|
+
assert !RailsResponseTime.help_text.empty?
|
|
37
|
+
end
|
|
38
|
+
end
|
data/test/test_helper.rb
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
TEST_DIR = File.dirname(__FILE__)
|
|
2
|
+
%w(lib ext bin test).each do |dir|
|
|
3
|
+
$LOAD_PATH.unshift "#{TEST_DIR}/../#{dir}"
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
%w(rubygems test/unit ruby-debug munin_manager).each do |f|
|
|
7
|
+
require f
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
class RailsLogReader < MuninManager::LogReader
|
|
11
|
+
def initialize(*params)
|
|
12
|
+
super
|
|
13
|
+
@state_dir = "#{TEST_DIR}/tmp"
|
|
14
|
+
@state_file = File.join(@state_dir, @me)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def scan(log_file)
|
|
18
|
+
@req_counter = 0
|
|
19
|
+
loop do
|
|
20
|
+
line = log_file.readline
|
|
21
|
+
@req_counter += 1 if line =~ /Completed in/
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def process!
|
|
26
|
+
# Do nothing
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def num_requests
|
|
30
|
+
@req_counter
|
|
31
|
+
end
|
|
32
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: entombedvirus-munin_manager
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Rohith Ravi
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
|
|
12
|
+
date: 2009-03-09 00:00:00 -07:00
|
|
13
|
+
default_executable:
|
|
14
|
+
dependencies: []
|
|
15
|
+
|
|
16
|
+
description: Tool to maintain and install munin plugins written in Ruby
|
|
17
|
+
email: entombedvirus@gmail.com
|
|
18
|
+
executables:
|
|
19
|
+
- munin_manager
|
|
20
|
+
- runner
|
|
21
|
+
extensions: []
|
|
22
|
+
|
|
23
|
+
extra_rdoc_files:
|
|
24
|
+
- README.markdown
|
|
25
|
+
- bin/munin_manager
|
|
26
|
+
- bin/runner
|
|
27
|
+
- lib/munin_manager.rb
|
|
28
|
+
- lib/munin_manager/log_reader.rb
|
|
29
|
+
- lib/munin_manager/plugins/haproxy_response_time.rb
|
|
30
|
+
- lib/munin_manager/plugins/rails_response_time.rb
|
|
31
|
+
- lib/munin_manager/plugins.rb
|
|
32
|
+
- lib/munin_manager/acts_as_munin_plugin.rb
|
|
33
|
+
- ext/string.rb
|
|
34
|
+
files:
|
|
35
|
+
- README.markdown
|
|
36
|
+
- bin/munin_manager
|
|
37
|
+
- bin/runner
|
|
38
|
+
- lib/munin_manager.rb
|
|
39
|
+
- lib/munin_manager/log_reader.rb
|
|
40
|
+
- lib/munin_manager/plugins/haproxy_response_time.rb
|
|
41
|
+
- lib/munin_manager/plugins/rails_response_time.rb
|
|
42
|
+
- lib/munin_manager/plugins.rb
|
|
43
|
+
- lib/munin_manager/acts_as_munin_plugin.rb
|
|
44
|
+
- test/haproxy_response_time_test.rb
|
|
45
|
+
- test/log_reader_test.rb
|
|
46
|
+
- test/test_helper.rb
|
|
47
|
+
- test/rails_response_time_test.rb
|
|
48
|
+
- ext/string.rb
|
|
49
|
+
- Rakefile
|
|
50
|
+
- Manifest
|
|
51
|
+
- munin_manager.gemspec
|
|
52
|
+
has_rdoc: true
|
|
53
|
+
homepage: ""
|
|
54
|
+
post_install_message:
|
|
55
|
+
rdoc_options:
|
|
56
|
+
- --line-numbers
|
|
57
|
+
- --inline-source
|
|
58
|
+
- --title
|
|
59
|
+
- Munin_manager
|
|
60
|
+
- --main
|
|
61
|
+
- README.markdown
|
|
62
|
+
require_paths:
|
|
63
|
+
- lib
|
|
64
|
+
- ext
|
|
65
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
66
|
+
requirements:
|
|
67
|
+
- - ">="
|
|
68
|
+
- !ruby/object:Gem::Version
|
|
69
|
+
version: "0"
|
|
70
|
+
version:
|
|
71
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - ">="
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: "1.2"
|
|
76
|
+
version:
|
|
77
|
+
requirements: []
|
|
78
|
+
|
|
79
|
+
rubyforge_project: munin_manager
|
|
80
|
+
rubygems_version: 1.2.0
|
|
81
|
+
signing_key:
|
|
82
|
+
specification_version: 2
|
|
83
|
+
summary: Tool to maintain and install munin plugins written in Ruby
|
|
84
|
+
test_files:
|
|
85
|
+
- test/haproxy_response_time_test.rb
|
|
86
|
+
- test/log_reader_test.rb
|
|
87
|
+
- test/test_helper.rb
|
|
88
|
+
- test/rails_response_time_test.rb
|