-module(pharos@alert).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/pharos/alert.gleam").
-export([prune_samples/3, window_breached/3]).
-export_type([alert_level/0, alert_event/0, alert_state/0, alert_data/0, window_mode/0, window_spec/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
-type alert_level() :: warning | critical.
-type alert_event() :: {alert_firing, binary(), alert_level(), binary()} |
{alert_resolved, binary()}.
-type alert_state() :: clear | pending | firing | cooling.
-type alert_data() :: {alert_data,
binary(),
alert_level(),
integer(),
integer(),
gleam@option:option(window_spec())}.
-type window_mode() :: average | {percentage, float()}.
-type window_spec() :: {window_spec, integer(), float(), window_mode()}.
-file("src/pharos/alert.gleam", 74).
?DOC(" Drop samples (a `#(timestamp_ms, value)` list) older than `now - window_ms`.\n").
-spec prune_samples(list({integer(), float()}), integer(), integer()) -> list({integer(),
float()}).
prune_samples(Samples, Now, Window_ms) ->
Cutoff = Now - Window_ms,
gleam@list:filter(
Samples,
fun(Sample) -> erlang:element(1, Sample) >= Cutoff end
).
-file("src/pharos/alert.gleam", 106).
-spec mean(list(float())) -> float().
mean(Values) ->
Sum = gleam@list:fold(Values, +0.0, fun(Acc, Value) -> Acc + Value end),
case erlang:float(erlang:length(Values)) of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator -> Sum / Gleam@denominator
end.
-file("src/pharos/alert.gleam", 85).
?DOC(
" Whether the window currently breaches per `spec`, considering only samples\n"
" within `[now - window_ms, now]`. An empty window is never breaching.\n"
).
-spec window_breached(list({integer(), float()}), integer(), window_spec()) -> boolean().
window_breached(Samples, Now, Spec) ->
Values = begin
_pipe = prune_samples(Samples, Now, erlang:element(2, Spec)),
gleam@list:map(_pipe, fun(Sample) -> erlang:element(2, Sample) end)
end,
case Values of
[] ->
false;
_ ->
case erlang:element(4, Spec) of
average ->
mean(Values) > erlang:element(3, Spec);
{percentage, Ratio} ->
Breaching = gleam@list:count(
Values,
fun(Value) -> Value > erlang:element(3, Spec) end
),
(case erlang:float(erlang:length(Values)) of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator -> erlang:float(Breaching) / Gleam@denominator
end) >= Ratio
end
end.