health_monitor 0.0.1 → 0.1.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/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 %>
|