Class: StandardHealth::Aggregator
- Inherits:
-
Object
- Object
- StandardHealth::Aggregator
- Defined in:
- lib/standard_health/aggregator.rb
Overview
Runs all registered checks and rolls them up into a single status.
Status semantics:
:ok — every check returned :ok
:degraded — at least one non-critical check failed, OR a check was
skipped because the total budget ran out
:unavailable — at least one critical check failed
The aggregator never raises. Each check is invoked through safe_run
which catches StandardError so a buggy custom check cannot take down
/ready. Instrumentation is held to the same bar — every emit is wrapped
so a broken subscriber cannot become a new way for /ready to 500.
Class Method Summary collapse
Class Method Details
.call(checks: StandardHealth.config.checks, now: Time.now.utc) ⇒ Object
27 28 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 |
# File 'lib/standard_health/aggregator.rb', line 27 def self.call(checks: StandardHealth.config.checks, now: Time.now.utc) started = monotonic budget = StandardHealth.config.total_check_budget check_rows = checks.map do |reg| if budget_exhausted?(budget, started) row = skipped_row(reg, budget) # Skips emit too. Without this a skip is invisible to the Metrics # notifier — the only trace would be a name inside ready.evaluated's # `failed[]`, with no per-check counter — so once a budget is # enabled you could not answer "how often is check X getting # skipped", which is exactly the question a budget creates. emit_check(row) row else safe_run(reg) end end duration_ms = ((monotonic - started) * 1000).round status = overall_status(check_rows) failing = check_rows.reject { |r| r[:status] == :ok } emit( "standard_health.ready.evaluated", status: status, duration_ms: duration_ms, failed: failing.map { |r| r[:name] }, # The full failure detail rides on THIS event, not only on the # per-check one. Redaction removes the message from the HTTP body on # the promise that it still reaches logs and Sentry — but both of # those subscribers are driven by ready.evaluated (deliberately: it # is the transition-gated event, so they don't fire per poll). If the # message only existed on check.completed, redaction would delete the # last copy instead of relocating it. failures: failing.map do |r| { name: r[:name], critical: r[:critical], status: r[:status], error_class: r[:error_class], error_message: r[:error] }.compact end ) { status: status, checks: check_rows, generated_at: now.iso8601 } end |