Macaw.Host.Health (macaw_host v0.3.0)

Copy Markdown View Source

An app's health, in one shape every <app>-ctl status reads.

A report runs named checks. Each returns a term: :ok, {:error, detail} (the app is unhealthy) or {:warn, detail} (worth showing, not a failure), detail a string for people:

Macaw.Host.Health.report([
  database: fn -> Macaw.Host.Health.database(MyApp.Repo) end,
  migrations: fn -> Macaw.Host.Health.migrations(MyApp.Repo) end,
  data_dir: fn -> Macaw.Host.Health.writable(data_dir) end
], version: MyApp.Build.version(), build: MyApp.Build.sha())

gives the report, its checks' results kept as the terms they are and healthy decided from them (a warning keeps it healthy, an error doesn't):

%{healthy: true, version: "1.2.0", build: "3f639e84a704",
  checks: %{database: :ok, migrations: :ok, data_dir: :ok}}

Text is made only at the edge: Macaw.Host.Health.to_wire/1 words each result ("ok", "warning: <detail>", "error: <detail>"), and the two edges that send a report, Macaw.Host.Health.Plug (JSON, 200 when healthy and 503 when not) and Macaw.Host.Release.status/1 (the line <app>-ctl status reads), call it.

A check that raises is reported as an error naming the exception, never a crash of the report: the point of a health check is to answer while something is broken. A check that returns anything else (a bare string, "ok" included) is an error naming the check: its text is never read for what it might mean. The whole report answers within its deadline (5 s unless :deadline says otherwise): the checks run side by side, each given the time left, and one that misses it is reported as timed out.

Summary

Types

A check's result: a term, never text.

Functions

Whether a repo answers SELECT 1. Requires ecto_sql.

Whether results are healthy: none is an error (a warning is not a failure, and no results are healthy). Decided from the terms alone.

Whether every migration has run. Requires ecto_sql.

Runs the checks and builds the report; extra is merged in (version, build), and may not set :healthy or :checks (an ArgumentError, at the call). Options: :deadline, the whole report's, in milliseconds (default 5000).

The report as it is sent: each check's result worded as text, "ok", "warning: <detail>" or "error: <detail>" (a detail that is not a string inspected), every other key as it is. Raises ArgumentError for a map that isn't a report (no :healthy boolean or :checks map) and for a result that isn't a check's term, naming the check: a report's results are never text before this.

Whether a directory is writable, by writing and removing a probe file.

Types

check()

@type check() :: (-> result())

result()

@type result() :: :ok | {:warn, term()} | {:error, term()}

A check's result: a term, never text.

Functions

database(repo)

@spec database(module()) :: :ok | {:error, String.t()}

Whether a repo answers SELECT 1. Requires ecto_sql.

healthy?(results)

@spec healthy?(%{optional(atom()) => result()}) :: boolean()

Whether results are healthy: none is an error (a warning is not a failure, and no results are healthy). Decided from the terms alone.

migrations(repo)

@spec migrations(module()) :: :ok | {:error, String.t()}

Whether every migration has run. Requires ecto_sql.

report(checks, extra \\ [], opts \\ [])

@spec report([{atom(), check()}], keyword(), keyword()) :: map()

Runs the checks and builds the report; extra is merged in (version, build), and may not set :healthy or :checks (an ArgumentError, at the call). Options: :deadline, the whole report's, in milliseconds (default 5000).

to_wire(report)

@spec to_wire(map()) :: map()

The report as it is sent: each check's result worded as text, "ok", "warning: <detail>" or "error: <detail>" (a detail that is not a string inspected), every other key as it is. Raises ArgumentError for a map that isn't a report (no :healthy boolean or :checks map) and for a result that isn't a check's term, naming the check: a report's results are never text before this.

writable(dir)

@spec writable(Path.t()) :: :ok | {:error, String.t()}

Whether a directory is writable, by writing and removing a probe file.