Packages
macula
0.22.4
7.1.0
7.0.0
6.0.0
5.2.2
5.2.1
5.2.0
5.1.0
5.0.0
4.8.0
4.7.1
4.7.0
4.6.0
4.5.0
4.4.10
4.4.9
4.4.8
4.4.7
4.4.6
4.4.5
4.4.4
4.4.3
4.4.2
4.4.1
4.4.0
4.3.1
4.3.0
4.2.9
4.2.8
4.2.7
4.2.6
4.2.5
4.2.4
4.2.3
4.2.2
4.2.1
4.2.0
4.1.1
4.1.0
4.0.0
3.16.0
3.15.3
3.15.2
3.15.1
3.14.0
3.13.0
3.12.1
3.12.0
3.11.1
3.11.0
3.10.3
3.10.2
3.10.1
3.9.0
3.8.0
3.7.0
3.5.0
3.4.0
3.3.0
3.2.0
3.1.0
3.0.0
2.1.1
2.1.0
2.0.0
1.5.2
1.5.1
1.4.30
1.4.29
1.4.28
1.4.27
1.4.26
1.4.25
1.4.24
1.4.23
1.4.22
1.4.21
1.4.20
1.4.19
1.4.18
1.4.17
1.4.16
1.4.15
1.4.14
1.4.13
1.4.11
1.4.10
1.4.9
1.4.8
1.4.7
1.4.6
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.3.1
1.3.0
1.2.0
1.1.0
1.0.10
1.0.9
1.0.8
1.0.7
1.0.6
1.0.5
1.0.4
1.0.3
1.0.2
1.0.1
1.0.0
0.48.6
0.48.5
0.48.4
0.48.3
0.48.2
0.48.1
0.48.0
0.47.1
0.47.0
0.46.3
0.46.1
0.46.0
0.45.3
0.45.2
0.45.1
0.45.0
0.44.2
0.44.1
0.44.0
0.43.3
0.43.2
0.43.1
0.43.0
0.42.9
0.42.8
0.42.7
0.42.6
0.42.5
0.42.4
0.42.3
0.42.2
0.42.1
0.42.0
0.41.1
0.41.0
0.40.1
0.40.0
0.39.9
0.39.8
0.39.7
0.39.6
0.39.5
0.39.4
0.39.3
0.39.2
0.39.1
0.39.0
0.38.8
0.38.7
0.38.6
0.38.5
0.38.4
0.38.3
0.38.2
0.38.1
0.38.0
0.37.7
0.37.6
0.37.5
0.37.4
0.37.3
0.37.2
0.37.1
0.37.0
0.36.6
0.36.5
0.36.4
0.36.3
0.36.2
0.36.1
0.36.0
0.35.4
0.35.3
0.35.2
0.35.1
0.35.0
0.34.1
0.34.0
0.33.1
0.33.0
0.32.5
0.32.4
0.32.3
0.32.2
0.32.1
0.32.0
0.31.9
0.31.8
0.31.7
0.31.6
0.31.5
0.31.4
0.31.3
0.31.2
0.31.1
0.31.0
0.30.10
0.30.9
0.30.8
0.30.7
0.30.6
0.30.5
0.30.4
0.30.3
0.30.2
0.30.1
0.30.0
0.29.0
0.28.3
0.28.2
0.28.1
0.28.0
0.27.1
0.27.0
0.26.1
0.26.0
0.25.6
0.25.5
0.25.4
0.25.3
0.25.2
0.25.1
0.25.0
0.24.6
0.24.5
0.24.4
0.24.3
0.24.2
0.24.1
0.24.0
0.23.3
0.23.2
0.23.1
0.23.0
0.22.12
0.22.11
0.22.10
0.22.9
0.22.8
0.22.7
0.22.6
0.22.5
0.22.4
0.22.3
0.22.2
0.22.1
0.22.0
0.21.7
0.21.6
0.21.5
0.21.4
0.21.2
0.21.1
0.21.0
0.20.25
0.20.24
0.20.23
0.20.22
0.20.21
0.20.20
0.20.19
0.20.18
0.20.17
0.20.16
0.20.15
0.20.14
0.20.13
0.20.12
0.20.11
0.20.10
0.20.9
0.20.8
0.20.7
0.20.6
0.20.5
0.20.3
0.20.2
0.20.1
0.20.0
0.19.2
0.19.1
0.19.0
0.18.1
0.18.0
0.17.4
0.17.3
0.17.2
0.17.1
0.17.0
0.16.6
0.16.5
0.16.4
0.16.3
0.16.2
0.16.1
0.16.0
0.15.1
0.15.0
0.14.3
0.14.2
0.14.1
0.14.0
0.12.6
0.12.5
0.12.3
0.11.3
0.10.2
0.10.1
0.10.0
0.9.2
0.9.1
0.9.0
0.8.25
0.8.24
0.8.23
0.8.22
0.8.21
0.8.20
0.8.19
0.8.18
0.8.17
0.8.16
0.8.15
0.8.14
0.8.13
0.8.12
0.8.11
0.8.10
0.8.9
0.8.8
0.8.7
0.8.6
0.8.5
0.8.4
0.8.3
0.8.2
0.8.1
0.8.0
0.7.30
0.7.29
0.7.28
0.7.27
0.7.26
0.7.25
0.7.24
0.7.23
0.7.22
0.7.21
0.7.20
0.7.19
0.7.18
0.7.17
0.7.16
0.7.15
0.7.14
0.7.13
0.7.12
0.7.11
0.7.10
0.7.9
0.7.8
0.7.7
0.7.6
0.7.5
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.7
0.6.6
0.6.5
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.0
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.4
0.3.3
0.3.2
0.3.1
Macula HTTP/3 Mesh SDK — connect, subscribe, publish, call, advertise
Current section
Files
Jump to
Current section
Files
src/macula_bootstrap_system/macula_bootstrap_health.erl
%%%-------------------------------------------------------------------
%%% @doc
%%% Bootstrap Health Monitor - Tracks system health metrics.
%%%
%%% Monitors:
%%% - Service registry size
%%% - DHT query rate
%%% - System uptime
%%% - Memory usage
%%%
%%% @end
%%%-------------------------------------------------------------------
-module(macula_bootstrap_health).
-behaviour(gen_server).
-export([start_link/1]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
-export([get_health/0, is_healthy/0]).
-include_lib("kernel/include/logger.hrl").
-record(state, {
check_interval_ms :: pos_integer(),
last_check :: integer(),
health_status :: map()
}).
%%%===================================================================
%%% API
%%%===================================================================
-spec start_link(Config :: map()) -> {ok, pid()} | {error, term()}.
start_link(Config) ->
gen_server:start_link({local, ?MODULE}, ?MODULE, Config, []).
%% @doc Get current health status
-spec get_health() -> {ok, map()}.
get_health() ->
gen_server:call(?MODULE, get_health).
%% @doc Check if system is healthy
-spec is_healthy() -> boolean().
is_healthy() ->
case get_health() of
{ok, #{status := healthy}} -> true;
_ -> false
end.
%%%===================================================================
%%% GenServer Callbacks
%%%===================================================================
init(Config) ->
CheckIntervalMs = maps:get(health_check_interval, Config, 30000), % 30 seconds default
?LOG_INFO("[BootstrapHealth] Starting health monitor (interval: ~pms)", [CheckIntervalMs]),
%% Schedule first health check
erlang:send_after(CheckIntervalMs, self(), health_check),
State = #state{
check_interval_ms = CheckIntervalMs,
last_check = erlang:system_time(second),
health_status = #{
status => unknown,
message => <<"Health check pending">>
}
},
{ok, State}.
handle_call(get_health, _From, State) ->
{reply, {ok, State#state.health_status}, State};
handle_call(_Request, _From, State) ->
{reply, {error, unknown_request}, State}.
handle_cast(_Msg, State) ->
{noreply, State}.
%% Perform health check
handle_info(health_check, State) ->
NewHealth = perform_health_check(),
NewState = State#state{
last_check = erlang:system_time(second),
health_status = NewHealth
},
%% Log if unhealthy
case maps:get(status, NewHealth) of
healthy -> ok;
Status ->
?LOG_WARNING("[BootstrapHealth] System unhealthy: ~p - ~s",
[Status, maps:get(message, NewHealth)])
end,
%% Schedule next check
erlang:send_after(State#state.check_interval_ms, self(), health_check),
{noreply, NewState};
handle_info(_Info, State) ->
{noreply, State}.
terminate(_Reason, _State) ->
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%%===================================================================
%%% Internal Functions
%%%===================================================================
perform_health_check() ->
Checks = [
check_server_running(),
check_registry_running(),
check_memory_usage()
],
%% If any check fails, system is unhealthy
case lists:filter(fun({Status, _}) -> Status =/= ok end, Checks) of
[] ->
#{
status => healthy,
message => <<"All checks passed">>,
checks => Checks,
timestamp => erlang:system_time(second)
};
Failures ->
#{
status => unhealthy,
message => format_failures(Failures),
checks => Checks,
timestamp => erlang:system_time(second)
}
end.
check_server_running() ->
case whereis(macula_bootstrap_server) of
undefined -> {error, <<"Bootstrap server not running">>};
Pid when is_pid(Pid) -> {ok, <<"Bootstrap server running">>}
end.
check_registry_running() ->
case whereis(macula_bootstrap_registry) of
undefined -> {error, <<"Service registry not running">>};
Pid when is_pid(Pid) ->
%% Registry is running (delegates to DHT)
{ok, <<"Bootstrap registry running (delegates to DHT)">>}
end.
check_memory_usage() ->
MemoryMB = erlang:memory(total) div (1024 * 1024),
case MemoryMB > 1000 of % Warn if > 1GB
true -> {warning, iolist_to_binary(io_lib:format("High memory usage: ~pMB", [MemoryMB]))};
false -> {ok, iolist_to_binary(io_lib:format("Memory usage: ~pMB", [MemoryMB]))}
end.
format_failures(Failures) ->
Messages = [Msg || {_Status, Msg} <- Failures],
iolist_to_binary(lists:join("; ", Messages)).