heartcheck 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/heartcheck +29 -0
- data/lib/generators/generator.rb +63 -0
- data/lib/generators/templates/config.rb +67 -0
- data/lib/heartcheck.rb +111 -0
- data/lib/heartcheck/app.rb +65 -0
- data/lib/heartcheck/checks.rb +3 -0
- data/lib/heartcheck/checks/base.rb +101 -0
- data/lib/heartcheck/checks/firewall.rb +26 -0
- data/lib/heartcheck/checks/process.rb +30 -0
- data/lib/heartcheck/controllers/base.rb +22 -0
- data/lib/heartcheck/controllers/dev.rb +34 -0
- data/lib/heartcheck/controllers/essential.rb +9 -0
- data/lib/heartcheck/controllers/functional.rb +9 -0
- data/lib/heartcheck/controllers/health_check.rb +9 -0
- data/lib/heartcheck/controllers/info.rb +11 -0
- data/lib/heartcheck/errors.rb +2 -0
- data/lib/heartcheck/errors/routing_error.rb +6 -0
- data/lib/heartcheck/errors/warning.rb +6 -0
- data/lib/heartcheck/logger.rb +53 -0
- data/lib/heartcheck/services.rb +1 -0
- data/lib/heartcheck/services/firewall.rb +72 -0
- data/lib/heartcheck/version.rb +3 -0
- metadata +248 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 30402737433dacfa08875ade181a6fb9570b6b0a
|
4
|
+
data.tar.gz: d6747a981694289118544f929e70cb254153bdf4
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3be0e45e564098d82e130030a69aaaca7c9858de1021629178745afd803db934f4ed439d9b9c580d67454519b950db42c082220b919992f8e08ae7336a6f73e7
|
7
|
+
data.tar.gz: d4129a1cf932d1ae0e3d724231dc03a0f7a02f158632318d372ee1c29f27cdc581c15983e1fdeafbe209711118769e29301b6a1ae131d9850b81a24991a319ab
|
data/bin/heartcheck
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'optparse'
|
5
|
+
$LOAD_PATH.push File.expand_path('../../lib', __FILE__)
|
6
|
+
require 'heartcheck'
|
7
|
+
|
8
|
+
optparse = OptionParser.new do |opts|
|
9
|
+
opts.banner = "Usage: heartcheck (rails|padrino|sinatra)\n\nYou can use flags as such:"
|
10
|
+
|
11
|
+
opts.on('-h', '--help', 'Display this screen') do
|
12
|
+
puts opts
|
13
|
+
exit
|
14
|
+
end
|
15
|
+
|
16
|
+
if ARGV.size != 1 || !%w(rails sinatra padrino).include?(ARGV[0])
|
17
|
+
puts opts
|
18
|
+
exit
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
begin
|
23
|
+
optparse.parse!
|
24
|
+
rescue OptionParser::InvalidOption => e
|
25
|
+
puts e
|
26
|
+
exit
|
27
|
+
end
|
28
|
+
|
29
|
+
Heartcheck::Generator.new(ARGV).invoke_all
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'thor/group'
|
2
|
+
|
3
|
+
module Heartcheck
|
4
|
+
class Generator < Thor::Group
|
5
|
+
include Thor::Actions
|
6
|
+
|
7
|
+
desc 'generate default files and instructions to use the Heartcheck'
|
8
|
+
|
9
|
+
argument :framework
|
10
|
+
|
11
|
+
def self.source_root
|
12
|
+
File.join(File.dirname(__FILE__), 'templates')
|
13
|
+
end
|
14
|
+
|
15
|
+
def show_framework
|
16
|
+
box framework.capitalize, :green
|
17
|
+
end
|
18
|
+
|
19
|
+
def generate_initializer
|
20
|
+
template('config.rb', initializer_path)
|
21
|
+
end
|
22
|
+
|
23
|
+
def generate_route
|
24
|
+
case framework
|
25
|
+
when 'rails'
|
26
|
+
instructions(
|
27
|
+
'config/routes.rb',
|
28
|
+
'mount Heartcheck::App.new, at: "/monitoring"'
|
29
|
+
)
|
30
|
+
when 'padrino', 'sinatra'
|
31
|
+
instructions(
|
32
|
+
'config.ru', %(require "heartcheck"
|
33
|
+
map "/monitoring" do
|
34
|
+
use Heartcheck::App
|
35
|
+
end)
|
36
|
+
)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def include_empty_lines
|
41
|
+
puts "\n\n"
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def instructions(file_name, file_content)
|
47
|
+
box "Include the following content to file #{file_name}"
|
48
|
+
say file_content, :blue
|
49
|
+
end
|
50
|
+
|
51
|
+
def box(content, color = :red)
|
52
|
+
size = content.size
|
53
|
+
say "#{'=' * (size + 2)}\n #{content}\n#{'=' * (size + 2)}\n", color
|
54
|
+
end
|
55
|
+
|
56
|
+
def initializer_path
|
57
|
+
case framework
|
58
|
+
when 'rails' then 'config/initializers/heartcheck.rb'
|
59
|
+
when 'padrino', 'sinatra' then 'app/app.rb'
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
Heartcheck.setup do |monitor|
|
2
|
+
# monitor has a default logger but you can use yours
|
3
|
+
# default value: Logger.new(STDOUT)
|
4
|
+
# monitor.logger = Rails.logger
|
5
|
+
|
6
|
+
# Services
|
7
|
+
# For each service you can set the folling options
|
8
|
+
# name: String => root name to show in report page (default: class.name)
|
9
|
+
# functional: Boolean => options to set if this service is optional (default: false)
|
10
|
+
# on_error: Block => to customize the errors (default: nil)
|
11
|
+
# to_validate: Block => to validate the sevices (default: nil)
|
12
|
+
#
|
13
|
+
# See the exemple bellow to undertand how you can configure it.
|
14
|
+
#
|
15
|
+
# monitor.add :base do |c|
|
16
|
+
# c.name = "filesystem"
|
17
|
+
# c.functional = true
|
18
|
+
# c.add_service(name: "my_file", path: "/var/www/my_project/my_file")
|
19
|
+
#
|
20
|
+
# c.on_error do |sevices|
|
21
|
+
# errors << "Custom error message for #{service[:name]}"
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# c.to_validate do |services, errors|
|
25
|
+
# services.each do |service|
|
26
|
+
# errors << "erro message" unless if File.exists?(service[:path])
|
27
|
+
# end
|
28
|
+
# end
|
29
|
+
# end
|
30
|
+
|
31
|
+
# Firewall
|
32
|
+
# check with telnet if the firewall is open to conect to the service
|
33
|
+
monitor.add :firewall do |c|
|
34
|
+
# you can add service wth (host and port) or (url)
|
35
|
+
c.add_service(host: 'lala.com', port: 80)
|
36
|
+
c.add_service(url: 'http://lalal.com')
|
37
|
+
|
38
|
+
# add dynamic services when you need it run after initializer
|
39
|
+
c.register_dynamic_services do
|
40
|
+
Pmta.all.map { |p| { host: p.host, port: 25 } }
|
41
|
+
end
|
42
|
+
|
43
|
+
# to customize default error message, the default is:
|
44
|
+
# "connection refused on: #{service.host}:#{service.port}"
|
45
|
+
# params
|
46
|
+
# errors: Array => errors to show on page
|
47
|
+
# service: FirewallService => object that respond to :host and :port
|
48
|
+
c.on_error do |_errors, service|
|
49
|
+
erros << "Custom error message for #{service.host}"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Redis
|
54
|
+
# check if you redis conection is working
|
55
|
+
# you need to send a redis conection as a service
|
56
|
+
monitor.add :redis do |c|
|
57
|
+
c.add_service(name: 'Redis', connection: Resque.redis)
|
58
|
+
end
|
59
|
+
|
60
|
+
# Cache
|
61
|
+
# check if your cache is working
|
62
|
+
# you can pass any kind of cache, but this need to respond to
|
63
|
+
# set, get and delete
|
64
|
+
monitor.add :cache do |c|
|
65
|
+
c.add_service(name: 'memcached', connection: Rails.cache.instance_variable_get('@data'))
|
66
|
+
end
|
67
|
+
end
|
data/lib/heartcheck.rb
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
module Heartcheck
|
2
|
+
require 'logger'
|
3
|
+
require 'heartcheck/app'
|
4
|
+
require 'heartcheck/checks'
|
5
|
+
require 'heartcheck/errors'
|
6
|
+
require 'heartcheck/services'
|
7
|
+
require 'heartcheck/logger'
|
8
|
+
|
9
|
+
@checks = []
|
10
|
+
|
11
|
+
class << self
|
12
|
+
# @attr [Array<Checks>] the checks to use when checking
|
13
|
+
attr_accessor :checks
|
14
|
+
# @attr_writer [Object] change the default logger
|
15
|
+
attr_writer :logger
|
16
|
+
|
17
|
+
# Is used to log some messages when checking if the logger
|
18
|
+
# is not set it's returns de default_logger.
|
19
|
+
#
|
20
|
+
# @return [Object] the logger object
|
21
|
+
def logger
|
22
|
+
@logger ||= default_logger
|
23
|
+
end
|
24
|
+
|
25
|
+
# @abstract
|
26
|
+
# Is used to configure.
|
27
|
+
#
|
28
|
+
# @yield A bock that recieve the class
|
29
|
+
# @example
|
30
|
+
# Heartcheck.setup do |c|
|
31
|
+
# puts c
|
32
|
+
# end
|
33
|
+
# @return [void]
|
34
|
+
def setup
|
35
|
+
yield(self)
|
36
|
+
end
|
37
|
+
|
38
|
+
# It's used to add an instance of a check
|
39
|
+
# to the check list
|
40
|
+
#
|
41
|
+
# @param [String] name to identify in the result page
|
42
|
+
# @param [Hash] options the options to create an instance of a check.
|
43
|
+
# @option options [String] :class The class name to get an instance
|
44
|
+
# @yield a block to config the instance
|
45
|
+
# @example
|
46
|
+
# Heartcheck.add(:base) do |c|
|
47
|
+
# c.name = 'Base check'
|
48
|
+
# end
|
49
|
+
# @return [void]
|
50
|
+
def add(name, options = {}, &block)
|
51
|
+
class_name = options.fetch(:class) { constantize(name) }
|
52
|
+
instance = Checks.const_get(class_name).new
|
53
|
+
|
54
|
+
if block_given?
|
55
|
+
checks << instance.tap(&block)
|
56
|
+
else
|
57
|
+
checks << instance
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# filter checks that are essential
|
62
|
+
#
|
63
|
+
# @return [Array<Check>] checks that are essential
|
64
|
+
def essential_checks
|
65
|
+
checks.select { |ctx| !ctx.functional? && !ctx.dev? }
|
66
|
+
end
|
67
|
+
|
68
|
+
# filter checks that are functional
|
69
|
+
#
|
70
|
+
# @return [Array<Check>] checks that are functional
|
71
|
+
def functional_checks
|
72
|
+
checks.select(&:functional?)
|
73
|
+
end
|
74
|
+
|
75
|
+
# filter checks that are not functional
|
76
|
+
#
|
77
|
+
# @return [Array<Check>] checks that are not functional
|
78
|
+
def dev_checks
|
79
|
+
checks.select { |ctx| !ctx.functional? }
|
80
|
+
end
|
81
|
+
|
82
|
+
# filter checks that has some information
|
83
|
+
#
|
84
|
+
# @return [Array<Check>] checks that respond to :info
|
85
|
+
def info_checks
|
86
|
+
checks.select { |ctx| ctx.respond_to?(:info) }
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
# if no logger is setted we create an instance
|
92
|
+
# of ruby logger with STDOUT
|
93
|
+
#
|
94
|
+
# @return [Logger] a ruby logger to STDOUT and info level
|
95
|
+
def default_logger
|
96
|
+
log = ::Logger.new(STDOUT)
|
97
|
+
log.level = ::Logger::INFO
|
98
|
+
log
|
99
|
+
end
|
100
|
+
|
101
|
+
# helper method to get a constant
|
102
|
+
#
|
103
|
+
# @example
|
104
|
+
# contantize('my_check') => 'MyCheck'
|
105
|
+
# @param [String] name of class
|
106
|
+
# @return [String]
|
107
|
+
def constantize(name)
|
108
|
+
name.to_s.split('_').map(&:capitalize).join
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'rack'
|
2
|
+
require 'heartcheck/controllers/base'
|
3
|
+
|
4
|
+
Dir.glob(File.expand_path('../controllers/*.rb', __FILE__))
|
5
|
+
.each { |x| require x }
|
6
|
+
|
7
|
+
# A web app that's use rack
|
8
|
+
module Heartcheck
|
9
|
+
class App
|
10
|
+
# A hash with paths as keys and controllers
|
11
|
+
# as values we use it in #dispath_action
|
12
|
+
# to routes the requests
|
13
|
+
ROUTE_TO_CONTROLLER = {
|
14
|
+
'/' => Controllers::Essential,
|
15
|
+
'' => Controllers::Essential,
|
16
|
+
'/functional' => Controllers::Functional,
|
17
|
+
'/dev' => Controllers::Dev,
|
18
|
+
'/info' => Controllers::Info,
|
19
|
+
'/health_check' => Controllers::HealthCheck
|
20
|
+
}
|
21
|
+
|
22
|
+
# Sets up the rack application.
|
23
|
+
#
|
24
|
+
# @param app [RackApp] is a rack app where
|
25
|
+
# heartcheck is included.
|
26
|
+
# @return [void]
|
27
|
+
def initialize(app = nil)
|
28
|
+
@app = app
|
29
|
+
end
|
30
|
+
|
31
|
+
# Sets up the rack application.
|
32
|
+
#
|
33
|
+
# @param env [Hash] is an instance of Hash
|
34
|
+
# that includes CGI-like headers.
|
35
|
+
# @return [Array] must be an array that contains
|
36
|
+
# - The HTTP response code
|
37
|
+
# - A Hash of headers
|
38
|
+
# - The response body, which must respond to each
|
39
|
+
def call(env)
|
40
|
+
req = Rack::Request.new(env)
|
41
|
+
|
42
|
+
[200, { 'Content-Type' => 'application/json' }, [dispatch_action(req)]]
|
43
|
+
rescue Heartcheck::Errors::RoutingError
|
44
|
+
[404, { 'Content-Type' => 'application/json' }, ['Not found']]
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
# Find a controller to espefic path
|
50
|
+
# and call the index method
|
51
|
+
#
|
52
|
+
# @param req [Rack::Request] an instance of request
|
53
|
+
# @return [String] a response body
|
54
|
+
def dispatch_action(req)
|
55
|
+
controller = ROUTE_TO_CONTROLLER[req.path_info]
|
56
|
+
fail Heartcheck::Errors::RoutingError if controller.nil?
|
57
|
+
|
58
|
+
Logger.info "Start [#{controller}] from #{req.ip} at #{Time.now}"
|
59
|
+
|
60
|
+
controller.new.index.tap do |_|
|
61
|
+
Logger.info "End [#{controller}]\n"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
module Heartcheck
|
2
|
+
module Checks
|
3
|
+
class Base
|
4
|
+
attr_accessor :functional, :dev, :name, :timeout
|
5
|
+
|
6
|
+
alias_method :functional?, :functional
|
7
|
+
alias_method :dev?, :dev
|
8
|
+
|
9
|
+
# call in Heartcheck.set
|
10
|
+
def initialize
|
11
|
+
@dynamic_services = nil
|
12
|
+
@error_proc = nil
|
13
|
+
@errors = []
|
14
|
+
@functional = false
|
15
|
+
@dev = false
|
16
|
+
@name = self.class.name.split('::').last.downcase
|
17
|
+
@services = []
|
18
|
+
@timeout = 0
|
19
|
+
@validate_proc = nil
|
20
|
+
end
|
21
|
+
|
22
|
+
def on_error(&block)
|
23
|
+
@error_proc = block if block_given?
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_validate(&block)
|
27
|
+
@validate_proc = block if block_given?
|
28
|
+
end
|
29
|
+
|
30
|
+
def register_dynamic_services(&block)
|
31
|
+
@dynamic_services = block if block_given?
|
32
|
+
end
|
33
|
+
|
34
|
+
def add_service(service)
|
35
|
+
@services << service
|
36
|
+
end
|
37
|
+
|
38
|
+
def services
|
39
|
+
if @dynamic_services
|
40
|
+
@services + @dynamic_services.call
|
41
|
+
else
|
42
|
+
@services
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def check
|
47
|
+
validation
|
48
|
+
hash = { name => { 'status' => (@errors.empty? ? 'ok' : 'error') } }
|
49
|
+
hash[name]['message'] = error_message unless @errors.empty?
|
50
|
+
|
51
|
+
Logger.info Oj.dump(hash)
|
52
|
+
hash
|
53
|
+
end
|
54
|
+
|
55
|
+
def informations
|
56
|
+
info
|
57
|
+
rescue => e
|
58
|
+
{ 'error' => e.message }
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def append_error(*args)
|
64
|
+
if @error_proc
|
65
|
+
@error_proc.call(@errors, *args)
|
66
|
+
else
|
67
|
+
custom_error(*args)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def validation
|
72
|
+
@errors = []
|
73
|
+
begin
|
74
|
+
Timeout.timeout(timeout, Heartcheck::Errors::Warning) do
|
75
|
+
if @validate_proc
|
76
|
+
@validate_proc.call(services, @errors)
|
77
|
+
else
|
78
|
+
validate
|
79
|
+
end
|
80
|
+
end
|
81
|
+
rescue Heartcheck::Errors::Warning => w
|
82
|
+
@errors = [{ type: 'warning', message: w.message }]
|
83
|
+
rescue => e
|
84
|
+
@errors = [e.message]
|
85
|
+
end
|
86
|
+
@errors
|
87
|
+
end
|
88
|
+
|
89
|
+
def error_message
|
90
|
+
@errors.map(&format_error)
|
91
|
+
end
|
92
|
+
|
93
|
+
def format_error
|
94
|
+
lambda do |error|
|
95
|
+
error.is_a?(Hash) ? error : { type: 'error', message: error }
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Heartcheck
|
2
|
+
module Checks
|
3
|
+
class Firewall < Base
|
4
|
+
def services
|
5
|
+
super.map { |opts| Services::Firewall.new(opts) }
|
6
|
+
end
|
7
|
+
|
8
|
+
def validate
|
9
|
+
services.each do |service|
|
10
|
+
begin
|
11
|
+
Net::Telnet.new(service.params)
|
12
|
+
rescue Errno::ECONNREFUSED; nil
|
13
|
+
rescue
|
14
|
+
append_error(service)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def custom_error(service)
|
22
|
+
@errors << "connection refused on: #{service.host}:#{service.port}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Heartcheck
|
2
|
+
module Checks
|
3
|
+
class Process < Base
|
4
|
+
def validate
|
5
|
+
services.each do |service|
|
6
|
+
begin
|
7
|
+
pid = get_pid(service)
|
8
|
+
::Process.kill(0, pid)
|
9
|
+
rescue Errno::ESRCH
|
10
|
+
append_error(service, pid)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def custom_error(service, pid)
|
18
|
+
@errors << "The process of #{service[:name]} is not run with pid #{pid}"
|
19
|
+
end
|
20
|
+
|
21
|
+
def get_pid(service)
|
22
|
+
if service[:pid]
|
23
|
+
service[:pid]
|
24
|
+
else
|
25
|
+
File.read(service[:file]).to_i
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'rack'
|
2
|
+
require 'oj'
|
3
|
+
|
4
|
+
Oj.default_options = { mode: :compat }
|
5
|
+
|
6
|
+
module Heartcheck
|
7
|
+
module Controllers
|
8
|
+
class Base
|
9
|
+
def index
|
10
|
+
fail NotImplementError
|
11
|
+
end
|
12
|
+
|
13
|
+
protected
|
14
|
+
|
15
|
+
def check(who)
|
16
|
+
Oj.dump(Heartcheck
|
17
|
+
.send("#{who}_checks")
|
18
|
+
.map(&:check))
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Heartcheck
|
2
|
+
module Controllers
|
3
|
+
class Dev < Base
|
4
|
+
def index
|
5
|
+
results = []
|
6
|
+
|
7
|
+
total_execution_time = time_diff do
|
8
|
+
checks = Heartcheck.dev_checks
|
9
|
+
|
10
|
+
results += checks.reduce([]) do |acc, elem|
|
11
|
+
context_result = {}
|
12
|
+
|
13
|
+
context_result['execution_time'] = time_diff do
|
14
|
+
context_result.merge!(elem.check)
|
15
|
+
end
|
16
|
+
|
17
|
+
acc << context_result
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
results << { 'total_execution_time' => total_execution_time }
|
22
|
+
Oj.dump(results)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def time_diff
|
28
|
+
start_time = Time.now
|
29
|
+
yield
|
30
|
+
'%.2f ms' % ((Time.now - start_time) * 1_000)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Heartcheck
|
2
|
+
# A simple Interface to log messages
|
3
|
+
class Logger
|
4
|
+
# log message with debug level
|
5
|
+
#
|
6
|
+
# @param message [String] message to log
|
7
|
+
# @return [void]
|
8
|
+
def self.debug(message)
|
9
|
+
logger(:debug, message)
|
10
|
+
end
|
11
|
+
|
12
|
+
# log message with info level
|
13
|
+
#
|
14
|
+
# @param message [String] message to log
|
15
|
+
#
|
16
|
+
# @return [void]
|
17
|
+
def self.info(message)
|
18
|
+
logger(:info, message)
|
19
|
+
end
|
20
|
+
|
21
|
+
# log message with warn level
|
22
|
+
#
|
23
|
+
# @param message [String] message to log
|
24
|
+
#
|
25
|
+
# @return [void]
|
26
|
+
def self.warn(message)
|
27
|
+
logger(:warn, message)
|
28
|
+
end
|
29
|
+
|
30
|
+
# log message with error level
|
31
|
+
#
|
32
|
+
# @param message [String] message to log
|
33
|
+
#
|
34
|
+
# @return [void]
|
35
|
+
def self.error(message)
|
36
|
+
logger(:error, message)
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
# Sent the message to Heartcheck logger
|
42
|
+
# that you can configure
|
43
|
+
#
|
44
|
+
# @see Heartcheck.logger
|
45
|
+
# @param level [Symbol] the level log
|
46
|
+
# @param message [String] message to log
|
47
|
+
#
|
48
|
+
# @return [void]
|
49
|
+
def self.logger(level, message)
|
50
|
+
Heartcheck.logger.send(level, "[Heartcheck] #{message}")
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'heartcheck/services/firewall'
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'net/telnet'
|
2
|
+
|
3
|
+
module Heartcheck
|
4
|
+
module Services
|
5
|
+
# A service to check with a simple telnet
|
6
|
+
# if the route to a host is working.
|
7
|
+
class Firewall
|
8
|
+
attr_reader :uri, :proxy
|
9
|
+
|
10
|
+
# Sets up the options to firewall.
|
11
|
+
#
|
12
|
+
# @param params [Hash] a hash with the configurations.
|
13
|
+
# @option params [String] :host The domain/Ip
|
14
|
+
# @option params [Integer] :port Number of port to check
|
15
|
+
# @option params [String] :proxy The uri of your proxy if is required
|
16
|
+
# @option params [integer] :timeout (defaults to: 2) Number in seconds
|
17
|
+
# @option params [string] :uri You can pass a URI instead a host and port
|
18
|
+
#
|
19
|
+
# @example
|
20
|
+
# Firewall.new(host: 'domain.com', port: 80)
|
21
|
+
# Firewall.new(host: 'domain.com', port: 80, timeout: 5)
|
22
|
+
# Firewall.new(uri: 'https://domain.com')
|
23
|
+
# Firewall.new(uri: 'https://domain.com', proxy: 'http://proxy.domain.com')
|
24
|
+
#
|
25
|
+
# @return [void]
|
26
|
+
def initialize(params)
|
27
|
+
@host = params[:host]
|
28
|
+
@port = params[:port]
|
29
|
+
@proxy = params[:proxy]
|
30
|
+
@timeout = params[:timeout] || 2
|
31
|
+
@uri = URI(params[:url].to_s)
|
32
|
+
end
|
33
|
+
|
34
|
+
# format params to use in Telnet
|
35
|
+
#
|
36
|
+
# @return [Hash] with the config
|
37
|
+
# - Host
|
38
|
+
# - Port
|
39
|
+
# - Timeout
|
40
|
+
# - Proxy - if is seted return a Net::Telnet object
|
41
|
+
def params
|
42
|
+
params = { 'Host' => host, 'Port' => port, 'Timeout' => @timeout }
|
43
|
+
params['Proxy'] = proxy_uri if proxy
|
44
|
+
params
|
45
|
+
end
|
46
|
+
|
47
|
+
# to get the host or stract from @uri
|
48
|
+
#
|
49
|
+
# @return [String] the host that is configured
|
50
|
+
def host
|
51
|
+
@host || uri.host
|
52
|
+
end
|
53
|
+
|
54
|
+
# to get the port or stract from @uri
|
55
|
+
#
|
56
|
+
# @return [Integre] the port that is configured
|
57
|
+
def port
|
58
|
+
@port || uri.port
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
# to get the configured proxy
|
64
|
+
#
|
65
|
+
# @return [Net::Telnet] an instance with configured proxy
|
66
|
+
def proxy_uri
|
67
|
+
uri_proxy = URI(proxy)
|
68
|
+
::Net::Telnet.new('Host' => uri_proxy.host, 'Port' => uri_proxy.port, 'Timeout' => @timeout)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
metadata
ADDED
@@ -0,0 +1,248 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: heartcheck
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Locaweb
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-02-10 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rack
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.6.0
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 1.6.0
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - "~>"
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 1.6.0
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 1.6.0
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: oj
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: 2.11.0
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: 2.11.4
|
43
|
+
type: :runtime
|
44
|
+
prerelease: false
|
45
|
+
version_requirements: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - "~>"
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: 2.11.0
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: 2.11.4
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
name: pry-nav
|
55
|
+
requirement: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - "~>"
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: 0.2.0
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: 0.2.4
|
63
|
+
type: :development
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - "~>"
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 0.2.0
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: 0.2.4
|
73
|
+
- !ruby/object:Gem::Dependency
|
74
|
+
name: rspec
|
75
|
+
requirement: !ruby/object:Gem::Requirement
|
76
|
+
requirements:
|
77
|
+
- - "~>"
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: 3.1.0
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 3.1.0
|
83
|
+
type: :development
|
84
|
+
prerelease: false
|
85
|
+
version_requirements: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 3.1.0
|
90
|
+
- - ">="
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: 3.1.0
|
93
|
+
- !ruby/object:Gem::Dependency
|
94
|
+
name: rubocop
|
95
|
+
requirement: !ruby/object:Gem::Requirement
|
96
|
+
requirements:
|
97
|
+
- - "~>"
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: 0.27.0
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: 0.27.1
|
103
|
+
type: :development
|
104
|
+
prerelease: false
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - "~>"
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: 0.27.0
|
110
|
+
- - ">="
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
version: 0.27.1
|
113
|
+
- !ruby/object:Gem::Dependency
|
114
|
+
name: thor
|
115
|
+
requirement: !ruby/object:Gem::Requirement
|
116
|
+
requirements:
|
117
|
+
- - "~>"
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
version: 0.19.0
|
120
|
+
- - ">="
|
121
|
+
- !ruby/object:Gem::Version
|
122
|
+
version: 0.19.1
|
123
|
+
type: :development
|
124
|
+
prerelease: false
|
125
|
+
version_requirements: !ruby/object:Gem::Requirement
|
126
|
+
requirements:
|
127
|
+
- - "~>"
|
128
|
+
- !ruby/object:Gem::Version
|
129
|
+
version: 0.19.0
|
130
|
+
- - ">="
|
131
|
+
- !ruby/object:Gem::Version
|
132
|
+
version: 0.19.1
|
133
|
+
- !ruby/object:Gem::Dependency
|
134
|
+
name: rack-test
|
135
|
+
requirement: !ruby/object:Gem::Requirement
|
136
|
+
requirements:
|
137
|
+
- - "~>"
|
138
|
+
- !ruby/object:Gem::Version
|
139
|
+
version: 0.6.0
|
140
|
+
- - ">="
|
141
|
+
- !ruby/object:Gem::Version
|
142
|
+
version: 0.6.3
|
143
|
+
type: :development
|
144
|
+
prerelease: false
|
145
|
+
version_requirements: !ruby/object:Gem::Requirement
|
146
|
+
requirements:
|
147
|
+
- - "~>"
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: 0.6.0
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: 0.6.3
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: yard
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - "~>"
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: 0.8.0
|
160
|
+
- - ">="
|
161
|
+
- !ruby/object:Gem::Version
|
162
|
+
version: 0.8.7
|
163
|
+
type: :development
|
164
|
+
prerelease: false
|
165
|
+
version_requirements: !ruby/object:Gem::Requirement
|
166
|
+
requirements:
|
167
|
+
- - "~>"
|
168
|
+
- !ruby/object:Gem::Version
|
169
|
+
version: 0.8.0
|
170
|
+
- - ">="
|
171
|
+
- !ruby/object:Gem::Version
|
172
|
+
version: 0.8.7
|
173
|
+
- !ruby/object:Gem::Dependency
|
174
|
+
name: redcarpet
|
175
|
+
requirement: !ruby/object:Gem::Requirement
|
176
|
+
requirements:
|
177
|
+
- - "~>"
|
178
|
+
- !ruby/object:Gem::Version
|
179
|
+
version: 3.2.0
|
180
|
+
- - ">="
|
181
|
+
- !ruby/object:Gem::Version
|
182
|
+
version: 3.2.2
|
183
|
+
type: :development
|
184
|
+
prerelease: false
|
185
|
+
version_requirements: !ruby/object:Gem::Requirement
|
186
|
+
requirements:
|
187
|
+
- - "~>"
|
188
|
+
- !ruby/object:Gem::Version
|
189
|
+
version: 3.2.0
|
190
|
+
- - ">="
|
191
|
+
- !ruby/object:Gem::Version
|
192
|
+
version: 3.2.2
|
193
|
+
description: A simple way to check your app heart.
|
194
|
+
email:
|
195
|
+
- desenvolvedores@locaweb.com.br
|
196
|
+
executables:
|
197
|
+
- heartcheck
|
198
|
+
extensions: []
|
199
|
+
extra_rdoc_files: []
|
200
|
+
files:
|
201
|
+
- bin/heartcheck
|
202
|
+
- lib/generators/generator.rb
|
203
|
+
- lib/generators/templates/config.rb
|
204
|
+
- lib/heartcheck.rb
|
205
|
+
- lib/heartcheck/app.rb
|
206
|
+
- lib/heartcheck/checks.rb
|
207
|
+
- lib/heartcheck/checks/base.rb
|
208
|
+
- lib/heartcheck/checks/firewall.rb
|
209
|
+
- lib/heartcheck/checks/process.rb
|
210
|
+
- lib/heartcheck/controllers/base.rb
|
211
|
+
- lib/heartcheck/controllers/dev.rb
|
212
|
+
- lib/heartcheck/controllers/essential.rb
|
213
|
+
- lib/heartcheck/controllers/functional.rb
|
214
|
+
- lib/heartcheck/controllers/health_check.rb
|
215
|
+
- lib/heartcheck/controllers/info.rb
|
216
|
+
- lib/heartcheck/errors.rb
|
217
|
+
- lib/heartcheck/errors/routing_error.rb
|
218
|
+
- lib/heartcheck/errors/warning.rb
|
219
|
+
- lib/heartcheck/logger.rb
|
220
|
+
- lib/heartcheck/services.rb
|
221
|
+
- lib/heartcheck/services/firewall.rb
|
222
|
+
- lib/heartcheck/version.rb
|
223
|
+
homepage: http://developer.locaweb.com.br
|
224
|
+
licenses:
|
225
|
+
- MIT
|
226
|
+
metadata: {}
|
227
|
+
post_install_message:
|
228
|
+
rdoc_options: []
|
229
|
+
require_paths:
|
230
|
+
- lib
|
231
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
232
|
+
requirements:
|
233
|
+
- - ">="
|
234
|
+
- !ruby/object:Gem::Version
|
235
|
+
version: '0'
|
236
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
237
|
+
requirements:
|
238
|
+
- - ">="
|
239
|
+
- !ruby/object:Gem::Version
|
240
|
+
version: '0'
|
241
|
+
requirements: []
|
242
|
+
rubyforge_project:
|
243
|
+
rubygems_version: 2.4.5
|
244
|
+
signing_key:
|
245
|
+
specification_version: 4
|
246
|
+
summary: A simple way to check if your app is runnig like as expected.
|
247
|
+
test_files: []
|
248
|
+
has_rdoc:
|