Out‑of‑process log watcher for Eagle test runs. Automatically detects hangs, logs progress locally and remotely, and (when needed) terminates stuck test processes. Includes a small cron helper to roll up batch results.
What it is
A tiny set of Eagle/Tcl scripts:
packages/watchCat.eagle— library of integration helpers (Eagle.Testprocedure hooks, automaticwatchCat.tool.eagleexecution, etc).packages/watchCat.library.eagle— library of tool helpers (file change detection, “is log complete?” checks, kill helpers, remote/local logging, etc).tools/watchCat.tool.eagle— the log monitor you run beside your test process. It watches a single log file and a target process; if the log stops changing too long, it declares the run hung and kills the process. It can also periodically “ping” your remote server via Eagle’s test logging.tools/watchCron.eagle— a simple post‑run/cron summarizer: countsOVERALL RESULTlines in a log, finds unique test‑run IDs, and sends a single “TOTALS” record to your remote logger.Ship as Eagle packages:
Eagle.WatchCat 1.0andWatchCat.Library 1.0(pkgIndex provided).
- Catches deadlocks & infinite waits by watching the log file’s mtime. If nothing’s written for > limit seconds, watchCat treats the run as hung.
- Terminates hangs with authority (Windows: uses
isActiveProcess/kill; POSIX: uses systemkill, optionally the whole process group). - Writes clear breadcrumbs both locally (into your test log via
tlog) and remotely (vialogRemoteMessage), including “PING”, “HUNG”, “KILL”, “CRASH”, and “DONE”. - Zero changes to your tests when you already use Eagle’s test harness (
package require Eagle.Test).
watchCat/
├─ packages/
│ ├─ pkgIndex_*.eagle # Declares Eagle.WatchCat & WatchCat.Library
│ ├─ watchCat.eagle # Test Package Integration Procedures (wiring, hooks)
│ └─ watchCat.library.eagle # Tool Library Procedures (internal API)
└─ tools/
├─ watchCat.tool.eagle # Primary CLI tool: watch one log + process
└─ watchCron.eagle # Secondary CLI tool: cron batch roll-up
Packages are recognized as Eagle.WatchCat 1.0 and WatchCat.Library 1.0 via the pkgIndex; WatchCat.Library is loaded with sourceWithInfo depending on Eagle patch level.
- Eagle (the Extensible Adaptable Generalized Logic Engine) with the Eagle.Test package available at runtime. watchCat explicitly requires
Eagle.Test. - POSIX: access to the
killtool (used for liveness checks and termination). Windows: the harness providesisActiveProcess. - Your test suite produces a standard Eagle test log containing lines like
OVERALL RESULT: ...and remote logging summaries; watchCat parses these to decide if the run “completed”.
You have three straightforward options. Pick one:
Add watchCat to your repository under, say, externals/watchCat, and point Eagle’s auto_path at watchCat/packages.
watchCat.tool.eagle will add itself to auto_path from either of these variables, if present:
XDG_WATCHCAT_HOMEXDG_STARTUP_HOME/LoadOnStartup/Public/WatchCat
i.e., you can drop the repository under ${XDG_STARTUP_HOME}/LoadOnStartup/Public/WatchCat and it will be found automatically.
Before sourcing, add the packages path:
lappend auto_path [file join $::env(PROJECT_ROOT) externals watchCat packages]Projects using Eagle the test-suite infrastructure:
# The test-suites that need WatchCat should do the following (at some point):
set ::env(SCRATCH_ROOT) /full/path/to/watchCat/packages
set ::env(XDG_WATCHCAT_HOME) /full/path/to/watchCat/tools
lappend ::auto_path $::env(SCRATCH_ROOT)
package require Eagle.WatchCat
hookGetTestLogForWatchCat true true
###############################################################################
# After this, just [source] your test-suite as usual, e.g. via "all.eagle".
###############################################################################Under the hood watchCat will:
-
Run
watchCat.tool.eagleautomatically, as necessary, i.e. so it can write to your test log and talk to your remote logger. -
Treat the first arg as the log file name and the second as the target process identifier (both validated).
-
Loop forever while the process is alive:
- If a tagged “kill” file appears (
<log>.killProcessor<log>.killProcess<pid>), it will log the event and kill the process immediately. - If the log hasn’t changed and the elapsed time exceeds
limit, it logs “HUNG” locally & remotely and kills the process (optionally the whole process group on POSIX). - If
ping > 0, it periodically PINGs your remote logger with elapsed status.
- If a tagged “kill” file appears (
-
On exit, it verifies the log “looks complete” (i.e., contains a valid
OVERALL RESULTand a successful remote logging line) and logs DONE or CRASH accordingly.
Set these Eagle variables in the watchCat interpreter before it starts looping (for example via a wrapper script that sets variables then sources the tool):
| Variable | Default | Meaning |
|---|---|---|
limit |
3600 seconds (1h) | “Consider it hung” threshold based on log file mtime. (Author has a long “overnight” variant on some hosts.) |
ping |
1200 seconds (20m) | Interval for remote PING logging; 0 disables. |
aggressive |
false |
If true, attempt an extra kill after success to mop up edge cases (e.g., Mono lingering on exit). |
verbose |
false |
Enables additional tracing (ties into enableTracing if available). |
quiet |
true |
Suppresses non‑error output from the tool itself. |
retries |
3 |
Overrides web request retries for remote logging (via Utility.SetWebMaximumRetries). |
Wrapper example (recommended):
# watchCat.wrap.eagle
# Configure first, then source the tool:
set limit 1800 ;# 30 minutes
set ping 600 ;# 10 minutes
set verbose true
set quiet false
# Find watchCat (use XDG_WATCHCAT_HOME if you installed via Option B)
set here [file dirname [info script]]
lappend auto_path [file normalize [file join $here .. packages]]
source [file join $here watchCat.tool.eagle]watchCat checks for special files next to your log (or script) to alter behavior at runtime. Create an empty file with the appropriate suffix to trigger:
| Tag file | Effect |
|---|---|
<logOrScript>.killProcess |
Kill the watched process now (logs event first). |
<logOrScript>.killProcess<pid> |
Same, but only for a matching process identifier. |
<logOrScript>.noKillProcess |
Disable all kill attempts (safety interlock). |
<logOrScript>.noKillHungProcess |
Disable “hung” kill path (diagnostics). |
<logOrScript>.noKillProcessAndSelf |
Disable the experimental “kill both target and self” path (not used). |
These files are checked with a simple
file exists+ tag regex match and are intended as emergency brakes while a run is in progress.
The library considers a log complete only when both are true:
- There’s an
OVERALL RESULT: (SUCCESS|FAILURE|STOP-ON-FAILURE|STOP-ON-LEAK|NONE)line (SUCCESS/FAILURE are “terminal”; others are flagged). - There’s a line confirming remote result logging (OK/ERROR/FAILURE) in the expected format.
It also extracts and reports skipped counts/names if present. All of this is implemented with explicit regex patterns.
Use after a batch of runs (e.g., nightly CI) to send an aggregate “TOTALS” message to your remote logger.
Usage
dotnet exec EagleShell.dll -file tools/watchCron.eagle path/to/nightly.logWhat it does:
- Scans the log for all
OVERALL RESULT:entries, tallies counts forSUCCESS,FAILURE,STOP-ON-FAILURE,STOP-ON-LEAK,NONE, plusUNKNOWN. - Extracts unique test‑run IDs and reports
RUNS=<count>. - Uses
env(BATCH_ID)if set; otherwise tries to extract one from the log (pattern likeBATCH-ID <64-hex>). - Emits a single remote
logRemoteMessage:BATCH BATCH-ID <bid> TOTALS TEST-RUN: host <host> counts <...>.
watchCat assumes the standard Eagle test harness:
- It requires
Eagle.Testso it can usetlog,getTestLog,logRemoteMessage, and friends. It sets::test_logto the watched file and attempts to set up theTEST-RUNtag if your harness exposessetupTestRunTag. - It can trace using
dtraceordebug tracewhen available (verbosemode). - On exit, it hooks
PreInterpreterDisposedto ensure a final cleanup attempt can be made (e.g., logging a late HUNG and trying to kill).
If your remote logging requires specific API keys or server configuration, keep those in your existing Eagle.Test setup; watchCat simply piggy‑backs calls like
logRemoteMessageand will ignore failures viacatch.
If your test launcher puts the run in its own process group, watchCat’s POSIX path can kill the entire group on a HUNG (uses kill -s KILL or a harness‑provided maybeKillProcessGroup).
On Windows, liveness is checked via isActiveProcess, and watchCat uses Eagle’s kill -force command when it must terminate.
-
“Can’t find package WatchCat.Library” Ensure
auto_pathincludeswatchCat/packages, or install underXDG_WATCHCAT_HOME/XDG_STARTUP_HOME/LoadOnStartup/Public/WatchCat. -
“unknown command tlog / logRemoteMessage” Your Eagle.Test package isn’t available. Add
package require Eagle.Testto the runner environment; watchCat itself requires it. -
“It won’t kill my process” Check for safety tags like
.noKillProcess. On POSIX, verify thekilltool exists and that the watchCat process has permission to signal the target. -
“It thinks the run is incomplete” Make sure your harness prints a valid
OVERALL RESULT:line and that the remote logging completion line is present; that’s how completeness is determined. -
Increase verbosity Set
verbose trueandquiet false, and (if present) callenableTracingfrom your harness to get richer traces.
- The repository is brand new. PRs welcome for docs, new heuristics, and portability improvements.
This project is available under the BSD 3‑Clause license. See LICENSE.
watchCat
dotnet exec EagleShell.dll -file tools/watchCat.tool.eagle <fileName> <pid>
- Validates
<fileName>exists and<pid>is an integer.
watchCron
dotnet exec EagleShell.dll -file tools/watchCron.eagle <fileName>
- Validates
<fileName>exists; aggregates results; logs one “BATCH TOTALS” record usingenv(BATCH_ID)or a parsed value.