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 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
- === Installation
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 blythedunham-health_monitor --sources=http://gemcutter
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
- # montior the database connection
38
- monitor_health :database, :description => 'Check database connection'
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 database; ActiveRecord::Base.connection; end
79
+ def user_check; User.first; end
54
80
 
55
81
  end
56
82
 
57
- === +monitor_health+ Options
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.0.1
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
- # # montior the database connection
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
- # :timeout => 240000 # Fail this test if it exceeds 4 minutes
13
- # :method => lambda{ |controller| ActionMailer::Base.deliver_my_mail('blah') }
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 database; ActiveRecord::Base.connection; end
24
- #
27
+ # def user_check; User.first; end
25
28
 
26
29
  <% for action in actions -%>
27
30
  def <%= action %>
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{health_monitor}
8
- s.version = "0.0.1"
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-06}
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/health_monitoring.rb",
30
- "templates/_health_monitor.html.erb",
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
  ]
@@ -1,2 +1,5 @@
1
- require File.dirname(__FILE__) + "/health_monitoring"
2
- ActionController::Base.extend HealthMonitoring::ActsAsHealthMonitor
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__), "/../generators/health_monitor/templates/_health_monitor.html.erb" )
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.0.1
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-06 00:00:00 -08:00
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/health_monitoring.rb
76
- - templates/_health_monitor.html.erb
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 %>