Skip to content

Instantly share code, notes, and snippets.

@nicolas-brousse
Last active January 13, 2023 23:04
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save nicolas-brousse/8f9bbf1ba7e34a98d55d6a25bc3ba205 to your computer and use it in GitHub Desktop.
Save nicolas-brousse/8f9bbf1ba7e34a98d55d6a25bc3ba205 to your computer and use it in GitHub Desktop.
Rails health check

HealthCheck

Install

Add the following line to your Gemfile

gem "server_health_check-rails"

Usage

Copy health_check.rb in config/initializers/health_check.rb

HEALTHCHECK_SECRET_KEY env var allow you to add a token in url.

You could update or create new checks. The example do the following checks:

  • active_record: Check ActiveRecord
  • redis: Check redis connection
  • sidekiq: Check sidekiq redis respond to PING
  • cache: Check Rails.cache write and read
  • database: Check database respond
  • migrations: Check migrations are up to date
  • activestorage: Check ActiveStorage upload and download works
  • external_services: Here you can check external status (ex: check status page of shopify)
  • email: Check email configuration (smtp only)

Response

{
  "status": {
    "heartbeat": "OK",
    "active_record": "OK",
    "redis": "OK",
    "sidekiq": "OK",
    "cache": "OK",
    "database": "OK",
    "migrations": "OK",
    "activestorage": "OK",
    "external_services": "OK",
    "email": "OK"
  }
}
# frozen_string_literal: true
module HealthCheck
CACHE_TIMEOUT = 10.seconds
SMTP_TIMEOUT = 30.0.seconds
class << self
def heartbeat
true
end
def check_database
ActiveRecord::Migrator.current_version if defined?(ActiveRecord)
end
def check_migrations
ActiveRecord::Migration.check_pending!.nil?
end
def check_cache
unless Rails.cache.write("__health_check_cache_test__", "ok", expires_in: CACHE_TIMEOUT)
raise "Unable to write to cache."
end
raise "Unable to read to cache." unless Rails.cache.read("__health_check_cache_test__") == "ok"
true
end
def check_activestorage
service = ActiveStorage::Blob.service
service.upload(".healthcheck", StringIO.new("ok"))
service.download(".healthcheck") == "ok"
end
def check_email
case ActionMailer::Base.delivery_method
when :smtp
check_smtp(ActionMailer::Base.smtp_settings)
else
false
end
end
def check_smtp(settings)
status = ""
smtp_client.start(settings[:domain], settings[:user_name], settings[:password], settings[:authentication]) do
status = smtp.helo(settings[:domain]).status
end
raise "SMTP: #{status || "unexpected error"}." unless status.match?(/^250/)
true
end
def check_external_services
# test external services like Shopify.
#
# response = Net::HTTP.get_response(URI("https://status.shopify.com/index.json"))
# (JSON.parse(response.body)&.dig("status", "indicator")) == "none"
end
def check_sidekiq
raise "Wrong configuration. Missing 'sidekiq' gem" unless defined?(::Sidekiq)
::Sidekiq.redis do |r|
raise "Sidekiq.redis.ping returned #{res.inspect} instead of PONG" unless r.ping == "PONG"
end
true
end
private
def smtp_client(settings)
smtp = Net::SMTP.new(settings[:address], settings[:port])
smtp.enable_starttls_auto if settings[:enable_starttls_auto]
smtp.open_timeout = SMTP_TIMEOUT
smtp.read_timeout = SMTP_TIMEOUT
smtp
end
end
end
secret_key = "-#{ENV["HEALTHCHECK_SECRET_KEY"]}" if ENV.key?("HEALTHCHECK_SECRET_KEY")
ServerHealthCheckRails::Config.path = +"/health_check#{secret_key}"
ServerHealthCheckRails::Config.logger = Rails.logger
ServerHealthCheckRails.check("heartbeat") { HealthCheck.heartbeat }
ServerHealthCheckRails.check_active_record!
ServerHealthCheckRails.check_redis!
ServerHealthCheckRails.check("sidekiq") { HealthCheck.check_sidekiq }
ServerHealthCheckRails.check("cache") { HealthCheck.check_cache }
ServerHealthCheckRails.check("database") { HealthCheck.check_database }
ServerHealthCheckRails.check("migrations") { HealthCheck.check_migrations }
ServerHealthCheckRails.check("activestorage") { HealthCheck.check_activestorage }
# ServerHealthCheckRails.check("external_services") { HealthCheck.check_external_services }
ServerHealthCheckRails.check("email") { HealthCheck.check_email }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment