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 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 %>