// Threshold-based alert triggers
///|
pub(all) struct AlertRule {
name : String
min_level : Level
max_count : Int
window_size : Int
}
///|
pub fn AlertRule::new(
name : String,
min_level : Level,
max_count : Int,
window_size : Int,
) -> AlertRule {
AlertRule::{ name, min_level, max_count, window_size }
}
///|
pub fn AlertRule::evaluate(self : AlertRule, logger : Logger) -> Bool {
let count = count_recent(logger, self.min_level, self.window_size)
count > self.max_count
}
///|
fn count_recent(logger : Logger, min_level : Level, window_size : Int) -> Int {
let records = logger.records()
let effective_window = if window_size > 0 { window_size } else { 0 }
let start = if records.length() > effective_window {
records.length() - effective_window
} else {
0
}
count_in_range(records, start, min_level, 0)
}
///|
fn count_in_range(
records : Array[LogRecord],
start : Int,
min_level : Level,
count : Int,
) -> Int {
if start >= records.length() {
count
} else {
let increment = if should_log(records[start].level, min_level) {
1
} else {
0
}
count_in_range(records, start + 1, min_level, count + increment)
}
}
// AlertManager — tracks multiple rules and triggers
///|
pub(all) struct AlertManager {
rules : Array[AlertRule]
}
///|
pub fn AlertManager::new() -> AlertManager {
AlertManager::{ rules: [] }
}
///|
pub fn AlertManager::add_rule(
self : AlertManager,
rule : AlertRule,
) -> AlertManager {
let new_rules = self.rules.copy()
new_rules.push(rule)
AlertManager::{ rules: new_rules }
}
///|
pub fn AlertManager::check_all(
self : AlertManager,
logger : Logger,
) -> (AlertManager, Array[String]) {
let triggered : Array[String] = []
check_rules(self.rules, logger, 0, triggered)
}
///|
fn check_rules(
rules : Array[AlertRule],
logger : Logger,
idx : Int,
triggered : Array[String],
) -> (AlertManager, Array[String]) {
if idx >= rules.length() {
(AlertManager::{ rules, }, triggered)
} else {
if rules[idx].evaluate(logger) {
triggered.push(rules[idx].name)
}
check_rules(rules, logger, idx + 1, triggered)
}
}
// Log summary — quick overview of log activity
///|
pub(all) struct LogSummary {
total : Int
by_level : Array[Int] // [TRACE, DEBUG, INFO, WARN, ERROR]
}
///|
pub fn summarize(logger : Logger) -> LogSummary {
compute_summary(logger.records(), 0, 0, 0, 0, 0, 0, 0)
}
///|
fn compute_summary(
records : Array[LogRecord],
idx : Int,
t : Int,
d : Int,
i : Int,
w : Int,
e : Int,
total : Int,
) -> LogSummary {
if idx >= records.length() {
LogSummary::{ total, by_level: [t, d, i, w, e] }
} else {
let (nt, nd, ni, nw, ne) = match records[idx].level {
Level::TRACE => (t + 1, d, i, w, e)
Level::DEBUG => (t, d + 1, i, w, e)
Level::INFO => (t, d, i + 1, w, e)
Level::WARN => (t, d, i, w + 1, e)
Level::ERROR => (t, d, i, w, e + 1)
}
compute_summary(records, idx + 1, nt, nd, ni, nw, ne, total + 1)
}
}
///|
pub fn LogSummary::error_rate(self : LogSummary) -> Int {
if self.total == 0 {
0
} else {
self.by_level[4] * 100 / self.total
}
}
///|
pub fn LogSummary::warn_rate(self : LogSummary) -> Int {
if self.total == 0 {
0
} else {
self.by_level[3] * 100 / self.total
}
}