writeonce/docs/examples/log-watcher/watcher.wo

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;
}
}