/health_check

Simple health check of Rails app for use with uptime.openacs.org or wasitup.com

Primary LanguageRubyMIT LicenseMIT

health_check gem

Simple health check of Rails 4.x+ apps for use with Pingdom, NewRelic, EngineYard or uptime.openacs.org etc.

Use

  • master branch or gem versions ~> 2.0 for Rails 4.x and 5.x;

  • pre_rails4 branch or gem versions ~> 1.7 for Rails 3.x;

  • rails2.3 branch or gem versions ~> 1.6.1 for Rails 2.3;

The basic goal is to quickly check that rails is up and running and that it has access to correctly configured resources (database, email gateway)

health_check provides various monitoring URIs, for example:

curl localhost:3000/health_check
success

curl localhost:3000/health_check/all.json
{"healthy":true,"message":"success"}

curl localhost:3000/health_check/database_cache_migration.xml
<?xml version="1.0" encoding="UTF-8"?>
<hash>
  <healthy type="boolean">true</healthy>
  <message>success</message>
</hash>

On failure a 500 http status is returned with a simple explanation of the failure

curl localhost:3000/health_check/fail
health_check failed: invalid argument to health_test.

The health_check controller disables sessions for versions that eagerly load sessions. Only failures are logged for rails < ‘4.1’.

Checks

  • standard (default) - site, database and migrations checks are run plus email if ActionMailer is defined and it is not using the default configuration

  • all / full - all checks are run (can be overriden in config block)

  • cache - checks that a value can be written to the cache

  • custom - runs checks added via config.add_custom_check

  • database - checks that the current migration level can be read from the database

  • email - basic check of email - :test returns true, :sendmail checks file is present and executable, :smtp sends HELO command to server and checks response

  • migration - checks that the database migration level matches that in db/migrations

  • redis / redis-if-present - checks Redis connectivity

  • resque-redis / resque-redis-if-present - checks Resque connectivity to Redis

  • s3 / s3-if-present - checks proper permissions to s3 buckets

  • sidekiq-redis / sidekiq-redis-if-present - checks Sidekiq connectivity to Redis

  • site - checks rails is running sufficiently to render text

Some checks have a *-if-present form, which only runs the check if the corresponding library has been required.

The email gateway is not checked unless the smtp settings have been changed. Specify full or include email in the list of checks to verify the smtp settings (eg use 127.0.0.1 instead of localhost).

Note: rails 4.0 also checks migrations by default in development mode and throws an ActiveRecord::PendingMigrationError exception (http error 500) if there is an error

Installation

Add the following line to Gemfile

gem "health_check"

And then execute

bundle

Or install it yourself as:

gem install health_check

Configuration

To change the configuration of health_check, create a file ‘config/initializers/health_check.rb` and add a configuration block like:

HealthCheck.setup do |config|

  # Text output upon success
  config.success = 'success'

  # Timeout in seconds used when checking smtp server
  config.smtp_timeout = 30.0

  # http status code used when plain text error message is output
  # Set to 200 if you want your want to distinguish between partial (text does not include success) and
  # total failure of rails application (http status of 500 etc)

  config.http_status_for_error_text = 500

  # http status code used when an error object is output (json or xml)
  # Set to 200 if you want your want to distinguish between partial (healthy property == false) and
  # total failure of rails application (http status of 500 etc)

  config.http_status_for_error_object = 500

  # bucket names to test connectivity - required only if s3 check used, access permissions can be mixed
  config.buckets = {'bucket_name' => [:R, :W, :D]}

  # You can customize which checks happen on a standard health check, eg to set an explicit list use:
  config.standard_checks = [ 'database', 'migrations', 'custom' ]

  # Or to exclude one check:
  config.standard_checks -= [ 'emailconf' ]

  # You can set what tests are run with the 'full' or 'all' parameter
  config.full_checks = ['database', 'migrations', 'custom', 'email', 'cache', 'redis', 'resque-redis', 'sidekiq-redis', 's3']

  # Add one or more custom checks that return a blank string if ok, or an error message if there is an error
  config.add_custom_check do
    CustomHealthCheck.perform_check # any code that returns blank on success and non blank string upon failure
  end

  # max-age of response in seconds
  # cache-control is public when max_age > 1 and basic_auth_username is not set
  # You can force private without authentication for longer max_age by
  # setting basic_auth_username but not basic_auth_password
  config.max_age = 1

  # Protect health endpoints with basic auth
  # These default to nil and the endpoint is not protected
  config.basic_auth_username = 'my_username'
  config.basic_auth_password = 'my_password'
end

You may call add_custom_check multiple times with different tests. These tests will be included in the default list (“standard”).

If you have a catchall route then add the following line above the catch all route (in ‘config/routes.rb`):

health_check_routes

