Packages
nerves_hub_link
2.8.0
2.12.0
2.11.1
2.11.0
retired
2.10.2
2.10.1
2.10.0
2.9.0
2.9.0-rc.3
2.9.0-rc.2
2.9.0-rc.1
2.8.1
2.8.0
2.7.3
2.7.2
2.7.0
retired
2.6.0
2.5.2
2.5.1
2.5.0
2.4.0
2.3.0
2.2.1
2.2.0
2.1.1
2.1.0
2.0.0
1.4.1
1.4.0
1.3.0
1.2.0
1.1.0
1.0.1
1.0.0
0.13.1
0.13.0
0.12.1
0.12.0
0.11.0
0.10.2
0.10.1
retired
0.10.0
retired
0.10.0-rc.0
0.9.4
0.9.3
0.9.2
0.9.1
0.9.0
0.8.2
0.8.1
0.8.0
0.7.6
Manage your Nerves fleet by connecting it to NervesHub
Current section
Files
Jump to
Current section
Files
lib/nerves_hub_link/extensions/health/health.ex
# SPDX-FileCopyrightText: 2024 Jon Carstens
# SPDX-FileCopyrightText: 2024 Lars Wikman
# SPDX-FileCopyrightText: 2025 Elin Olsson
# SPDX-FileCopyrightText: 2025 Josh Kalderimis
#
# SPDX-License-Identifier: Apache-2.0
#
defmodule NervesHubLink.Extensions.Health do
@moduledoc """
The Health Extension.
Provides metrics, metadata and alarms to allow building an understanding of
the operational state of a device. The device's "health". This information
is reported over the extensions mechanism to NervesHub for display, alerting
and more.
"""
use NervesHubLink.Extensions, name: "health", version: "0.0.1"
alias NervesHubLink.Alarms
alias NervesHubLink.Extensions.Health.DefaultReport
alias NervesHubLink.Extensions.Health.DeviceStatus
require Logger
@doc """
Request a health report be sent asynchronously.
## Examples
iex> NervesHubLink.Extensions.Health.send_report()
:ok
"""
@spec send_report() :: :ok
def send_report() do
GenServer.call(__MODULE__, :send_report)
end
@doc """
Confirms if a health report has been sent.
## Examples
iex> NervesHubLink.Extensions.Health.report_sent?()
false
iex> NervesHubLink.Extensions.Health.send_report()
:ok
iex> NervesHubLink.Extensions.Health.report_sent?()
true
"""
@spec report_sent?() :: boolean()
def report_sent?() do
GenServer.call(__MODULE__, :report_sent?)
end
@impl GenServer
def init(_opts) do
# Does not send an initial report, server requests one
{:ok, %{report_sent: false}}
end
@impl GenServer
def handle_call(:send_report, _args, state) do
case send_health_report(state) do
{:ok, state} -> {:reply, :ok, state}
{:error, state} -> {:reply, :error, state}
end
end
@impl GenServer
def handle_call(:report_sent?, _args, state) do
{:reply, state[:report_sent], state}
end
@impl NervesHubLink.Extensions
def handle_event("check", _msg, state) do
case send_health_report(state) do
{:ok, state} -> {:noreply, state}
{:error, state} -> {:noreply, state}
end
end
defp send_health_report(state) do
case push("report", %{"value" => check_health()}) do
{:ok, _} -> {:ok, %{state | report_sent: true}}
{:error, _reason} -> {:error, state}
end
end
@spec check_health(module()) :: DeviceStatus.t() | nil
def check_health(default_report \\ DefaultReport) do
config = Application.get_env(:nerves_hub, :health, [])
report = Keyword.get(config, :report, default_report)
if report do
Alarms.clear_alarm(NervesHubLink.Extensions.Health.CheckFailed)
DeviceStatus.new(
timestamp: report.timestamp(),
metadata: report.metadata(),
alarms: report.alarms(),
metrics: report.metrics(),
checks: report.checks()
)
end
rescue
err ->
reason =
try do
inspect(err)
rescue
_ ->
"unknown error"
end
Logger.error("Health check failed due to error: #{reason}")
Alarms.clear_alarm(NervesHubLink.Extensions.Health.CheckFailed)
Alarms.set_alarm({NervesHubLink.Extensions.Health.CheckFailed, [reason: reason]})
DeviceStatus.new(
timestamp: DateTime.utc_now(),
metadata: %{},
alarms: %{to_string(NervesHubLink.Extensions.Health.CheckFailed) => reason},
metrics: %{},
checks: %{}
)
end
end