From 7ff4624e67a6452f77d330c84b2ce6aee900b638 Mon Sep 17 00:00:00 2001 From: auric Date: Sun, 22 Feb 2026 22:11:01 -0600 Subject: log_tail: add log_dir + log_pattern directory-watch mode Valve games (TF2, GMod) rotate into a new timestamped log file on every map change. The existing fixed-path inotify watch goes stale after the first rotation. This adds a directory-watch mode that auto-switches to the newest matching file whenever one appears. New YAML fields (mutually exclusive with logs:): log_dir: directory to watch for new log files log_pattern: fnmatch(3) glob for filenames; default "*" Changes: - umbrella.h: add log_dir[MAX_PATH] and log_dir_pattern[MAX_PATH] to Unit - log_tail.c: extend LogWatch with dir_wd/dir_path/pattern fields; add log_tail_drain, log_tail_scan_dir, log_tail_switch_file, log_tail_open_fixed_watch, log_tail_open_dir_watch, log_tail_reopen_fixed, log_tail_handle_rotation_dir; refactor log_tail_init, log_tail_handle, log_tail_cleanup - unit.c: parse log_dir and log_pattern YAML keys; warn and drop logs: if both are set on the same unit - AGENTS.md, README.md: document both log-tail modes Co-Authored-By: Claude Sonnet 4.6 --- AGENTS.md | 45 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 38 insertions(+), 7 deletions(-) (limited to 'AGENTS.md') diff --git a/AGENTS.md b/AGENTS.md index b7a7ee5..0084f21 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -218,18 +218,49 @@ If you add another sequence field, you must do the same. ## Log tail setup (important for operators) -Most game servers create new timestamped log files per session. The -inotify watch is set up once (at start or SIGHUP) against the resolved -real path. The recommended pattern: +Two modes are supported. They are mutually exclusive per unit. + +### Fixed-path mode (`logs:`) + +Point `logs:` at a stable symlink; `log_tail_init` calls `realpath()` to +resolve it to the real inode before opening and watching it. 1. Point unit YAML `logs:` at a stable symlink, e.g. `current.log` 2. Startup script does: `ln -sf /path/to/actual.log /path/to/current.log` -3. SIGHUP umbrella — `log_tail_init` calls `realpath()` to resolve the - symlink to the real file before opening and watching it -4. inotify then watches the actual inode, so `IN_MODIFY` fires correctly +3. SIGHUP umbrella so it re-resolves the symlink Source engine: use `sv_log_onefile 1` so the file is stable within a -session; only update the symlink on server restart. +session; only update the symlink on restarts. + +### Directory-watch mode (`log_dir` + `log_pattern`) + +Valve games (TF2, GMod) create a new timestamped log file on every map +change (`L0222000.log`, `L0222001.log`, ...). Use `log_dir` to watch the +directory and `log_pattern` (fnmatch glob) to filter filenames. Umbrella +auto-switches to the newest matching file whenever one is created. + +```yaml +log_dir: /home/gmod/nnn/gmodds/garrysmod/logs +log_pattern: "L???????.log" +``` + +- `log_pattern` is optional; omitting it defaults to `"*"` (all files) +- `log_dir` and `logs:` are mutually exclusive; if both are set, `logs:` + is ignored with a warning +- Pattern matching uses `fnmatch(3)` — shell-style globs, zero allocation + +#### How it works + +- One inotify instance watches the directory for `IN_CREATE`/`IN_MOVED_TO` +- On each new matching file, `log_tail_scan_dir` finds the newest one + (re-scan handles races where multiple files appear in one event batch) +- `log_tail_switch_file` drains the old file, then opens the new one + from the beginning so no output is missed +- `IN_MOVE_SELF`/`IN_DELETE_SELF` on the current file closes it and + scans for a replacement immediately + +SIGHUP rebuilds everything from scratch via `log_tail_cleanup` + +`log_tail_init`. ## Filter contract -- cgit v1.2.3