rails_health_checks 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +112 -0
- data/Rakefile +14 -0
- data/app/controllers/rails_health_checks/application_controller.rb +6 -0
- data/app/controllers/rails_health_checks/health_controller.rb +27 -0
- data/app/jobs/rails_health_checks/application_job.rb +6 -0
- data/app/mailers/rails_health_checks/application_mailer.rb +8 -0
- data/app/models/rails_health_checks/application_record.rb +7 -0
- data/config/routes.rb +6 -0
- data/lib/rails_health_checks/check.rb +34 -0
- data/lib/rails_health_checks/check_registry.rb +41 -0
- data/lib/rails_health_checks/checks/database_check.rb +14 -0
- data/lib/rails_health_checks/configuration.rb +12 -0
- data/lib/rails_health_checks/engine.rb +8 -0
- data/lib/rails_health_checks/response_builder.rb +35 -0
- data/lib/rails_health_checks/version.rb +5 -0
- data/lib/rails_health_checks.rb +21 -0
- data/lib/tasks/rails_health_checks_tasks.rake +6 -0
- metadata +77 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 0a376b4d6a5d680684058c1c123903ebc889e48ffa38cfd937cec29d53ebdaf6
|
|
4
|
+
data.tar.gz: c5aabce9da9a24785ae363f97cf0781f6a26184a53bfaa6d629fcea671c80189
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 35b074807441355e162b1c9bdccf1303ed3ce9c8b125fd6043676261e297c362cce28d3fb651555476c2a14f84ab282a18f1e59bf6b2e10a2e4c7268cab13603
|
|
7
|
+
data.tar.gz: e5c9a043d6627f7bbfc8015d6104b679c1f80b99ec23952b353024c037e2c50954af36f1926e404005ab83a8637c360b92dd66e9aaa3722d852b865cd7111beb
|
data/MIT-LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright TODO: Write your name
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# RailsHealthChecks
|
|
2
|
+
|
|
3
|
+
[](https://github.com/eclectic-coding/rails_health_checks/actions/workflows/ci.yml)
|
|
4
|
+
[](https://badge.fury.io/rb/rails_health_checks)
|
|
5
|
+
[](https://rubygems.org/gems/rails_health_checks)
|
|
6
|
+
[](https://www.ruby-lang.org)
|
|
7
|
+
[](https://codecov.io/gh/eclectic-coding/rails_health_checks)
|
|
8
|
+
[](https://opensource.org/licenses/MIT)
|
|
9
|
+
|
|
10
|
+
A Rails engine providing structured, pluggable health check endpoints for monitoring application status. Goes beyond Rails' built-in `/up` endpoint with per-check diagnostics, latency tracking, and a configurable check registry.
|
|
11
|
+
|
|
12
|
+
## Table of Contents
|
|
13
|
+
|
|
14
|
+
- [Installation](#installation)
|
|
15
|
+
- [Endpoints](#endpoints)
|
|
16
|
+
- [Configuration](#configuration)
|
|
17
|
+
- [Built-in Checks](#built-in-checks)
|
|
18
|
+
- [Contributing](#contributing)
|
|
19
|
+
- [License](#license)
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
Add to your Gemfile:
|
|
26
|
+
|
|
27
|
+
```ruby
|
|
28
|
+
gem "rails_health_checks"
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Then run:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
bundle install
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Mount the engine in `config/routes.rb`:
|
|
38
|
+
|
|
39
|
+
```ruby
|
|
40
|
+
mount RailsHealthChecks::Engine => "/health"
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
[↑ Back to top](#table-of-contents)
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Endpoints
|
|
48
|
+
|
|
49
|
+
| Endpoint | Format | Use case |
|
|
50
|
+
|----------|--------|----------|
|
|
51
|
+
| `GET /health` | JSON | Monitoring dashboards, detailed diagnostics |
|
|
52
|
+
| `GET /health/live` | Plain text | Load balancer liveness probes |
|
|
53
|
+
|
|
54
|
+
HTTP status is `200 OK` when all checks pass, `503 Service Unavailable` otherwise.
|
|
55
|
+
|
|
56
|
+
### JSON response shape
|
|
57
|
+
|
|
58
|
+
```json
|
|
59
|
+
{
|
|
60
|
+
"status": "ok",
|
|
61
|
+
"timestamp": "2026-06-08T20:00:00Z",
|
|
62
|
+
"checks": {
|
|
63
|
+
"database": { "status": "ok", "latency_ms": 4 }
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Status values: `ok` | `degraded` | `critical`. Overall status is `critical` if any check is `critical`, `degraded` if any is `degraded`.
|
|
69
|
+
|
|
70
|
+
[↑ Back to top](#table-of-contents)
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## Configuration
|
|
75
|
+
|
|
76
|
+
```ruby
|
|
77
|
+
# config/initializers/rails_health_checks.rb
|
|
78
|
+
RailsHealthChecks.configure do |config|
|
|
79
|
+
config.checks = [:database] # checks to run (default: [:database])
|
|
80
|
+
config.timeout = 5 # global timeout per check in seconds (default: 5)
|
|
81
|
+
end
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
[↑ Back to top](#table-of-contents)
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## Built-in Checks
|
|
89
|
+
|
|
90
|
+
| Check | Description |
|
|
91
|
+
|-------|-------------|
|
|
92
|
+
| `:database` | ActiveRecord `SELECT 1` against the primary connection, includes latency |
|
|
93
|
+
|
|
94
|
+
[↑ Back to top](#table-of-contents)
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Contributing
|
|
99
|
+
|
|
100
|
+
1. Fork the repository
|
|
101
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
|
102
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
|
103
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
|
104
|
+
5. Create a new Pull Request
|
|
105
|
+
|
|
106
|
+
[↑ Back to top](#table-of-contents)
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## License
|
|
111
|
+
|
|
112
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'bundler/setup'
|
|
4
|
+
|
|
5
|
+
require 'bundler/gem_tasks'
|
|
6
|
+
require 'rubocop/rake_task'
|
|
7
|
+
require 'bundler/audit/task'
|
|
8
|
+
require 'rspec/core/rake_task'
|
|
9
|
+
|
|
10
|
+
RuboCop::RakeTask.new(:lint)
|
|
11
|
+
Bundler::Audit::Task.new
|
|
12
|
+
RSpec::Core::RakeTask.new(:spec)
|
|
13
|
+
|
|
14
|
+
task default: [:lint, :'bundle:audit:update', 'bundle:audit:check', :spec]
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RailsHealthChecks
|
|
4
|
+
class HealthController < ApplicationController
|
|
5
|
+
def show
|
|
6
|
+
builder = ResponseBuilder.new(run_checks)
|
|
7
|
+
render json: builder.to_json, status: builder.http_status
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def live
|
|
11
|
+
builder = ResponseBuilder.new(run_checks)
|
|
12
|
+
if builder.overall_status == "ok"
|
|
13
|
+
render plain: "OK", status: :ok
|
|
14
|
+
else
|
|
15
|
+
render plain: "Service Unavailable", status: :service_unavailable
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def run_checks
|
|
22
|
+
config = RailsHealthChecks.configuration
|
|
23
|
+
checks = CheckRegistry.build(config.checks)
|
|
24
|
+
CheckRegistry.run(checks, timeout: config.timeout)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
data/config/routes.rb
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RailsHealthChecks
|
|
4
|
+
class Check
|
|
5
|
+
attr_reader :status, :message, :latency_ms
|
|
6
|
+
|
|
7
|
+
def call
|
|
8
|
+
raise NotImplementedError, "#{self.class} must implement #call"
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
private
|
|
12
|
+
|
|
13
|
+
def pass(message = nil)
|
|
14
|
+
@status = "ok"
|
|
15
|
+
@message = message
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def warn_with(message)
|
|
19
|
+
@status = "degraded"
|
|
20
|
+
@message = message
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def fail_with(message)
|
|
24
|
+
@status = "critical"
|
|
25
|
+
@message = message
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def measure
|
|
29
|
+
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
30
|
+
yield
|
|
31
|
+
@latency_ms = ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - start) * 1000).round
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "timeout"
|
|
4
|
+
|
|
5
|
+
module RailsHealthChecks
|
|
6
|
+
class CheckRegistry
|
|
7
|
+
BUILT_INS = {
|
|
8
|
+
database: -> { Checks::DatabaseCheck.new }
|
|
9
|
+
}.freeze
|
|
10
|
+
|
|
11
|
+
def self.build(check_names)
|
|
12
|
+
check_names.each_with_object({}) do |name, hash|
|
|
13
|
+
factory = BUILT_INS.fetch(name) do
|
|
14
|
+
raise ArgumentError, "Unknown check: #{name}. Available: #{BUILT_INS.keys.join(', ')}"
|
|
15
|
+
end
|
|
16
|
+
hash[name] = factory.call
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.run(checks, timeout:)
|
|
21
|
+
checks.transform_values { |check| run_check(check, timeout: timeout) }
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.run_check(check, timeout:)
|
|
25
|
+
Timeout.timeout(timeout) { check.call }
|
|
26
|
+
check
|
|
27
|
+
rescue Timeout::Error
|
|
28
|
+
mark_critical(check, "timed out")
|
|
29
|
+
rescue StandardError => e
|
|
30
|
+
mark_critical(check, e.message)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def self.mark_critical(check, message)
|
|
34
|
+
check.instance_variable_set(:@status, "critical")
|
|
35
|
+
check.instance_variable_set(:@message, message)
|
|
36
|
+
check
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private_class_method :run_check, :mark_critical
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RailsHealthChecks
|
|
4
|
+
module Checks
|
|
5
|
+
class DatabaseCheck < Check
|
|
6
|
+
def call
|
|
7
|
+
measure { ActiveRecord::Base.connection.execute("SELECT 1") }
|
|
8
|
+
pass
|
|
9
|
+
rescue StandardError => e
|
|
10
|
+
fail_with(e.message)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RailsHealthChecks
|
|
4
|
+
class ResponseBuilder
|
|
5
|
+
def initialize(results)
|
|
6
|
+
@results = results
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def overall_status
|
|
10
|
+
statuses = @results.values.map(&:status)
|
|
11
|
+
if statuses.include?("critical")
|
|
12
|
+
"critical"
|
|
13
|
+
elsif statuses.include?("degraded")
|
|
14
|
+
"degraded"
|
|
15
|
+
else
|
|
16
|
+
"ok"
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def to_json(*)
|
|
21
|
+
checks_hash = @results.transform_values do |check|
|
|
22
|
+
result = { status: check.status }
|
|
23
|
+
result[:latency_ms] = check.latency_ms if check.latency_ms
|
|
24
|
+
result[:message] = check.message if check.message
|
|
25
|
+
result
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
{ status: overall_status, timestamp: Time.now.utc.iso8601, checks: checks_hash }.to_json
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def http_status
|
|
32
|
+
overall_status == "ok" ? :ok : :service_unavailable
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails_health_checks/version"
|
|
4
|
+
require "rails_health_checks/engine"
|
|
5
|
+
require "rails_health_checks/configuration"
|
|
6
|
+
require "rails_health_checks/check"
|
|
7
|
+
require "rails_health_checks/checks/database_check"
|
|
8
|
+
require "rails_health_checks/check_registry"
|
|
9
|
+
require "rails_health_checks/response_builder"
|
|
10
|
+
|
|
11
|
+
module RailsHealthChecks
|
|
12
|
+
class << self
|
|
13
|
+
def configure
|
|
14
|
+
yield(configuration)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def configuration
|
|
18
|
+
@configuration ||= Configuration.new
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: rails_health_checks
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Chuck Smith
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: rails
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: 8.1.3
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: 8.1.3
|
|
26
|
+
description: A Rails engine providing configurable health check endpoints for monitoring
|
|
27
|
+
application status.
|
|
28
|
+
email:
|
|
29
|
+
- chuck@eclecticcoding.com
|
|
30
|
+
executables: []
|
|
31
|
+
extensions: []
|
|
32
|
+
extra_rdoc_files: []
|
|
33
|
+
files:
|
|
34
|
+
- MIT-LICENSE
|
|
35
|
+
- README.md
|
|
36
|
+
- Rakefile
|
|
37
|
+
- app/controllers/rails_health_checks/application_controller.rb
|
|
38
|
+
- app/controllers/rails_health_checks/health_controller.rb
|
|
39
|
+
- app/jobs/rails_health_checks/application_job.rb
|
|
40
|
+
- app/mailers/rails_health_checks/application_mailer.rb
|
|
41
|
+
- app/models/rails_health_checks/application_record.rb
|
|
42
|
+
- config/routes.rb
|
|
43
|
+
- lib/rails_health_checks.rb
|
|
44
|
+
- lib/rails_health_checks/check.rb
|
|
45
|
+
- lib/rails_health_checks/check_registry.rb
|
|
46
|
+
- lib/rails_health_checks/checks/database_check.rb
|
|
47
|
+
- lib/rails_health_checks/configuration.rb
|
|
48
|
+
- lib/rails_health_checks/engine.rb
|
|
49
|
+
- lib/rails_health_checks/response_builder.rb
|
|
50
|
+
- lib/rails_health_checks/version.rb
|
|
51
|
+
- lib/tasks/rails_health_checks_tasks.rake
|
|
52
|
+
homepage: https://github.com/eclectic-coding/rails_health_checks
|
|
53
|
+
licenses:
|
|
54
|
+
- MIT
|
|
55
|
+
metadata:
|
|
56
|
+
homepage_uri: https://github.com/eclectic-coding/rails_health_checks
|
|
57
|
+
source_code_uri: https://github.com/eclectic-coding/rails_health_checks
|
|
58
|
+
changelog_uri: https://github.com/eclectic-coding/rails_health_checks/blob/main/CHANGELOG.md
|
|
59
|
+
rubygems_mfa_required: 'true'
|
|
60
|
+
rdoc_options: []
|
|
61
|
+
require_paths:
|
|
62
|
+
- lib
|
|
63
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - ">="
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '3.3'
|
|
68
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
69
|
+
requirements:
|
|
70
|
+
- - ">="
|
|
71
|
+
- !ruby/object:Gem::Version
|
|
72
|
+
version: '0'
|
|
73
|
+
requirements: []
|
|
74
|
+
rubygems_version: 3.6.9
|
|
75
|
+
specification_version: 4
|
|
76
|
+
summary: Health check endpoints for Rails applications.
|
|
77
|
+
test_files: []
|