To change the url prefix (eg to /ping), add the following line in ‘config/routes.rb`:

health_check_routes('ping')

Installing As Middleware

To use health_check as middleware add the following line to the config/application.rb:

`config.middleware.insert_after "Rails::Rack::Logger", HealthCheck::MiddlewareHealthcheck`

In config/initializers/health_check.rb config.uri can be changed to other than default ‘health_check’

Uptime Monitoring

Use a website monitoring service to check the url regularly for the word “success” (without the quotes) rather than just a 200 http status so that any substitution of a different server or generic information page should also be reported as an error.

If an error is encounted, the text “health_check failed: some error message/s” will be returned and the http status will be 500.

See

Requesting Json and XML responses

Health_check will respond with an encoded hash object if json or xml is requested. Either set the HTTP Accept header or append .json or .xml to the url.

The hash contains two keys:

  • healthy - true if requested checks pass (boolean)

  • message - text message (“success” or error message)

The following commands

curl -v localhost:3000/health_check.json
curl -v localhost:3000/health_check/email.json
curl -v -H "Accept: application/json" localhost:3000/health_check

Will return a result with Content-Type: application/json and body like:

{"healthy":true,"message":"success"}

These following commands

curl -v localhost:3000/health_check.xml
curl -v localhost:3000/health_check/migration_cache.xml
curl -v -H "Accept: text/xml" localhost:3000/health_check/cache

Will return a result with Content-Type: application/xml and body like:

<?xml version="1.0" encoding="UTF-8"?>
<hash>
  <healthy type="boolean">true</healthy>
  <message>success</message>
</hash>

See github.com/ianheggie/health_check/wiki/Ajax-Example for an Ajax example

Silencing log output

It is recomended that you use silencer, lograge or one of the other log filtering gems.

For example, with lograge use the following to exclude health_check from being logged:

config.lograge.ignore_actions = ["health_check/health_check#index"]

Likewise you will probably want to exclude health_check from monitoring systems like newrelic.

Caching

Cache-control is set with

  • public if max_age is > 1 and basic_auth_username is not set (otherwise private)

  • no-cache

  • must-revalidate

  • max-age (default 1)

Last-modified is set to the current time (rounded down to a multiple of max_age when max_age > 1)

Note on Patches/Pull Requests

Feedback welcome! Especially with suggested replacement code and corresponding tests

  1. Fork it

  2. Create your feature branch (git checkout -b my-new-feature)

  3. Commit your changes (git commit -am 'Add some feature')

  4. Push to the branch (git push origin my-new-feature)

  5. Create new Pull Request.

Known Issues

  • No inline documentation for methods

  • rvm gemsets breaks the test - specifically rvm use 1.9.3 works but rvm gemset use ruby-1.9.3-p385@health_check --create triggers a “Could not find gem ‘coffee-rails (~> 3.2.1) ruby’ in the gems available on this machine.” error in the last call to bundle (installing health_check as a gem via a path into the temp railsapp)

Similar projects

  • fitter_happier plugin by atmos - plugin with similar goals, but not compatible with uptime, and does not check email gateway

Testing

Automated testing and other checks

Manual testing

The test will package up and install the gem under a temporary path, create a dummy rails app configured for sqlite, install the gem, and then run up tests against the server. This will require TCP port 3456 to be free.

Using rbenv or rvm, install and set the version of ruby you wish to test against. You will need to install the bundler gem if using rbenv. See the .travis.yml file for the list of ruby versions that we test against.

  • rbenv command: rbenv shell 1.8.7-p371

  • rvm command: rvm use 1.9.3

Create a temp directory for throw away testing, and clone the health_check gem into it

mkdir -p ~/tmp
cd ~/tmp
git clone https://github.com/ianheggie/health_check.git

Cd to the checked out health_check directory and then run the test as follows:

cd ~/tmp/health_check

test/test_with_railsapp

The script will first call ‘test/setup_railsapp` to setup a rails app with health_check installed and then run up the rails server and perform veraious tests.

The script ‘test/setup_railsapp` will prompt you for which gemfile under test you wish to use to install the appropriate rails version, and then setup tmp/railsapp accordingly.

The command ‘rake test` will also launch these tests, except it cannot install the bundler and rake gems if they are missing first (unlike test/test_with_railsapp)

Copyright © 2010-2014 Ian Heggie, released under the MIT license. See MIT-LICENSE for details.

Contributors

Thanks go to the various people who have given feedback and suggestions via the issues list and pull requests.

Contributing

  1. Fork it

  2. Create your feature branch (‘git checkout -b my-new-feature`)

  3. Commit your changes (‘git commit -am ’Add some feature’‘)

  4. Push to the branch (‘git push origin my-new-feature`)

  5. Create new Pull Request (Code with BDD tests are favoured)