health_monitor 0.0.1 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +57 -6
- data/Rakefile +3 -1
- data/VERSION +1 -1
- data/generators/health_monitor/templates/controller.rb +10 -7
- data/health_monitor.gemspec +4 -4
- data/lib/health_monitor.rb +5 -2
- data/lib/health_monitor/built_in_checks.rb +77 -0
- data/lib/{health_monitoring.rb → health_monitor/health_monitoring.rb} +35 -12
- metadata +4 -4
- data/templates/_health_monitor.html.erb +0 -29
data/README.rdoc
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|
= Health Monitor
|
2
2
|
Monitor individual aspects of your rails application's health
|
3
3
|
|
4
|
+
|
5
|
+
|
6
|
+
|
7
|
+
|
4
8
|
Most rails applications have many additional moving parts of which the health cannot be assessed with
|
5
9
|
simply pinging the (hopefully page cached) homepage
|
6
10
|
|
@@ -20,22 +24,44 @@ to nginx to mongrels must be happily shoving rubies around to get that 200 oh bo
|
|
20
24
|
So ping away, grab a beer and know that hey, you might be too drunk but at least you will know your application
|
21
25
|
is sick before your clients do.
|
22
26
|
|
23
|
-
|
27
|
+
== Installation
|
24
28
|
Install the plugin or gem
|
25
29
|
script/plugin install git://github.com/blythedunham/health_monitor
|
26
30
|
|
27
|
-
sudo gem install
|
31
|
+
sudo gem install health_monitor --source=http://gemcutter.org
|
28
32
|
|
29
33
|
Run the generator to create HealthMonitorController
|
30
34
|
script/generate health_monitor
|
31
35
|
|
36
|
+
|
37
|
+
== Quick Reference
|
38
|
+
# Built in checks to monitor db and schema
|
39
|
+
monitor_health :schema_check, :database_check
|
40
|
+
|
41
|
+
# Monitor a process
|
42
|
+
monitor_process :monit
|
43
|
+
|
44
|
+
montior_health :mycheck, :description => 'Check blahs' do |controller|
|
45
|
+
... do something ....
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
|
32
50
|
=== Examples
|
33
51
|
|
34
52
|
class HealthMonitorController < ApplicationController
|
35
53
|
acts_as_health_monitor
|
36
54
|
|
37
|
-
#
|
38
|
-
|
55
|
+
# Built in checks query the database for the schema and make sure
|
56
|
+
# that the database connection
|
57
|
+
monitor_health :schema_check, :database_check, :ey_agent_check
|
58
|
+
|
59
|
+
#monitor specified process
|
60
|
+
monitor_process :monit
|
61
|
+
|
62
|
+
# check database connection by ensuring there is at least one user
|
63
|
+
# calls protected method +user_check+
|
64
|
+
monitor_health :user_check, :description => 'Check database connection'
|
39
65
|
|
40
66
|
# Monitor email sending. Fail if it exceeds 4 minutes
|
41
67
|
monitor_health :email,
|
@@ -50,11 +76,26 @@ Run the generator to create HealthMonitorController
|
|
50
76
|
end
|
51
77
|
|
52
78
|
protected
|
53
|
-
def
|
79
|
+
def user_check; User.first; end
|
54
80
|
|
55
81
|
end
|
56
82
|
|
57
|
-
=== +
|
83
|
+
=== +monitor_process+ Monitor a server process
|
84
|
+
+process_name+ - name of the process to monitor
|
85
|
+
|
86
|
+
==== Options
|
87
|
+
+sudo+ - (default false) set to true to run pgrep as sudo
|
88
|
+
+pattern+ - specify a pattern to match (pgrep -f) instead of the process_name
|
89
|
+
+arguments+ - additional arguments to pgrep "-o root,blah -a"
|
90
|
+
|
91
|
+
monitor_process :monit, :someproc, :sudo => true
|
92
|
+
monitor_process :mongod, :pattern => 'mongodb.*slave'
|
93
|
+
|
94
|
+
|
95
|
+
=== +monitor_health+
|
96
|
+
Monitor the health of the application
|
97
|
+
|
98
|
+
==== Options
|
58
99
|
<tt>:description</tt> - description of the task
|
59
100
|
<tt>:message</tt> - Defaults to SUCCESS or FAILED!
|
60
101
|
Additional information that is either a string or a hash with keys <tt>:success</tt> and <tt>:failure</tt>
|
@@ -65,6 +106,16 @@ Run the generator to create HealthMonitorController
|
|
65
106
|
<tt>:method</tt> - The name of the method or a Proc to invoke. A block can be given as well.
|
66
107
|
Defaults to the method with the feature name
|
67
108
|
|
109
|
+
|
110
|
+
=== Built in checks
|
111
|
+
+schema_check+ and +database_check+ are protected methods.
|
112
|
+
|
113
|
+
Check the database connection and report the schema number
|
114
|
+
monitor_health :schema_check
|
115
|
+
|
116
|
+
Check for an active database connection
|
117
|
+
monitor_health :database_check
|
118
|
+
|
68
119
|
=== Monitored Methods
|
69
120
|
The proc or method defined should return its status as one of the following:
|
70
121
|
* true or false indicating success or failure
|
data/Rakefile
CHANGED
@@ -16,7 +16,9 @@ begin
|
|
16
16
|
#gem.add_development_dependency('timecop', '0.3.1')
|
17
17
|
gem.add_development_dependency('mocha', '>=0.9.8')
|
18
18
|
gem.add_development_dependency "thoughtbot-shoulda", ">= 0"
|
19
|
-
|
19
|
+
|
20
|
+
gem.extra_rdoc_files = ["README.rdoc", "LICENSE"]
|
21
|
+
|
20
22
|
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
21
23
|
end
|
22
24
|
Jeweler::GemcutterTasks.new
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.1.1
|
@@ -1,16 +1,20 @@
|
|
1
1
|
class <%= class_name %>Controller < ApplicationController
|
2
2
|
acts_as_health_monitor
|
3
3
|
|
4
|
+
# Built in checks
|
5
|
+
monitor_health :schema_check, :database_check, #:ey_agent_check
|
6
|
+
|
4
7
|
# Refer to the README at http://github.com/blythedunham/health_monitor
|
5
8
|
# for more examples
|
6
|
-
|
7
|
-
#
|
8
|
-
# monitor_health :database, :description => 'Check database connection'
|
9
|
+
# #Make sure monit and 'myspecial cron task' is running
|
10
|
+
# monitor_process :monit, :myspecialcrontask
|
9
11
|
#
|
12
|
+
# monitor_health :user_check
|
10
13
|
# # Monitor email sending. Fail if it exceeds 4 minutes
|
14
|
+
|
11
15
|
# monitor_health :email,
|
12
|
-
#
|
13
|
-
#
|
16
|
+
# :timeout => 240000 # Fail this test if it exceeds 4 minutes
|
17
|
+
# :method => lambda{ |controller| ActionMailer::Base.deliver_my_mail('blah') }
|
14
18
|
#
|
15
19
|
# # Display the results of system df call with the results
|
16
20
|
# monitor_health :check_disk, :description => 'Check Disk status' do |controller|
|
@@ -20,8 +24,7 @@ class <%= class_name %>Controller < ApplicationController
|
|
20
24
|
# end
|
21
25
|
#
|
22
26
|
# protected
|
23
|
-
# def
|
24
|
-
#
|
27
|
+
# def user_check; User.first; end
|
25
28
|
|
26
29
|
<% for action in actions -%>
|
27
30
|
def <%= action %>
|
data/health_monitor.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{health_monitor}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.1.1"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Blythe Dunham"]
|
12
|
-
s.date = %q{2009-12-
|
12
|
+
s.date = %q{2009-12-13}
|
13
13
|
s.email = %q{blythe@snowgiraffe.com}
|
14
14
|
s.extra_rdoc_files = [
|
15
15
|
"LICENSE",
|
@@ -26,8 +26,8 @@ Gem::Specification.new do |s|
|
|
26
26
|
"generators/health_monitor/templates/controller.rb",
|
27
27
|
"health_monitor.gemspec",
|
28
28
|
"lib/health_monitor.rb",
|
29
|
-
"lib/
|
30
|
-
"
|
29
|
+
"lib/health_monitor/built_in_checks.rb",
|
30
|
+
"lib/health_monitor/health_monitoring.rb",
|
31
31
|
"test/helper.rb",
|
32
32
|
"test/test_health_monitor.rb"
|
33
33
|
]
|
data/lib/health_monitor.rb
CHANGED
@@ -1,2 +1,5 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
|
2
|
+
require File.dirname(__FILE__) + "/health_monitor/built_in_checks"
|
3
|
+
require File.dirname(__FILE__) + "/health_monitor/health_monitoring"
|
4
|
+
|
5
|
+
ActionController::Base.extend HealthMonitor::HealthMonitoring::ActsAsHealthMonitor
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module HealthMonitor
|
2
|
+
module BuiltInChecks
|
3
|
+
protected
|
4
|
+
|
5
|
+
# Check the schema version
|
6
|
+
def schema_check
|
7
|
+
version = ActiveRecord::Base.connection.select_value(
|
8
|
+
'select version from schema_migrations order by version limit 1'
|
9
|
+
)
|
10
|
+
|
11
|
+
{
|
12
|
+
:status => :success,
|
13
|
+
:message => "Schema version #{version}",
|
14
|
+
:description => 'Check database connection and schema'
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
# Check db connection
|
19
|
+
def database_check
|
20
|
+
{
|
21
|
+
:status => ActiveRecord::Base.connection && ActiveRecord::Base.connection.active?,
|
22
|
+
:description => 'Active Database connection',
|
23
|
+
:message => "DATABASE CHECK #{ActiveRecord::Base.connection.active?}"
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
# Call ey-agent to return nginx or apache status as well as database status
|
28
|
+
def ey_agent_check
|
29
|
+
results = {
|
30
|
+
:description => 'Run ey-agent to monitor haproxy and monitor'
|
31
|
+
}
|
32
|
+
|
33
|
+
agent_results = JSON.load( `sudo ey-agent` )
|
34
|
+
|
35
|
+
results.update(
|
36
|
+
:message => agent_results.inspect,
|
37
|
+
:status => agent_results && agent_results.any?{|k,v| v == 'down' }
|
38
|
+
)
|
39
|
+
rescue => e
|
40
|
+
return results.update( :status => :failure, :exception => e )
|
41
|
+
end
|
42
|
+
|
43
|
+
# return true if the pid is alive
|
44
|
+
def pid_alive?( pid, options = {} )
|
45
|
+
`#{'sudo' if options[:sudo]} kill -0 #{pid}; echo $?`.chomp.to_i == 0
|
46
|
+
end
|
47
|
+
|
48
|
+
# ideas for this came from the ey-flex gem
|
49
|
+
# +process_name+ - name of the process to monitor
|
50
|
+
# === Options
|
51
|
+
# +sudo+ - (default false) set to true to run pgrep as sudo
|
52
|
+
# +pattern+ - specify a pattern to match (pgrep -f) instead of the process_name
|
53
|
+
# +arguments+ - additional arguments to pgrep "-o root,blah -a"
|
54
|
+
def process_alive?( process_name, options = {})
|
55
|
+
return if process_name == ''
|
56
|
+
|
57
|
+
cmd = []
|
58
|
+
cmd << 'sudo' if options[:sudo]
|
59
|
+
cmd << 'pgrep'
|
60
|
+
cmd << options[:arguments].to_s if options[:arguments]
|
61
|
+
cmd << "-f" if options[:pattern]
|
62
|
+
cmd << (options[:pattern].is_a?( String ) ? options[:pattern] : process_name)
|
63
|
+
|
64
|
+
pids = `#{cmd.join(' ')}`.split("\n")
|
65
|
+
!pids.empty? && pids.all? {|p| pid_alive?( p )}
|
66
|
+
end
|
67
|
+
|
68
|
+
# Return a health monitor hash of status and description for
|
69
|
+
# monitoring the specified process
|
70
|
+
def process_health( process_name, options = {} )
|
71
|
+
{
|
72
|
+
:status => process_alive?( process_name, options ),
|
73
|
+
:description => "Check that process #{process_name} is alive"
|
74
|
+
}
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -90,10 +90,12 @@
|
|
90
90
|
# To disable and write your own routes, use +route+ option with +acts_as_health_monitor+
|
91
91
|
# acts_as_health_monitor :route => false
|
92
92
|
#
|
93
|
+
module HealthMonitor
|
93
94
|
module HealthMonitoring
|
94
95
|
def self.included( base )
|
95
96
|
base.class_eval do
|
96
97
|
extend HealthMonitoring::ClassMethods
|
98
|
+
include HealthMonitor::BuiltInChecks
|
97
99
|
cattr_accessor :monitored_features
|
98
100
|
self.monitored_features ||= ActiveSupport::OrderedHash.new
|
99
101
|
|
@@ -119,7 +121,7 @@ module HealthMonitoring
|
|
119
121
|
#
|
120
122
|
module ActsAsHealthMonitor
|
121
123
|
def acts_as_health_monitor( options = {} )
|
122
|
-
include HealthMonitoring unless method_defined?( :health_monitor )
|
124
|
+
include HealthMonitor::HealthMonitoring unless method_defined?( :health_monitor )
|
123
125
|
|
124
126
|
setup_health_monitor_routes( options.delete( :route ) )
|
125
127
|
end
|
@@ -134,17 +136,8 @@ module HealthMonitoring
|
|
134
136
|
:show
|
135
137
|
end
|
136
138
|
|
137
|
-
|
138
139
|
ActionController::Routing::Routes.draw do |map|
|
139
140
|
map.resource controller_name, :controller => controller_name, :only => base_methods, :member => { :monitor_health => :get }
|
140
|
-
#map.named_route controller_name, controller_name + '.:format', :conditions => {:method => :get}, :controller => controller_name, :action => 'monitor_health'
|
141
|
-
#map.named_route "monitor_health_#{controller_name}", "#{controller_name}/monitor_health.:format", :conditions => {:method => :get}, :controller => controller_name, :action => 'monitor_health'
|
142
|
-
|
143
|
-
#map.resource controller_name, :controller => controller_name, :only => :none, :member => { :monitor_health => :get } do |r|
|
144
|
-
# r.named_route '', '.:format', :conditions => {:method => :get}, :controller => controller_name, :action => 'monitor_health'
|
145
|
-
#end
|
146
|
-
#map.named_route route_name, route_path, :controller => controller_name, :action => 'health', :method => :get
|
147
|
-
#map.connect "#{controller_name}", :controller => controller_name, :action => 'health'
|
148
141
|
end
|
149
142
|
end
|
150
143
|
end
|
@@ -156,6 +149,31 @@ module HealthMonitoring
|
|
156
149
|
options[:method] = block if block_given?
|
157
150
|
features.each {|name| self.monitored_features[ name.to_sym ] = options }
|
158
151
|
end
|
152
|
+
|
153
|
+
# Monitor a server process
|
154
|
+
# +process_name+ - name of the process to monitor
|
155
|
+
# === Options
|
156
|
+
# +sudo+ - (default false) set to true to run pgrep as sudo
|
157
|
+
# +pattern+ - specify a pattern to match (pgrep -f) instead of the process_name
|
158
|
+
# +arguments+ - additional arguments to pgrep "-o root,blah -a"
|
159
|
+
# === Examples
|
160
|
+
# monitor_process :monit, :someproc, :sudo => true
|
161
|
+
# monitor_process :mongod, :pattern => 'mongodb.*slave'
|
162
|
+
def monitor_process( *processes )
|
163
|
+
options = processes.extract_options!
|
164
|
+
options.symbolize_keys!
|
165
|
+
|
166
|
+
processes.each do |process_name|
|
167
|
+
monitor_name = "check_process_#{process_name.to_s.underscore}".to_sym
|
168
|
+
class_eval <<-END_SRC, __FILE__, __LINE__
|
169
|
+
def #{monitor_name}
|
170
|
+
process_health( #{process_name.inspect}, monitored_features[#{monitor_name.inspect}] )
|
171
|
+
end
|
172
|
+
END_SRC
|
173
|
+
|
174
|
+
monitor_health monitor_name, options
|
175
|
+
end
|
176
|
+
end
|
159
177
|
end
|
160
178
|
|
161
179
|
# Show a status page showing the health of monitored features
|
@@ -174,7 +192,6 @@ module HealthMonitoring
|
|
174
192
|
end
|
175
193
|
|
176
194
|
protected
|
177
|
-
|
178
195
|
def monitor_health_of( feature_name )
|
179
196
|
feature_status = { :name => feature_name }
|
180
197
|
result = nil
|
@@ -189,6 +206,11 @@ module HealthMonitoring
|
|
189
206
|
else send( method )
|
190
207
|
end
|
191
208
|
rescue => e
|
209
|
+
logger.error(
|
210
|
+
"Health Monitor Error for feature '#{feature_name}': " +
|
211
|
+
" ##{e.inspect}\n #{e.backtrace.join("\n")}"
|
212
|
+
) if defined?( logger ) && logger
|
213
|
+
|
192
214
|
return { :status => :failure, :exception => e }
|
193
215
|
end
|
194
216
|
|
@@ -205,7 +227,7 @@ module HealthMonitoring
|
|
205
227
|
def on_unhealthy() end
|
206
228
|
|
207
229
|
def health_check_template
|
208
|
-
File.join( File.dirname(__FILE__), "
|
230
|
+
File.join( File.dirname(__FILE__), "/../../generators/health_monitor/templates/_health_monitor.html.erb" )
|
209
231
|
end
|
210
232
|
|
211
233
|
def health_response_code
|
@@ -304,3 +326,4 @@ module HealthMonitoring
|
|
304
326
|
set_default_health_status!( feature_status )
|
305
327
|
end
|
306
328
|
end
|
329
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: health_monitor
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Blythe Dunham
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-12-
|
12
|
+
date: 2009-12-13 00:00:00 -08:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -72,8 +72,8 @@ files:
|
|
72
72
|
- generators/health_monitor/templates/controller.rb
|
73
73
|
- health_monitor.gemspec
|
74
74
|
- lib/health_monitor.rb
|
75
|
-
- lib/
|
76
|
-
-
|
75
|
+
- lib/health_monitor/built_in_checks.rb
|
76
|
+
- lib/health_monitor/health_monitoring.rb
|
77
77
|
- test/helper.rb
|
78
78
|
- test/test_health_monitor.rb
|
79
79
|
has_rdoc: true
|
@@ -1,29 +0,0 @@
|
|
1
|
-
|
2
|
-
<h1>Health O Meter</h1>
|
3
|
-
<h2><%= healthy? ? "OK" : "FAIL" %></h2>
|
4
|
-
|
5
|
-
<table border="1" width="90%">
|
6
|
-
<tr>
|
7
|
-
<th></th>
|
8
|
-
<th>Name</th>
|
9
|
-
<th>Result</th>
|
10
|
-
<th>Time (s)</th>
|
11
|
-
<th>Description</th>
|
12
|
-
</tr>
|
13
|
-
<% monitored_features.each do |name, options| %>
|
14
|
-
<tr>
|
15
|
-
<td style="background-color:<%= status_color( name )%>">
|
16
|
-
<%= h status_text( name ) %>
|
17
|
-
</td>
|
18
|
-
<td><%= h name.to_s.titleize %></td>
|
19
|
-
<td><%= monitored_message( name ) %></td>
|
20
|
-
<td><%= monitored_timing( name ) %></td>
|
21
|
-
<td><%= monitored_description( name ) %></td>
|
22
|
-
</tr>
|
23
|
-
<% end %>
|
24
|
-
</table>
|
25
|
-
|
26
|
-
<br>
|
27
|
-
<b>Total: </b><%= total_time %> <br>
|
28
|
-
<b>PID: </b><%= $$ %><br>
|
29
|
-
<b>host: </b><%= request.host %>
|