RuntimeCheck behaviour (runtime_check v0.1.0)
View SourceA GenServer to run a set of system checks on application start up.
The process is normally run after the rest of the children in the supervisor, so processes like
Ecto, Oban, and FunWithFlags are available.
Additionally, it is not actually kept in the supervision tree as init/1 returns :ignore when
the checks succeed.
Usage
Define a module that uses RuntimeCheck:
defmodule MyApp.RuntimeChecks do
use RuntimeCheck
@impl true
def run? do
# Decide if checks should run. Maybe skip them during tests or if an env var is set.
true
end
@impl true
def checks do
# Return a list of checks. See `RuntimeCheck.DSL`.
[
check(:foo, fn ->
# Run function that should return :ok, {:ok, something}, :ignore or {:error, reason}
:ok
end),
check(:nested, [
check(:bar, fn -> :ignore end),
check(:baz, fn ->
if everything_ok() do
:ok
else
{:error, "not everything is ok!"}
end
end)
]),
# If FunWithFlags is installed. Run nested checks only if the flag is enabled.
feature_check(:some_flag, [
app_var(:quzz_url :my_app, [:quzz, :url]),
env_var("QUZZ_API_KEY")
])
]
end
endThen in MyApp.application add the worker
children = [
# ...
MyApp.Repo,
MyAppWeb.Endpoint,
# ...
MyApp.RuntimeChecks
]Then when running the app, something like this will be logged:
[info] [RuntimeCheck] starting...
[info] [RuntimeCheck] foo: passed
[info] [RuntimeCheck] nested:
[warning] [RuntimeCheck] > bar: ignored
[info] [RuntimeCheck] > baz: passed
[info] [RuntimeCheck] nested: passed
[info] [RuntimeCheck] some_flag:
[info] [RuntimeCheck] > quzz_url: passed
[info] [RuntimeCheck] > QUZZ_API_KEY: passed
[info] [RuntimeCheck] some_flag: passed
[info] [RuntimeCheck] doneOr if some checks fail:
[info] [RuntimeCheck] starting...
[info] [RuntimeCheck] foo: passed
[info] [RuntimeCheck] nested:
[warning] [RuntimeCheck] > bar: ignored
[error] [RuntimeCheck] > baz: failed. Reason: "not everything is ok!"
[error] [RuntimeCheck] nested: failed
[info] [RuntimeCheck] some_flag:
[info] [RuntimeCheck] > quzz_url: passed
[info] [RuntimeCheck] > QUZZ_API_KEY: passed
[info] [RuntimeCheck] some_flag: passed
[error] [RuntimeCheck] some checks failed!
** (Mix) Could not start application my_app: MyApp.Application.start(:normal, []) returned an error: shutdown: failed to start child: MyApp.RuntimeChecks
** (EXIT) :runtime_check_failedChecks
Each check is a RuntimeCheck.Check but normally, they are constructed using the functions
in RuntimeCheck.DSL.
Feature flag checks
If fun_with_flags is installed, a feature_check
function will be available in the DSL. It allows running checks only if a feature flag is
enabled.
If you add fun_with_flags after adding runtime_check make sure to recompile with
mix deps.compile --force runtime_check.
Testing
To test the checks you can run MyApp.RuntimeChecks.run(), which will run the checks in the
current process instead of starting a new one. You can pass log: false to disable logging.
assert MyApp.RuntimeChecks.run(log: false) == {:ok, %{}}See RuntimeCheck.run/2 for details on the return value.
Summary
Functions
Runs the checks in the module.
Callbacks
@callback checks() :: [RuntimeCheck.Check.t()]
A list of checks to run.
@callback run?() :: boolean()
Whether the checks should run on startup.
Functions
Runs the checks in the module.
Returns {:ok, ignored_map} if checks pass. ignored_map is a a nested map of checks that
were ignored. Like %{check1: :ignored, check2: %{subcheck1: :ignored}}. The map is empty if
no checks are ignored.
If at least one check fails, {:error, map} is returned. Where map is a nested map with
ignored and failed checks like
%{
check1: :ignored,
check2: "failure reason",
check3: %{
subcheck1: :ignored,
subcheck2: "another reason"
}
}By default, the result will be logged, but it can be disabled by passing log: false.
Use the module directly when starting in a supervisor tree. See the moduledocs for
RuntimeCheck.