Rapporteur

Gem Version Build Status Code Climate

This gem provides a singular, status-checking endpoint to your application. The endpoint provides a JSON response with either an HTTP 200 or an HTTP 500 response, depending on the current application environment.

When the environment tests successfully, an HTTP 200 response is returned with the current application Git revision and server time:

{
  "revision": "906731e6467ea381ba5bc70f103b85ed4178fee7",
  "time": "2013-05-19T05:38:46Z"
}

When an application validation fails, an HTTP 500 response is returned with a collection of error messages, similar to the default Rails responders for model validations:

{
  "errors": {
    "base": ["The application database is inaccessible or unavailable"]
  }
}

Installation

Supported Ruby versions:

  • MRI 1.9.2
  • MRI 1.9.3
  • JRuby 1.7.4

Supported Rails versions:

  • Rails 3.1.x.
  • Rails 3.2.x.

To install, add this line to your application's Gemfile:

gem 'rapporteur'

And then execute:

$ bundle

Usage

Simply adding the gem requirement to your Gemfile is enough to install and automatically load and configure the gem. Technically speaking, the gem is a Rails Engine, so it auto-initializes with Rails starts up. There is no further configuration necessary.

By default, there are no application checks that run and the status endpoint simply reports the current application revision and time. This is useful for a basic connectivity check to be watched by a third party service like Pingdom for a very simple, non-critical application.

You may optionally use any of the pre-defined checks (such as the ActiveRecord connection check) to expand the robustness of the status checks. Adding a check will execute that check each time the status endpoint is requested, so be somewhat wary of doing too much. See more in the Adding checks section, below.

Further, you can define your own checks which could be custom to your application or environment and report their own, unique errors. The only requirement is that the check objects are callable (respond to #call, like a Proc). See more in the Creating custom checks section, below.

The endpoint

This gem provides a new, single endpoint in your application. Specifically, it creates a named /status.json route, with the "status" name. It does not match on any other format or variation, which isolates the pollution of your application routes.

If you'd like to link to the status endpoint from within your application (why, I couldn't guess), you can use a standard Rails URL helper:

link_to status_path

Were you already using the /status.json endpoint or the "status" route name? Hmm. Well... you just broke it.

Customization

Adding checks

This gem ships with the following checks tested and packaged:

  • Rapporteur::Checks::ActiveRecordCheck - Performs a trivial test of the current ActiveRecord::Base.connection to ensure basic database connectivity.

To add checks to your application, define the checks you'd like to run in your environment or application configuration files or initializers, such as:

# config/initializers/rapporteur.rb
Rapporteur.add_check(Rapporteur::Checks::ActiveRecordCheck)

Or, make an environment specific check with:

# config/environments/production.rb
MyApplication.configure do
  config.to_prepare do
    Rapporteur.add_check(Rapporteur::Checks::ActiveRecordCheck)
  end
end

Creating custom checks

It is simple to add a custom check to the status endpoint. All that is required is that you give the checker an object that is callable. In your object, simply check for the state of the world that you're interested in, and if you're not happy with it, add an error to the given checker instance:

# config/initializers/rapporteur.rb

Rapporteur.add_check do |checker|
  checker.add_message(:paid, "too much")
end

my_proc_check = lambda { |checker|
  checker.add_error("You have bad luck!") if rand(10) > 5
  checker.add_message(:luck, "good")
}

Rapporteur.add_check(my_proc_check)

class MyClassCheck
  def self.call(checker)
    @@counter ||= 0
    checker.add_error("Stop calling me!!") if @@counter > 50
  end
end

Rapporteur.add_check(MyClassCheck)

Certainly, the definition and registration of the checks do not need to occur within the same file, but you get the idea. Also: Please make your checks more useful than those defined above. ;)

You could create a checker for your active Redis connection, Memcached connections, disk usage percentage, process count, memory usage, or really anything you like. Again, because these checks get executed every time the status endpoint is called, be mindful of the tradeoffs when making a check that may be resource intensive.

Customizing the revision

If you need to customize the way in which the current application revision is calculated (by default it runs a git rev-parse HEAD), you may do so by modifying the necessary environment file or creating an initializer in your Rails application:

# config/initializers/rapporteur.rb
Rapporteur::Revision.current = "revision123"
# config/environments/production.rb
MyApplication.configure do
  config.to_prepare do
    Rapporteur::Revision.current = "revision123"
  end
end

You may pass a String or a callable object (Proc) to .current= and it will be executed and memoized. Useful examples of this are:

# Read a Capistrano REVISION file
Rapporteur::Revision.current = Rails.root.join("REVISION").read.strip

# Force a particular directory and use Git
Rapporteur::Revision.current = `cd "#{Rails.root}" && git rev-parse HEAD`.strip

# Use an ENV variable (Heroku)
Rapporteur::Revision.current = ENV["REVISION"]

# Do some crazy calculation
Rapporteur::Revision.current = lambda { MyRevisionCalculator.execute! }

Customizing the error messages

The error messages displayed in the event that application validations fail are all collected through I18n. There are default localization strings provided with the gem, but you may override them as necessary, simply by redefining them in a locales file within your local application.

For example, to override the database check failure message:

en:
  activemodel:
    errors:
      models:
        rapporteur/checker:
          attributes:
            base:
              database_unavailable: "Something went wrong"

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