Project

wellness

0.01
No commit activity in last 3 years
No release in over 3 years
A rack middleware health check
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Development

~> 1.3
>= 0
>= 0
>= 0
 Project Readme

Wellness

Build Status Code Climate

A rack middleware library that adds a health check to your service. It comes with pre made services and has the option and flexibility for you to make your own.

Usage - Rails

There are some pre configured services that are provided with this gem. However, you must require them when you need them. This is because they have external dependencies that need to be loaded, that your application may not necessarily have.

module MySuperCoolApplication
  class Application < Rails::Application
    system  = Wellness::System.new('my-super-cool-app')
    service = Wellness::Services::PostgresService.new({
      host:     ENV['POSTGRESQL_HOST'],
      port:     ENV['POSTGRESQL_PORT'],
      database: ENV['POSTGRESQL_DATABASE'],
      user:     ENV['POSTGRESQL_USERNAME'],
      password: ENV['POSTGRESQL_PASSWORD']
    })
    system.add_service('database', service, { critical: true })
    service = Wellness::Services::RedisService.new({
      host: ENV['REDIS_HOST']
    })
    system.add_service('redis', service, { critical: false})

    config.middleware.insert_before('::ActiveRecord::QueryCache', 'Wellness::Middleware', system)
  end
end

Usage - Sinatra

require 'wellness'

system  = Wellness::System.new('my-super-cool-app')
service = Wellness::Services::PostgresService.new({
  host:     ENV['POSTGRESQL_HOST'],
  port:     ENV['POSTGRESQL_PORT'],
  database: ENV['POSTGRESQL_DATABASE'],
  user:     ENV['POSTGRESQL_USERNAME'],
  password: ENV['POSTGRESQL_PASSWORD']
})
system.add_service('database', service, { critical: true })
service = Wellness::Services::RedisService.new({
  host: ENV['REDIS_HOST']
})
system.add_service('redis', service, { critical: false})

use(Wellness::Middleware, system)

Example Responses

{
  "status": "UNHEALTHY",
  "details": {
    "git" : {
      "revision" : "1234567"
    }
  },
  "dependencies":{
    "database":{
      "status":"UNHEALTHY",
      "details":{
        "error":"no response from ping"
      }
    },
    "sidekiq":{
      "status":"HEALTHY",
      "details":{
        "processed":0,
        "failed":0,
        "busy":0,
        "enqueued":0,
        "scheduled":0,
        "retries":0,
        "default_latency":0,
        "redis":{
          "uptime_in_days":"0",
          "connected_clients":"1",
          "used_memory_human":"979.22K",
          "used_memory_peak_human":"1.02M"
        }
      }
    }
  }
}
{
    "status": "HEALTHY",
    "services": {
        "postgresql": {
            "status": "HEALTHY",
            "details": { }
        },
        "mysql": {
            "status": "HEALTHY",
            "details": { }
        }
    },
    "details": {
        "git": {
            "revision": "1234567"
        }
    }
}

Custom Services

Creating a custom service is super easy. As long as the service responds to #call, you are just fine. Passing a lambda or Proc is perfectly acceptable.

Requirements

This interface has a few requirements.

  1. A service MUST respond to #call
  2. A hash MUST be returned
  3. The hash returned MUST contain a status key at the root level.
  4. Status can ONLY be HEALTHY, UNHEALTHY, and DEGRADED
  5. All hash keys MUST be strings

Example

module MyServices
  class MySQLService
    def initialize(args={})
      @connection_options = {
        host: args[:host],
        port: args[:port],
        database: args[:database],
        username: args[:username],
        password: args[:password]
      }
    end

    def healthy(details={})
      {
        'status' => 'HEALTHY',
        'details' => details
      }
    end

    def unhealthy(details={})
      {
        'status' => 'UNHEALTHY',
        'details' => details
      }
    end

    def call
      connection = Mysql2::Client.new(@connection_options)
      connection.ping ? healthy : unhealthy
    rescue Mysql2::Error => error
      unhealthy('error' => error.message)
    end
  end
end

system = Wellness::System.new('my-app')
system.use('mysql', MyServices::MySQLService.new({
  # ... configuration ...
}))

Custom Details

Details are useful if you want to display information on an application like the current git revision, or how many requests have been serviced by the application.

Requirements

  1. A detail MUST respond to #call
  2. A hash MUST be returned
  3. All hash keys MUST be strings

Example

# Your custom detail component
module MyDetails
  class GitRevisionDetail
    def call
      {
        'revision' => revision
      }
    end

    def revision
      `git rev-parse --short HEAD`.chomp
    end
  end
end
# Initialize the wellness system
system = Wellness::System.new('my-app')
system.use('git', MyDetails::GitRevisionDetail.new)

# Load it into your rack
use(Wellness::Middleware, system)

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