rails_health_checks 0.7.0 → 1.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cf33a3b43064f931e789b7d28fff6145c206229b14da15d4da208717007f2c44
4
- data.tar.gz: 85c221c95529597327d5a830c9dc6f46da3720133cc19b2366be7af3256f0f57
3
+ metadata.gz: f105b603379473145f5d8340416290d38b09ee3f1aa1d1511b9bf7e7245286ea
4
+ data.tar.gz: 623dada0a331945d0923d0338858ec7b2e3b224378981660f5065965fd96a916
5
5
  SHA512:
6
- metadata.gz: 5f460a617a378811cceda76d3993f589424af6b8bca336a00f2e887cdcc540f1bf29a6a0f049dd7a1a19c8944efff566f92e567a70380651983ed95e106abaa3
7
- data.tar.gz: ec5f9be2fc4ad1d84582006455cb742c010232b4e65c0f99f8d4782429d48bda9973e0f6f9e9074bc1d799a03b5e856daf650adc8c0b2dda4637242a7844ede9
6
+ metadata.gz: ea3dc72e7ab0f7f76aa0b6223643e26401d46421302f41b347721d56814d139a03e65c8a84b757ef0445ad6db1d49c3c331db1635dbc76c5454625890282cd8d
7
+ data.tar.gz: dc8fb5b87af3e8b06b7b721d3ece226614d48dc91c023860e066289f8be43ab9bde8f359ed5999511bc609ed9effe3fa2fe9d44d780e52dd657c238fa60587ea
data/README.md CHANGED
@@ -13,13 +13,19 @@ A Rails engine providing structured, pluggable health check endpoints for monito
13
13
  - [Installation](#installation)
14
14
  - [Endpoints](#endpoints)
15
15
  - [Configuration](#configuration)
16
+ - [Configuration Reference](#configuration-reference)
16
17
  - [Authentication](#authentication)
17
18
  - [Built-in Checks](#built-in-checks)
18
19
  - [Notifications](#notifications)
19
20
  - [Prometheus Metrics](#prometheus-metrics)
21
+ - [Result Caching](#result-caching)
20
22
  - [Per-Environment Toggling](#per-environment-toggling)
21
23
  - [Check Groups](#check-groups)
22
24
  - [Custom Checks](#custom-checks)
25
+ - [Check API](#check-api)
26
+ - [Testing Custom Checks](#testing-custom-checks)
27
+ - [Migrating from OkComputer](#migrating-from-okcomputer)
28
+ - [Performance](#performance)
23
29
  - [Contributing](#contributing)
24
30
  - [License](#license)
25
31
 
@@ -56,6 +62,7 @@ mount RailsHealthChecks::Engine => "/health"
56
62
  | `GET /health` | JSON | Monitoring dashboards, detailed diagnostics |
57
63
  | `GET /health/live` | Plain text | Load balancer liveness probes |
58
64
  | `GET /health/metrics` | Prometheus text | Prometheus / OpenMetrics scraping |
65
+ | `GET /health/:group` | JSON | Scoped check group (e.g. `/health/workers`) |
59
66
 
60
67
  `/health` and `/health/live` also respond to `HEAD` requests (useful for lightweight load balancer probes).
61
68
 
@@ -68,12 +75,13 @@ HTTP status is `200 OK` when all checks pass, `503 Service Unavailable` otherwis
68
75
  "status": "ok",
69
76
  "timestamp": "2026-06-08T20:00:00Z",
70
77
  "checks": {
71
- "database": { "status": "ok", "latency_ms": 4 }
78
+ "database": { "status": "ok", "latency_ms": 4 },
79
+ "cache": { "status": "ok", "latency_ms": 1 }
72
80
  }
73
81
  }
74
82
  ```
75
83
 
76
- Status values: `ok` | `degraded` | `critical`. Overall status is `critical` if any check is `critical`, `degraded` if any is `degraded`.
84
+ Status values: `ok` | `degraded` | `critical`. Overall status is `critical` if any check is `critical`, `degraded` if any is `degraded`, `ok` otherwise.
77
85
 
78
86
  [↑ Back to top](#table-of-contents)
79
87
 
@@ -81,16 +89,145 @@ Status values: `ok` | `degraded` | `critical`. Overall status is `critical` if a
81
89
 
82
90
  ## Configuration
83
91
 
92
+ Run the initializer generator to create `config/initializers/rails_health_checks.rb` with every option documented as a commented example:
93
+
94
+ ```bash
95
+ rails generate rails_health_checks:initializer
96
+ ```
97
+
98
+ The generated file (shown below with all options) uses the block-style `configure` API. Every setting has a sensible default — uncomment only what you need:
99
+
84
100
  ```ruby
85
- # config/initializers/rails_health_checks.rb
101
+ # frozen_string_literal: true
102
+
86
103
  RailsHealthChecks.configure do |config|
87
- config.checks = [:database, :cache] # checks to run (default: [:database])
88
- config.timeout = 5 # global timeout per check in seconds (default: 5)
89
- config.cache_duration = 10 # cache results for N seconds (default: nil, disabled)
104
+ # Checks to run (default: [:database])
105
+ # Available built-ins: :database, :cache, :redis, :smtp, :sidekiq, :solid_queue,
106
+ # :good_job, :resque, :disk, :memory, :http
107
+ config.checks = [:database]
108
+
109
+ # Global timeout per check in seconds (default: 5)
110
+ config.timeout = 5
111
+
112
+ # Cache check results for N seconds to avoid re-running on every request (default: nil, disabled)
113
+ # config.cache_duration = 10
114
+
115
+ # ---------------------------------------------------------------------------
116
+ # Authentication — all strategies are mutually exclusive; default is public
117
+ # ---------------------------------------------------------------------------
118
+
119
+ # Bearer token: requests must include Authorization: Bearer <token>
120
+ # config.token = ENV["HEALTH_TOKEN"]
121
+
122
+ # IP allowlist: exact IPs or CIDR ranges
123
+ # config.allowed_ips = ["127.0.0.1", "10.0.0.0/8"]
124
+
125
+ # Custom block: return truthy to allow the request
126
+ # config.authenticate { |request| request.headers["X-Internal"] == "true" }
127
+
128
+ # ---------------------------------------------------------------------------
129
+ # Per-environment toggling
130
+ # ---------------------------------------------------------------------------
131
+ # config.disable :disk, in: :test
132
+ # config.disable :memory, in: [:test, :development]
133
+
134
+ # ---------------------------------------------------------------------------
135
+ # Check groups — expose subsets at GET /health/:group
136
+ # ---------------------------------------------------------------------------
137
+ # config.group :system, [:disk, :memory]
138
+ # config.group :workers, [:sidekiq, :good_job]
139
+
140
+ # ---------------------------------------------------------------------------
141
+ # Redis check (requires :redis in config.checks and the redis gem)
142
+ # ---------------------------------------------------------------------------
143
+ # config.redis_url = ENV["REDIS_URL"] # default: redis://localhost:6379/0
144
+
145
+ # ---------------------------------------------------------------------------
146
+ # SMTP check (requires :smtp in config.checks)
147
+ # Reads ActionMailer::Base.smtp_settings automatically if not set here.
148
+ # ---------------------------------------------------------------------------
149
+ # config.smtp_address = "smtp.example.com" # default: ActionMailer config or localhost
150
+ # config.smtp_port = 587 # default: ActionMailer config or 25
151
+
152
+ # ---------------------------------------------------------------------------
153
+ # Disk check (requires :disk in config.checks)
154
+ # ---------------------------------------------------------------------------
155
+ # config.disk_path = "/" # mount point (default: "/")
156
+ # config.disk_warn_threshold = 2 * 1024**3 # bytes free → degraded
157
+ # config.disk_critical_threshold = 512 * 1024**2 # bytes free → critical
158
+
159
+ # ---------------------------------------------------------------------------
160
+ # Memory check (requires :memory in config.checks)
161
+ # ---------------------------------------------------------------------------
162
+ # config.memory_threshold = 512 * 1024**2 # RSS bytes → degraded
163
+
164
+ # ---------------------------------------------------------------------------
165
+ # HTTP check (requires :http in config.checks)
166
+ # ---------------------------------------------------------------------------
167
+ # config.http_url = "https://api.example.com/status"
168
+ # config.http_expected_status = 200 # expected response code (default: 200)
169
+ # config.http_headers = { "Authorization" => "Bearer #{ENV['API_TOKEN']}" }
170
+
171
+ # ---------------------------------------------------------------------------
172
+ # Sidekiq check (requires :sidekiq in config.checks)
173
+ # ---------------------------------------------------------------------------
174
+ # config.sidekiq_queue_size = 1000 # total depth → degraded
175
+
176
+ # ---------------------------------------------------------------------------
177
+ # Solid Queue check (requires :solid_queue in config.checks)
178
+ # ---------------------------------------------------------------------------
179
+ # config.solid_queue_job_count = 500 # pending jobs → degraded
180
+
181
+ # ---------------------------------------------------------------------------
182
+ # GoodJob check (requires :good_job in config.checks)
183
+ # ---------------------------------------------------------------------------
184
+ # config.good_job_latency = 300 # seconds oldest job waiting → degraded
185
+
186
+ # ---------------------------------------------------------------------------
187
+ # Resque check (requires :resque in config.checks)
188
+ # ---------------------------------------------------------------------------
189
+ # config.resque_queue_size = 1000 # total depth → degraded
190
+
191
+ # ---------------------------------------------------------------------------
192
+ # Custom checks
193
+ # ---------------------------------------------------------------------------
194
+ # class MyApiCheck < RailsHealthChecks::Check
195
+ # def call
196
+ # res = Net::HTTP.get_response(URI("https://api.example.com/status"))
197
+ # res.code == "200" ? pass : fail_with("API returned #{res.code}")
198
+ # end
199
+ # end
200
+ #
201
+ # config.register :my_api, MyApiCheck.new
202
+ # config.register :slow_api, MyApiCheck.new, timeout: 10 # per-check timeout override
90
203
  end
91
204
  ```
92
205
 
93
- Configuration is validated at boot time. An unknown check name or a missing `http_url` for the `:http` check raises `RailsHealthChecks::ConfigurationError` on startup rather than silently failing on the first request.
206
+ Configuration is validated at boot time. An unknown check name, a missing `http_url` for the `:http` check, or a group referencing an undefined check raises `RailsHealthChecks::ConfigurationError` on startup rather than silently failing on the first request.
207
+
208
+ ### Configuration Reference
209
+
210
+ | Option | Type | Default | Description |
211
+ |--------|------|---------|-------------|
212
+ | `checks` | `Array` | `[:database]` | Built-in or custom check names to run |
213
+ | `timeout` | `Integer` | `5` | Global per-check timeout in seconds |
214
+ | `cache_duration` | `Integer\|nil` | `nil` | Cache results for N seconds; `nil` disables caching |
215
+ | `token` | `String\|nil` | `nil` | Bearer token for authentication |
216
+ | `allowed_ips` | `Array\|nil` | `nil` | IP allowlist; accepts exact IPs and CIDR ranges |
217
+ | `redis_url` | `String\|nil` | `nil` | Redis URL for `:redis` check; falls back to `REDIS_URL` env var then `redis://localhost:6379/0` |
218
+ | `smtp_address` | `String\|nil` | `nil` | SMTP host for `:smtp` check; falls back to `ActionMailer` config then `localhost` |
219
+ | `smtp_port` | `Integer\|nil` | `nil` | SMTP port for `:smtp` check; falls back to `ActionMailer` config then `25` |
220
+ | `sidekiq_queue_size` | `Integer\|nil` | `nil` | Total Sidekiq queue depth that triggers `degraded` |
221
+ | `solid_queue_job_count` | `Integer\|nil` | `nil` | Pending SolidQueue jobs that trigger `degraded` |
222
+ | `good_job_latency` | `Integer\|nil` | `nil` | Oldest pending GoodJob age (seconds) that triggers `degraded` |
223
+ | `resque_queue_size` | `Integer\|nil` | `nil` | Total Resque queue depth that triggers `degraded` |
224
+ | `disk_path` | `String` | `"/"` | Mount point for `:disk` check |
225
+ | `disk_warn_threshold` | `Integer\|nil` | `nil` | Free bytes below which `:disk` reports `degraded` |
226
+ | `disk_critical_threshold` | `Integer\|nil` | `nil` | Free bytes below which `:disk` reports `critical` |
227
+ | `memory_threshold` | `Integer\|nil` | `nil` | Process RSS bytes above which `:memory` reports `degraded` |
228
+ | `http_url` | `String\|nil` | `nil` | Target URL for `:http` check (**required** when `:http` is active) |
229
+ | `http_expected_status` | `Integer` | `200` | Expected HTTP response code for `:http` check |
230
+ | `http_headers` | `Hash` | `{}` | Request headers sent by `:http` check |
94
231
 
95
232
  [↑ Back to top](#table-of-contents)
96
233
 
@@ -134,17 +271,21 @@ The block receives the `ActionDispatch::Request` object and must return a truthy
134
271
 
135
272
  ## Built-in Checks
136
273
 
137
- | Check | Description |
138
- |-------|-------------|
139
- | `:database` | ActiveRecord `SELECT 1` against the primary connection, includes latency |
140
- | `:cache` | `Rails.cache` read/write probe; works with Redis, Memcached, or in-process store |
141
- | `:sidekiq` | Sidekiq Redis connectivity; optional `config.sidekiq_queue_size` threshold for queue depth |
142
- | `:solid_queue` | Solid Queue DB connectivity; optional `config.solid_queue_job_count` threshold for pending jobs |
143
- | `:good_job` | GoodJob queue latency; optional `config.good_job_latency` (seconds) threshold for oldest pending job |
144
- | `:resque` | Resque Redis connectivity; optional `config.resque_queue_size` threshold for total queue depth |
145
- | `:disk` | Free disk bytes via `df`; optional `config.disk_warn_threshold` / `config.disk_critical_threshold` (bytes) and `config.disk_path` (default: `/`) |
146
- | `:memory` | Process RSS via `ps`; optional `config.memory_threshold` (bytes) reports `degraded` when exceeded |
147
- | `:http` | HTTP GET to `config.http_url`; reports `critical` if response code differs from `config.http_expected_status` (default: `200`) or a network error occurs; optional `config.http_headers` hash sends custom request headers (e.g. `{ "Authorization" => "Bearer ..." }`) |
274
+ | Check | Requires | Description |
275
+ |-------|----------|-------------|
276
+ | `:database` | — | ActiveRecord `SELECT 1` against the primary connection |
277
+ | `:cache` | — | `Rails.cache` read/write probe; works with any cache store |
278
+ | `:redis` | `redis` gem | Direct Redis `PING`; `config.redis_url` or `REDIS_URL` env var |
279
+ | `:smtp` | | SMTP connectivity via `Net::SMTP`; reads `ActionMailer` config automatically |
280
+ | `:sidekiq` | `sidekiq` gem | Sidekiq Redis connectivity; optional `config.sidekiq_queue_size` depth threshold |
281
+ | `:solid_queue` | `solid_queue` gem | SolidQueue DB connectivity; optional `config.solid_queue_job_count` threshold |
282
+ | `:good_job` | `good_job` gem | GoodJob queue latency; optional `config.good_job_latency` (seconds) threshold |
283
+ | `:resque` | `resque` gem | Resque Redis connectivity; optional `config.resque_queue_size` depth threshold |
284
+ | `:disk` | | Free disk space via `df`; `config.disk_warn_threshold` / `config.disk_critical_threshold` (bytes) |
285
+ | `:memory` | — | Process RSS via `ps`; optional `config.memory_threshold` (bytes) reports `degraded` when exceeded |
286
+ | `:http` | — | HTTP GET to `config.http_url`; `config.http_expected_status` and `config.http_headers` |
287
+
288
+ All checks run in parallel. Each check times out independently using `config.timeout` (default: 5s) or a per-check override set via `config.register`.
148
289
 
149
290
  [↑ Back to top](#table-of-contents)
150
291
 
@@ -162,7 +303,14 @@ ActiveSupport::Notifications.subscribe("health_check.rails_health_checks") do |*
162
303
  end
163
304
  ```
164
305
 
165
- The payload includes `status` (overall: `ok`/`degraded`/`critical`) and `checks` (per-check hash with `status`, `latency_ms`, and `message` when present). Duration is measured over the entire parallel check run.
306
+ The payload includes:
307
+
308
+ | Key | Value |
309
+ |-----|-------|
310
+ | `status` | Overall status: `"ok"`, `"degraded"`, or `"critical"` |
311
+ | `checks` | Hash of `{ check_name => { status:, latency_ms:, message: } }` |
312
+
313
+ `duration` on the event covers the entire parallel check run, not individual checks.
166
314
 
167
315
  [↑ Back to top](#table-of-contents)
168
316
 
@@ -170,7 +318,7 @@ The payload includes `status` (overall: `ok`/`degraded`/`critical`) and `checks`
170
318
 
171
319
  ## Prometheus Metrics
172
320
 
173
- `GET /health/metrics` returns Prometheus text exposition format (`text/plain; version=0.0.4`). This endpoint always returns HTTP 200 Prometheus convention is that scrape targets should always respond successfully, with check state encoded in metric values.
321
+ `GET /health/metrics` returns Prometheus text exposition format (`text/plain; version=0.0.4`). This endpoint always returns HTTP 200 per Prometheus scraping convention check state is encoded in metric values.
174
322
 
175
323
  ```
176
324
  # HELP rails_health_check_status Health check status (0=ok, 1=degraded, 2=critical)
@@ -184,6 +332,24 @@ rails_health_check_latency_ms{check="database"} 4
184
332
  rails_health_check_latency_ms{check="cache"} 2
185
333
  ```
186
334
 
335
+ Latency lines are omitted for checks that do not call `measure { }`.
336
+
337
+ [↑ Back to top](#table-of-contents)
338
+
339
+ ---
340
+
341
+ ## Result Caching
342
+
343
+ By default every request re-runs all checks. Set `cache_duration` to serve cached results for N seconds, reducing load on the database, Redis, and other dependencies:
344
+
345
+ ```ruby
346
+ RailsHealthChecks.configure do |config|
347
+ config.cache_duration = 10 # seconds
348
+ end
349
+ ```
350
+
351
+ The cache is keyed per check set — `GET /health` and `GET /health/workers` cache independently. The cache is in-process (not shared across dynos/containers), so each instance maintains its own result window.
352
+
187
353
  [↑ Back to top](#table-of-contents)
188
354
 
189
355
  ---
@@ -217,12 +383,12 @@ RailsHealthChecks.configure do |config|
217
383
  end
218
384
  ```
219
385
 
220
- | Endpoint | Description |
221
- |----------|-------------|
222
- | `GET /health/system` | Runs only `:disk` and `:memory`, same JSON shape as `GET /health` |
223
- | `GET /health/workers` | Runs only `:sidekiq` and `:good_job` |
386
+ | Endpoint | Runs |
387
+ |----------|------|
388
+ | `GET /health/system` | `:disk`, `:memory` |
389
+ | `GET /health/workers` | `:sidekiq`, `:good_job` |
224
390
 
225
- Unknown group names return `404 Not Found`.
391
+ The response shape is identical to `GET /health`. Unknown group names return `404 Not Found`.
226
392
 
227
393
  [↑ Back to top](#table-of-contents)
228
394
 
@@ -230,26 +396,115 @@ Unknown group names return `404 Not Found`.
230
396
 
231
397
  ## Custom Checks
232
398
 
233
- Define a class inheriting from `RailsHealthChecks::Check` and register it in your initializer:
399
+ ### Authoring
400
+
401
+ Define a class inheriting from `RailsHealthChecks::Check`, implement `call`, and register it:
234
402
 
235
403
  ```ruby
236
- class MyApiCheck < RailsHealthChecks::Check
404
+ class PaymentGatewayCheck < RailsHealthChecks::Check
237
405
  def call
238
- res = Net::HTTP.get_response(URI("https://api.example.com/status"))
239
- res.code == "200" ? pass : fail_with("API returned #{res.code}")
406
+ measure do
407
+ response = Net::HTTP.get_response(URI("https://api.stripe.com/v1/charges"))
408
+ case response.code.to_i
409
+ when 200, 401 # 401 = auth error, but gateway is reachable
410
+ pass
411
+ when 429
412
+ warn_with("rate limited (429)")
413
+ else
414
+ fail_with("unexpected status #{response.code}")
415
+ end
416
+ end
417
+ rescue StandardError => e
418
+ fail_with(e.message)
240
419
  end
241
420
  end
242
421
 
243
422
  RailsHealthChecks.configure do |config|
244
- config.register :my_api, MyApiCheck.new
423
+ config.register :payment_gateway, PaymentGatewayCheck.new
424
+ config.register :slow_gateway, PaymentGatewayCheck.new, timeout: 15
245
425
  end
246
426
  ```
247
427
 
248
- `config.register` automatically adds the check to the active checks list. Pass `timeout:` to override the global timeout for this check only:
428
+ `config.register` appends the check to the active list automatically.
429
+
430
+ ### Check API
431
+
432
+ | Method | Status set | Use when |
433
+ |--------|-----------|----------|
434
+ | `pass(message = nil)` | `ok` | Check passed; optional message |
435
+ | `warn_with(message)` | `degraded` | Check is functional but degraded |
436
+ | `fail_with(message)` | `critical` | Check failed; service is impaired |
437
+ | `measure { }` | — | Wraps a block and records `latency_ms` |
438
+
439
+ **State contract:** call exactly one of `pass`, `warn_with`, or `fail_with` per `call` invocation. The check instance is `dup`'d before each run, so instance variables set during one request do not bleed into the next.
440
+
441
+ ### Testing Custom Checks
442
+
443
+ Call the check directly in a unit test — no request stack needed:
249
444
 
250
445
  ```ruby
251
- config.register :slow_api, MyApiCheck.new, timeout: 10
252
- ``` Use `pass`, `warn_with`, and `fail_with` (inherited from `Check`) to set status, and `measure { }` to record latency.
446
+ RSpec.describe PaymentGatewayCheck do
447
+ subject(:check) { described_class.new }
448
+
449
+ context "when the gateway is reachable" do
450
+ before do
451
+ stub_request(:get, "https://api.stripe.com/v1/charges")
452
+ .to_return(status: 200)
453
+ end
454
+
455
+ it "passes" do
456
+ check.call
457
+ expect(check.status).to eq("ok")
458
+ end
459
+ end
460
+
461
+ context "when the gateway is rate-limited" do
462
+ before do
463
+ stub_request(:get, "https://api.stripe.com/v1/charges")
464
+ .to_return(status: 429)
465
+ end
466
+
467
+ it "warns" do
468
+ check.call
469
+ expect(check.status).to eq("degraded")
470
+ expect(check.message).to include("rate limited")
471
+ end
472
+ end
473
+ end
474
+ ```
475
+
476
+ [↑ Back to top](#table-of-contents)
477
+
478
+ ---
479
+
480
+ ## Migrating from OkComputer
481
+
482
+ See [MIGRATING_FROM_OKCOMPUTER.md](MIGRATING_FROM_OKCOMPUTER.md) for a full mapping of check names, configuration keys, and endpoint differences.
483
+
484
+ Quick reference:
485
+
486
+ | OkComputer | rails_health_checks |
487
+ |------------|---------------------|
488
+ | `OkComputer::ActiveRecordCheck` | `:database` |
489
+ | `OkComputer::CacheCheck` | `:cache` |
490
+ | `OkComputer::RedisCheck` | `:redis` |
491
+ | `OkComputer::SidekiqLatencyCheck` | `:sidekiq` + `config.sidekiq_queue_size` |
492
+ | `OkComputer::HttpCheck` | `:http` + `config.http_url` |
493
+ | `OkComputer::CustomCheck` subclass | Subclass `RailsHealthChecks::Check` |
494
+ | `GET /okcomputer` | `GET /health` |
495
+ | `GET /okcomputer/all` | `GET /health` |
496
+
497
+ [↑ Back to top](#table-of-contents)
498
+
499
+ ---
500
+
501
+ ## Performance
502
+
503
+ See [BENCHMARKS.md](BENCHMARKS.md) for throughput numbers, parallel execution speedup, and cache effectiveness measurements. To run the suite locally:
504
+
505
+ ```bash
506
+ bundle exec rake benchmark
507
+ ```
253
508
 
254
509
  [↑ Back to top](#table-of-contents)
255
510
 
@@ -269,4 +524,4 @@ config.register :slow_api, MyApiCheck.new, timeout: 10
269
524
 
270
525
  ## License
271
526
 
272
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
527
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile CHANGED
@@ -12,3 +12,8 @@ Bundler::Audit::Task.new
12
12
  RSpec::Core::RakeTask.new(:spec)
13
13
 
14
14
  task default: [:lint, :'bundle:audit:update', 'bundle:audit:check', :spec]
15
+
16
+ desc "Run performance benchmarks"
17
+ task :benchmark do
18
+ ruby "benchmarks/benchmark.rb"
19
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+
5
+ module RailsHealthChecks
6
+ module Generators
7
+ class InitializerGenerator < Rails::Generators::Base
8
+ source_root File.expand_path("templates", __dir__)
9
+
10
+ desc "Creates a RailsHealthChecks initializer with all available configuration options."
11
+
12
+ def copy_initializer
13
+ template "initializer.rb", "config/initializers/rails_health_checks.rb"
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ RailsHealthChecks.configure do |config|
4
+ # Checks to run (default: [:database])
5
+ # Available built-ins: :database, :cache, :redis, :smtp, :sidekiq, :solid_queue,
6
+ # :good_job, :resque, :disk, :memory, :http
7
+ config.checks = [:database]
8
+
9
+ # Global timeout per check in seconds (default: 5)
10
+ config.timeout = 5
11
+
12
+ # Cache check results for N seconds to avoid re-running on every request (default: nil, disabled)
13
+ # config.cache_duration = 10
14
+
15
+ # ---------------------------------------------------------------------------
16
+ # Authentication — all strategies are mutually exclusive; default is public
17
+ # ---------------------------------------------------------------------------
18
+
19
+ # Bearer token: requests must include Authorization: Bearer <token>
20
+ # config.token = ENV["HEALTH_TOKEN"]
21
+
22
+ # IP allowlist: exact IPs or CIDR ranges
23
+ # config.allowed_ips = ["127.0.0.1", "10.0.0.0/8"]
24
+
25
+ # Custom block: return truthy to allow the request
26
+ # config.authenticate { |request| request.headers["X-Internal"] == "true" }
27
+
28
+ # ---------------------------------------------------------------------------
29
+ # Per-environment toggling
30
+ # ---------------------------------------------------------------------------
31
+ # config.disable :disk, in: :test
32
+ # config.disable :memory, in: [:test, :development]
33
+
34
+ # ---------------------------------------------------------------------------
35
+ # Check groups — expose subsets at GET /health/:group
36
+ # ---------------------------------------------------------------------------
37
+ # config.group :system, [:disk, :memory]
38
+ # config.group :workers, [:sidekiq, :good_job]
39
+
40
+ # ---------------------------------------------------------------------------
41
+ # Redis check (requires :redis in config.checks and the redis gem)
42
+ # ---------------------------------------------------------------------------
43
+ # config.redis_url = ENV["REDIS_URL"] # default: redis://localhost:6379/0
44
+
45
+ # ---------------------------------------------------------------------------
46
+ # SMTP check (requires :smtp in config.checks)
47
+ # Reads ActionMailer::Base.smtp_settings automatically if not set here.
48
+ # ---------------------------------------------------------------------------
49
+ # config.smtp_address = "smtp.example.com" # default: ActionMailer config or localhost
50
+ # config.smtp_port = 587 # default: ActionMailer config or 25
51
+
52
+ # ---------------------------------------------------------------------------
53
+ # Disk check (requires :disk in config.checks)
54
+ # ---------------------------------------------------------------------------
55
+ # config.disk_path = "/" # mount point (default: "/")
56
+ # config.disk_warn_threshold = 2 * 1024**3 # bytes free → degraded
57
+ # config.disk_critical_threshold = 512 * 1024**2 # bytes free → critical
58
+
59
+ # ---------------------------------------------------------------------------
60
+ # Memory check (requires :memory in config.checks)
61
+ # ---------------------------------------------------------------------------
62
+ # config.memory_threshold = 512 * 1024**2 # RSS bytes → degraded
63
+
64
+ # ---------------------------------------------------------------------------
65
+ # HTTP check (requires :http in config.checks)
66
+ # ---------------------------------------------------------------------------
67
+ # config.http_url = "https://api.example.com/status"
68
+ # config.http_expected_status = 200 # expected response code (default: 200)
69
+ # config.http_headers = { "Authorization" => "Bearer #{ENV['API_TOKEN']}" }
70
+
71
+ # ---------------------------------------------------------------------------
72
+ # Sidekiq check (requires :sidekiq in config.checks)
73
+ # ---------------------------------------------------------------------------
74
+ # config.sidekiq_queue_size = 1000 # total depth → degraded
75
+
76
+ # ---------------------------------------------------------------------------
77
+ # Solid Queue check (requires :solid_queue in config.checks)
78
+ # ---------------------------------------------------------------------------
79
+ # config.solid_queue_job_count = 500 # pending jobs → degraded
80
+
81
+ # ---------------------------------------------------------------------------
82
+ # GoodJob check (requires :good_job in config.checks)
83
+ # ---------------------------------------------------------------------------
84
+ # config.good_job_latency = 300 # seconds oldest job waiting → degraded
85
+
86
+ # ---------------------------------------------------------------------------
87
+ # Resque check (requires :resque in config.checks)
88
+ # ---------------------------------------------------------------------------
89
+ # config.resque_queue_size = 1000 # total depth → degraded
90
+
91
+ # ---------------------------------------------------------------------------
92
+ # Custom checks
93
+ # ---------------------------------------------------------------------------
94
+ # class MyApiCheck < RailsHealthChecks::Check
95
+ # def call
96
+ # res = Net::HTTP.get_response(URI("https://api.example.com/status"))
97
+ # res.code == "200" ? pass : fail_with("API returned #{res.code}")
98
+ # end
99
+ # end
100
+ #
101
+ # config.register :my_api, MyApiCheck.new
102
+ # config.register :slow_api, MyApiCheck.new, timeout: 10 # per-check timeout override
103
+ end
@@ -8,6 +8,11 @@ module RailsHealthChecks
8
8
  BUILT_INS = {
9
9
  database: -> { Checks::DatabaseCheck.new },
10
10
  cache: -> { Checks::CacheCheck.new },
11
+ redis: -> { Checks::RedisCheck.new(url: RailsHealthChecks.configuration.redis_url) },
12
+ smtp: -> { Checks::SmtpCheck.new(
13
+ address: RailsHealthChecks.configuration.smtp_address,
14
+ port: RailsHealthChecks.configuration.smtp_port
15
+ ) },
11
16
  sidekiq: -> { Checks::SidekiqCheck.new(queue_size: RailsHealthChecks.configuration.sidekiq_queue_size) },
12
17
  solid_queue: -> { Checks::SolidQueueCheck.new(job_count: RailsHealthChecks.configuration.solid_queue_job_count) },
13
18
  good_job: -> { Checks::GoodJobCheck.new(latency: RailsHealthChecks.configuration.good_job_latency) },
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsHealthChecks
4
+ module Checks
5
+ class RedisCheck < Check
6
+ def initialize(url: nil)
7
+ unless defined?(::Redis)
8
+ raise LoadError, "Redis is not installed. Add `gem 'redis'` to your Gemfile to use the :redis check."
9
+ end
10
+
11
+ @url = url
12
+ end
13
+
14
+ def call
15
+ measure do
16
+ client = ::Redis.new(url: @url || ENV.fetch("REDIS_URL", "redis://localhost:6379/0"))
17
+ client.ping
18
+ client.close
19
+ end
20
+ pass
21
+ rescue StandardError => e
22
+ fail_with(e.message)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "net/smtp"
4
+
5
+ module RailsHealthChecks
6
+ module Checks
7
+ class SmtpCheck < Check
8
+ def initialize(address: nil, port: nil)
9
+ @address = address
10
+ @port = port
11
+ end
12
+
13
+ def call
14
+ measure do
15
+ smtp = Net::SMTP.new(resolved_address, resolved_port)
16
+ smtp.start { }
17
+ end
18
+ pass
19
+ rescue StandardError => e
20
+ fail_with(e.message)
21
+ end
22
+
23
+ private
24
+
25
+ def resolved_address
26
+ @address ||
27
+ actionmailer_setting(:address) ||
28
+ "localhost"
29
+ end
30
+
31
+ def resolved_port
32
+ @port ||
33
+ actionmailer_setting(:port) ||
34
+ 25
35
+ end
36
+
37
+ def actionmailer_setting(key)
38
+ return unless defined?(::ActionMailer::Base)
39
+
40
+ ::ActionMailer::Base.smtp_settings[key]
41
+ rescue StandardError
42
+ nil
43
+ end
44
+ end
45
+ end
46
+ end
@@ -4,10 +4,12 @@ module RailsHealthChecks
4
4
  class ConfigurationError < StandardError; end
5
5
 
6
6
  class Configuration
7
- BUILT_IN_NAMES = %i[database cache sidekiq solid_queue good_job resque disk memory http].freeze
7
+ BUILT_IN_NAMES = %i[database cache redis sidekiq solid_queue good_job resque disk memory http smtp].freeze
8
8
 
9
9
  attr_writer :checks
10
10
  attr_accessor :timeout, :cache_duration, :allowed_ips, :token,
11
+ :redis_url,
12
+ :smtp_address, :smtp_port,
11
13
  :sidekiq_queue_size, :solid_queue_job_count, :good_job_latency,
12
14
  :resque_queue_size, :disk_warn_threshold, :disk_critical_threshold, :disk_path,
13
15
  :memory_threshold, :http_url, :http_expected_status, :http_headers
@@ -20,6 +22,9 @@ module RailsHealthChecks
20
22
  @allowed_ips = nil
21
23
  @token = nil
22
24
  @authenticate_block = nil
25
+ @redis_url = nil
26
+ @smtp_address = nil
27
+ @smtp_port = nil
23
28
  @sidekiq_queue_size = nil
24
29
  @solid_queue_job_count = nil
25
30
  @good_job_latency = nil
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RailsHealthChecks
4
- VERSION = "0.7.0"
4
+ VERSION = "1.0.0"
5
5
  end
@@ -7,6 +7,8 @@ require "rails_health_checks/authentication"
7
7
  require "rails_health_checks/check"
8
8
  require "rails_health_checks/checks/database_check"
9
9
  require "rails_health_checks/checks/cache_check"
10
+ require "rails_health_checks/checks/redis_check"
11
+ require "rails_health_checks/checks/smtp_check"
10
12
  require "rails_health_checks/checks/sidekiq_check"
11
13
  require "rails_health_checks/checks/solid_queue_check"
12
14
  require "rails_health_checks/checks/good_job_check"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_health_checks
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chuck Smith
@@ -15,14 +15,14 @@ dependencies:
15
15
  requirements:
16
16
  - - ">="
17
17
  - !ruby/object:Gem::Version
18
- version: 8.1.3
18
+ version: '8.0'
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - ">="
24
24
  - !ruby/object:Gem::Version
25
- version: 8.1.3
25
+ version: '8.0'
26
26
  description: A Rails engine providing configurable health check endpoints for monitoring
27
27
  application status.
28
28
  email:
@@ -43,6 +43,8 @@ files:
43
43
  - app/mailers/rails_health_checks/application_mailer.rb
44
44
  - app/models/rails_health_checks/application_record.rb
45
45
  - config/routes.rb
46
+ - lib/generators/rails_health_checks/initializer_generator.rb
47
+ - lib/generators/rails_health_checks/templates/initializer.rb
46
48
  - lib/rails_health_checks.rb
47
49
  - lib/rails_health_checks/authentication.rb
48
50
  - lib/rails_health_checks/check.rb
@@ -53,8 +55,10 @@ files:
53
55
  - lib/rails_health_checks/checks/good_job_check.rb
54
56
  - lib/rails_health_checks/checks/http_check.rb
55
57
  - lib/rails_health_checks/checks/memory_check.rb
58
+ - lib/rails_health_checks/checks/redis_check.rb
56
59
  - lib/rails_health_checks/checks/resque_check.rb
57
60
  - lib/rails_health_checks/checks/sidekiq_check.rb
61
+ - lib/rails_health_checks/checks/smtp_check.rb
58
62
  - lib/rails_health_checks/checks/solid_queue_check.rb
59
63
  - lib/rails_health_checks/configuration.rb
60
64
  - lib/rails_health_checks/engine.rb