Class: Pinglish
- Inherits:
-
Object
- Object
- Pinglish
- Defined in:
- lib/pinglish.rb,
lib/pinglish/check.rb
Overview
This Rack app provides an endpoint for configurable system health checks. It’s intended to be consumed by machines.
Defined Under Namespace
Constant Summary collapse
- HEADERS =
The HTTP headers sent for every response.
{ "Content-Type" => "application/json; charset=UTF-8" }
Instance Method Summary collapse
- #call(env) ⇒ Object
-
#check(name = :default, options = nil, &block) ⇒ Object
Add a new check with optional ‘name`.
-
#failure?(value) ⇒ Boolean
Does ‘value` represent a check failure? This default implementation returns `true` for any value that is an Exception or false.
-
#initialize(options = nil) {|_self| ... } ⇒ Pinglish
constructor
Create a new instance of the app, with optional parameter ‘:max` timeout in seconds (default: `29`); yields itself to an optional block for configuring checks.
- #selected_checks(params) ⇒ Object
-
#timeout(seconds, &block) ⇒ Object
Raise Pinglish::TooLong after ‘seconds` has elapsed.
-
#timeout?(value) ⇒ Boolean
Does ‘value` represent a check timeout? Returns `true` for any value that is an instance of Pinglish::TooLong.
Constructor Details
#initialize(options = nil) {|_self| ... } ⇒ Pinglish
Create a new instance of the app, with optional parameter ‘:max` timeout in seconds (default: `29`); yields itself to an optional block for configuring checks.
20 21 22 23 24 25 26 27 |
# File 'lib/pinglish.rb', line 20 def initialize(=nil, &block) ||= {} @checks = {} @max = [:max] || 29 # seconds yield self if block_given? end |
Instance Method Details
#call(env) ⇒ Object
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
# File 'lib/pinglish.rb', line 29 def call(env) request = Rack::Request.new(env) begin timeout @max do results = {} selected_checks(request.params).each do |check| begin timeout(check.timeout) do results[check.name] = check.call end rescue => e results[check.name] = e end end failed = results.values.any? { |v| failure? v } http_status = failed ? 503 : 200 text_status = failed ? "failures" : "ok" data = { :now => Time.now.to_i.to_s, :status => text_status } results.each do |name, value| if failure?(value) # If a check fails its name is added to a `failures` array. # If the check failed because it timed out, its name is # added to a `timeouts` array instead. key = timeout?(value) ? :timeouts : :failures (data[key] ||= []) << name if key == :failures and value.is_a?(Exception) data[name] = { state: :error, exception: value.class.name, message: value., } end elsif value # If the check passed and returned a value, the stringified # version of the value is returned under the `name` key. data[name] = value end end [http_status, HEADERS, [JSON.generate(data)]] end rescue Exception => ex # Something catastrophic happened. We can't even run the checks # and render a JSON response. Fall back on a pre-rendered string # and interpolate the current epoch time. now = Time.now.to_i.to_s body = " {\n \"status\": \"failures\",\n \"now\": \"\#{now}\",\n \"error\": {\n \"class\": \"\#{ex.class.name}\",\n \"message\": \"\#{ex.message.tr('\"', '')}\"\n }\n }\n EOF\n\n [500, HEADERS, [body]]\n end\nend\n".gsub(/^ {6}/, '') |
#check(name = :default, options = nil, &block) ⇒ Object
Add a new check with optional ‘name`. A `:timeout` option can be specified in seconds for checks that might take longer than the one second default. A previously added check with the same name will be replaced.
118 119 120 |
# File 'lib/pinglish.rb', line 118 def check(name = :default, = nil, &block) @checks[name.to_sym] = Check.new(name, , &block) end |
#failure?(value) ⇒ Boolean
Does ‘value` represent a check failure? This default implementation returns `true` for any value that is an Exception or false. Subclasses can override this method for different behavior.
126 127 128 |
# File 'lib/pinglish.rb', line 126 def failure?(value) value.is_a?(Exception) || value == false end |
#selected_checks(params) ⇒ Object
105 106 107 108 109 110 111 |
# File 'lib/pinglish.rb', line 105 def selected_checks(params) if (selected = params['checks']) selected = selected.split(',').map(&:to_sym) return @checks.values_at(*selected).compact end @checks.values end |
#timeout(seconds, &block) ⇒ Object
Raise Pinglish::TooLong after ‘seconds` has elapsed. This default implementation uses Ruby’s built-in Timeout class. Subclasses can override this method for different behavior, but any new implementation must raise Pinglish::TooLong when the timeout is exceeded or override ‘timeout?` appropriately.
136 137 138 |
# File 'lib/pinglish.rb', line 136 def timeout(seconds, &block) Timeout.timeout seconds, Pinglish::TooLong, &block end |
#timeout?(value) ⇒ Boolean
Does ‘value` represent a check timeout? Returns `true` for any value that is an instance of Pinglish::TooLong.
143 144 145 |
# File 'lib/pinglish.rb', line 143 def timeout?(value) value.is_a? Pinglish::TooLong end |