64 lines
2.6 KiB
Text
64 lines
2.6 KiB
Text
-- watcher.wo — Watcher.hx, file for file. One watched log. Service mode
|
|
-- (live=true) reports ALERT/CLEAR transitions continuously; cron mode is
|
|
-- driven by the supervisor via done()/result().
|
|
|
|
-- Watcher.hx:3-7 — the Haxe enum is a tagged union.
|
|
type CronResult = Ok | ErrorFinal | Miss
|
|
-- Ok: completed, last entry not error
|
|
-- ErrorFinal: completed with error as the last entry -> alert
|
|
-- Miss: log never produced content during the window
|
|
|
|
class Watcher {
|
|
-- Haxe `(default, null)` — public read, owner-only write — is `pub(read)`.
|
|
pub(read) path: Text
|
|
pub(read) alerted: Bool = false
|
|
pub(read) saw_content: Bool = false
|
|
|
|
-- The constructor body (Watcher.hx:23-30) is gone: `st` self-initializes
|
|
-- through TailState's field defaults; construction is the brace literal.
|
|
st: TailState = TailState {}
|
|
quiet_ms: Int -- quietPeriod, seconds -> ms
|
|
poll_ms: Int -- pollInterval, seconds -> ms
|
|
live: Bool
|
|
activated_at: Int -- ms
|
|
last_poll_at: Int = 0 -- 0 = never polled; first due() is true
|
|
-- (Haxe used NEGATIVE_INFINITY)
|
|
|
|
fn due(now: Int) -> Bool {
|
|
return now - self.last_poll_at >= self.poll_ms;
|
|
}
|
|
|
|
fn tick(now: Int) {
|
|
self.last_poll_at = now;
|
|
let r = poll(self.path, self.st, now); -- exclusive borrow of st for the call
|
|
if r.new_lines > 0 { self.saw_content = true; }
|
|
if self.live == false { return; }
|
|
-- the alert rule (README): last entry is error and nothing follows
|
|
if self.alerted == false and self.saw_content and self.st.last_level == "error" and now - self.st.last_newline_at >= self.quiet_ms {
|
|
self.alerted = true;
|
|
print("ALERT ${self.path}: last entry is error, quiet for ${self.quiet_ms / 1000}s");
|
|
return;
|
|
}
|
|
if self.alerted and self.st.last_level != "error" {
|
|
self.alerted = false;
|
|
print("CLEAR ${self.path}: new ${self.st.last_level} entries arrived");
|
|
}
|
|
}
|
|
|
|
-- cron mode: the job is judged complete after a quiet period — from the
|
|
-- last write if the log produced content, else from activation (a miss)
|
|
fn done(now: Int) -> Bool {
|
|
if self.saw_content { return now - self.st.last_newline_at >= self.quiet_ms; }
|
|
return now - self.activated_at >= self.quiet_ms;
|
|
}
|
|
|
|
fn result() -> CronResult {
|
|
if self.saw_content == false { return Miss; }
|
|
if self.st.last_level == "error" { return ErrorFinal; }
|
|
return Ok;
|
|
}
|
|
|
|
fn last_level() -> Text {
|
|
return self.st.last_level;
|
|
}
|
|
}
|