Class: StandardHealth::Configuration

Inherits:
Object
  • Object
show all
Defined in:
lib/standard_health/configuration.rb

Overview

Holds engine-wide configuration.

Host apps configure the engine via:

StandardHealth.configure do |c|
c.parent_controller = "ApplicationController"
c.register_check :custom, MyCheck, critical: true
c.env_spec = StandardHealth::EnvSpec.define { ... }
end

Defined Under Namespace

Classes: Registration

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeConfiguration

Returns a new instance of Configuration.



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/standard_health/configuration.rb', line 123

def initialize
  @parent_controller = "ActionController::API"
  @diagnostics_parent_controller = nil
  @env_spec = nil
  @checks = []

  @instrumentation_enabled = true
  @logger = nil
  @sentry_enabled = true
  @metric_prefix = "health"
  @extra_notifiers = []

  @expose_check_errors = false
  @detail_token = nil

  @default_check_timeout = nil
  @total_check_budget = nil
end

Instance Attribute Details

#default_check_timeoutObject

--- Timeouts (machinery only in 0.4.1) ----------------------------

BOTH DEFAULT TO nil, meaning OFF — identical behaviour to v0.4.0.

This is deliberate. Turning timeouts on is a semantic change: a check that has always been slow-but-fine starts reporting :fail, and for a critical check that pulls the instance out of rotation. Shipping that in a patch release, to five apps at once, on a bundle update, is how you cause the outage you were trying to prevent.

The machinery ships now so apps can opt in per check and so the events emitted in this release can tell us what the real p99 latencies are. Sensible defaults get chosen from that data in a later release, once enough p99 data has accumulated to pick them from evidence rather than guesswork — deliberately NOT 0.5.0, which shipped without it.



99
100
101
# File 'lib/standard_health/configuration.rb', line 99

def default_check_timeout
  @default_check_timeout
end

#detail_tokenObject

When set, a request carrying this value in X-Health-Token receives the unredacted body. Break-glass for on-call without a redeploy. Compared with a constant-time comparison.



82
83
84
# File 'lib/standard_health/configuration.rb', line 82

def detail_token
  @detail_token
end

#diagnostics_parent_controllerObject

Optional class name of a controller that ONLY DiagnosticsController should inherit from. When set, HealthController continues to use parent_controller while DiagnosticsController uses this one. Lets host apps put auth (e.g. HTTP Basic) on the diagnostics endpoint without needing to set raise_on_missing_callback_actions = false to suppress Rails 7.1's missing-action error caused by a single parent declaring before_action :auth, only: :env for both controllers.

When unset (the default), DiagnosticsController falls back to parent_controller — fully backward-compatible with v0.1.0.



41
42
43
# File 'lib/standard_health/configuration.rb', line 41

def diagnostics_parent_controller
  @diagnostics_parent_controller
end

#env_specObject

An optional StandardHealth::EnvSpec instance describing required and recommended environment variables for the host app. Audited via the /diagnostics/env endpoint.



46
47
48
# File 'lib/standard_health/configuration.rb', line 46

def env_spec
  @env_spec
end

#expose_check_errorsObject

--- Response redaction --------------------------------------------

When false (the default) a failing check reports error_class + error_code instead of the raw exception message. /ready is unauthenticated, and raw driver errors leak hosts, ports and usernames. The full message still reaches logs and Sentry via instrumentation.

Set true to restore the pre-0.4.1 verbose bodies.



77
78
79
# File 'lib/standard_health/configuration.rb', line 77

def expose_check_errors
  @expose_check_errors
end

#extra_notifiersObject (readonly)

Extra call(event_name, payload) subscribers supplied by the host.



67
68
69
# File 'lib/standard_health/configuration.rb', line 67

def extra_notifiers
  @extra_notifiers
end

#instrumentation_enabledObject

--- Instrumentation -----------------------------------------------

Master switch for the Logger / Sentry / Metrics subscribers. On by default: a health system nobody can see the history of is the gap this release exists to close.



53
54
55
# File 'lib/standard_health/configuration.rb', line 53

def instrumentation_enabled
  @instrumentation_enabled
