-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.