end

#loggerObject

Logger for the Logger notifier. nil falls back to Rails.logger.



56
57
58
# File 'lib/standard_health/configuration.rb', line 56

def logger
  @logger
end

#metric_prefixObject

Prefix for emitted metric names, e.g. "health.check.duration".



64
65
66
# File 'lib/standard_health/configuration.rb', line 64

def metric_prefix
  @metric_prefix
end

#parent_controllerObject

Class name of the controller that StandardHealth's controllers should inherit from. Resolved lazily via constantize at request time so the host app's controller (which may pull in auth concerns) is fully loaded before we touch it. Defaults to ActionController::API so the engine works in API-only host apps without configuration.



29
30
31
# File 'lib/standard_health/configuration.rb', line 29

def parent_controller
  @parent_controller
end

#sentry_enabledObject

Whether to register the Sentry notifier. Sentry itself stays a SOFT dependency (guarded by defined?), so leaving this true costs nothing in a host that doesn't use Sentry.



61
62
63
# File 'lib/standard_health/configuration.rb', line 61

def sentry_enabled
  @sentry_enabled
end

#total_check_budgetObject

Budget across all checks, evaluated BEFORE each check starts. Checks not reached are reported :skipped, and a skip alone floors the roll-up at :degraded — never :unavailable. Otherwise a slow NON-critical check could exhaust the budget, leave a critical check unrun, and pull a healthy app out of rotation. nil = no budget.

IMPORTANT — this bounds HOW MANY CHECKS RUN, not how long the probe takes. It is not enforced during an in-flight check: with a 1s budget and a first check that blocks for 30s, /ready still takes 30s and only the checks after it are skipped.

Clamping each check to the remaining budget would fix that, and is deliberately NOT done: it would apply Timeout.timeout to checks whose author never asked for one, and that mechanism raises into the thread at an arbitrary point (see the README's timeout caveat — it can return a broken connection to the pool). Setting a budget must not silently opt you into that.

To bound wall-clock, put a timeout: on the checks that can safely take one — or better, a driver-level timeout.



121
122
123
# File 'lib/standard_health/configuration.rb', line 121

def total_check_budget
  @total_check_budget
end

Instance Method Details

#add_notifier(notifier) ⇒ Object

Register an extra subscriber. Validated at add time so a bad entry fails loudly at boot rather than silently at the first health probe.



144
145
146
147
148
149
150
151
152
# File 'lib/standard_health/configuration.rb', line 144

def add_notifier(notifier)
  unless notifier.respond_to?(:call)
    raise ArgumentError,
          "extra notifiers must respond to `call(event_name, payload)`; got #{notifier.class}"
  end

  @extra_notifiers << notifier
  notifier
end

#checksArray<Registration>

Returns frozen view of registered checks.

Returns:

  • (Array<Registration>)

    frozen view of registered checks



168
169
170
# File 'lib/standard_health/configuration.rb', line 168

def checks
  @checks.dup
end

#register_check(name, klass, critical: false, timeout: nil) ⇒ Object

Register a health check class.

Parameters:

  • name (Symbol)

    short identifier surfaced in /ready output

  • klass (Class)

    subclass of StandardHealth::Check

  • critical (Boolean) (defaults to: false)

    failure flips overall status to :unavailable

  • timeout (Numeric, nil) (defaults to: nil)

    per-check seconds; nil falls back to default_check_timeout (nil = no timeout)



161
162
163
164
165
# File 'lib/standard_health/configuration.rb', line 161

def register_check(name, klass, critical: false, timeout: nil)
  @checks << Registration.new(
    name: name.to_sym, klass: klass, critical: critical, timeout: timeout
  )
end

#reset_checks!Object

Remove all registered checks. Mainly useful in tests where the host app and the engine share a process.



174
175
176
# File 'lib/standard_health/configuration.rb', line 174

def reset_checks!
  @checks = []
end

#reset_notifiers!Object

Drop host-registered notifiers. Test hygiene, mirroring reset_checks!.



179
180
181
# File 'lib/standard_health/configuration.rb', line 179

def reset_notifiers!
  @extra_notifiers = []
end