Skip to main content

firewatcher

Watch a log stream (journald, syslog, or any file) for incident patterns, then persist a window of surrounding messages to a durable location — including a network filesystem — so the record survives even if the machine later dies.

The watcher prefers journalctl --follow when journalctl is on PATH. On hosts without journald it falls back to /var/log/syslog, then /var/log/messages, or --log-file. Running the daemon does not require systemd; only --install-service / --uninstall-service do.

Install

pip install firewatcher

From a clone of this repository:

pip install .

Requires Python 3.6+. No third-party runtime dependencies.

Quick start

firewatcher /etc/firewatcher/patterns.d
firewatcher -t 300 -o /var/log/captured_messages/ /etc/firewatcher/patterns.d

A directory argument loads every non-hidden pattern file inside it. Files ending in .regex are compiled as regular expressions. Any other pattern file is treated as fnmatch (bash-like) substrings, with * added on both ends.

--install-service seeds /etc/firewatcher/patterns.d with example patterns when that directory is empty. The examples/ directory in the source tree is not installed onto PATH by pip install; copy the pattern files from a clone, or let --install-service write them.

On a match, firewatcher writes:

  • a per-incident capture under {output-folder}/{YYYY-MM}/…
  • a line in {output-folder}/journal.log

Live capture is written as lines arrive, so a copy can already be on another filesystem if the host then disappears. Each captured line is flushed as it is written, and the capture file is fsynced at most once a second and when the capture closes, so a host crash loses at most about the last second of it.

systemd service

sudo firewatcher --install-service
sudo firewatcher --print-unit
sudo firewatcher --uninstall-service

--install-service writes /etc/systemd/system/firewatcher.service, creates the output directory, seeds /etc/firewatcher/patterns.d/ with example patterns only if that directory is empty, writes /etc/firewatcher/firewatcher.conf only if that file is missing, then systemctl daemon-reload && systemctl enable --now firewatcher.

If systemd is not available, --install-service and --uninstall-service print a warning and exit. --print-unit still works so you can copy the unit elsewhere.

Useful flags:

Flag Description
--unit-name NAME Unit name (default: firewatcher)
--requires-mounts-for PATH Add RequiresMountsFor= (repeat for NFS/remote output)
--no-enable Write the unit and reload, but do not enable or start it
sudo firewatcher --install-service -o /mnt/logs/captured_messages \
  --requires-mounts-for /mnt/logs/captured_messages \
  /etc/firewatcher/patterns.d

Options

Flag Description
pattern_file … Pattern files or directories
--log-file Log file to follow (default: journalctl / syslog / messages)
-t, --capture-time Seconds of context after a match (default: 300)
-o, --output-folder Where to write captures (default: /var/log/captured_messages/)
--tail_lines Lines to start with (default: 20; +N from line N, not with journalctl)
--filter_only Filter existing logs only (cat instead of tail -F, or journalctl without --follow)
--compress-after-months Tar.xz monthly dirs after N months (default: 3)
--delete-after-months Delete YYYY-MM dirs after N months (default: 0 = never)
--capture_line_count_max Max lines before/after a match (default: 10000). Also caps how many recent lines are held in memory
--config PATH INI config file. Default: /etc/firewatcher/firewatcher.conf when that file exists. An explicit path must exist
--llm / --no-llm Enable or disable LLM analysis of each finished capture
--llm-base-url URL OpenAI-compatible API root, such as http://127.0.0.1:11434/v1
--llm-model NAME Model name
--llm-timeout SEC LLM HTTP timeout (default: 60)
--llm-max-bytes N Capture bytes sent to the model (default: 48000)
--llm-notes TEXT Short note sent to the model with each capture, such as what is normal on this host (default: none)
--notify / --no-notify Enable or disable ntfy pushes
--ntfy-url URL ntfy topic URL, such as https://ntfy.sh/mytopic
--ntfy-priority N ntfy priority 1–5 (default: 4)
--ntfy-timeout SEC Worker ntfy timeout (default: 10). A full analysis queue uses 5 seconds
--install-service Install and enable a systemd unit
--print-unit Print that unit and exit
--uninstall-service Disable and remove the unit
-V, --version Show version and exit

Config file

Runtime settings live in an INI file. Explicit command-line flags override the file. Built-in defaults apply when neither sets a value. A missing default file is ignored. --uninstall-service does not read the file.

A commented example with all supported settings is in examples/firewatcher.conf. Copy it to /etc/firewatcher/firewatcher.conf, or use firewatcher --config /path/to/firewatcher.conf.

[watch]
patterns = /etc/firewatcher/patterns.d
capture_time = 300
output_folder = /var/log/captured_messages/
filter_only = false

[llm]
enabled = false
base_url = http://127.0.0.1:11434/v1
model = llama3.1
timeout = 60
max_bytes = 48000
notes = loop/dm devices from pytest and OOMs in capped test containers are expected

[notify]
enabled = false
ntfy_url = https://ntfy.sh/firewatcher-myhost-REPLACE_ME
priority = 4
timeout = 10

patterns is one path per line, or comma-separated. Positional pattern arguments replace that list.

api_key under [llm] and token under [notify] are not command-line flags. FIREWATCHER_LLM_API_KEY and FIREWATCHER_NTFY_TOKEN override them when set. --install-service creates /etc/firewatcher/firewatcher.conf with mode 0600 only when it is missing, and the unit runs firewatcher --config that path. An existing file is left as it is. Flags passed to a later install are appended on ExecStart so they override the file.

Generated configs include explanatory comments above each setting. When no ntfy URL is supplied, a new config gets https://ntfy.sh/firewatcher-<hostname>-<random>, with a 12-character URL-safe base64 suffix (72 random bits, no padding). The hostname is sanitized and shortened as needed to keep the topic within 64 characters. Explicit URLs are preserved, and reinstalling leaves an existing config and topic unchanged. Notifications still default to off; subscribe to the generated topic in ntfy, then set [notify] enabled = true. When copying the example above, replace its topic with your own.

LLM analysis and notifications

Both features default off.

  • Notifications on, LLM off: every finished capture is pushed. The title is {hostname}: {pattern}.
  • LLM on, notifications off: the model writes a title, summary, and notify decision. The report is appended to the capture file and logged. Nothing is pushed.
  • Both on: a push is sent only when the model sets notify to true.
  • If the LLM request times out, returns an HTTP error, or is not the expected JSON, and notifications are on, firewatcher still pushes, using the generated title.

A capture larger than max_bytes is not sent whole. The model gets the first 2 KB of the file (the header and the oldest lines before the match; at most half of max_bytes), then a window around the matched line: a quarter of the remaining budget before it and the rest from it onward, with a ... line wherever bytes were skipped. A window that reaches either end of the file gives its unused share to the other side. A match during a capture only extends that capture, so the matched lines after the window are sent too, in up to a quarter of the budget, newest first when they do not all fit, followed by the capture's last lines (often a retry that worked or a service back up) and its closing lines (line count, truncation), in up to an eighth of the budget.

notes under [llm] (also accepted as note) is optional free text, empty by default. When set, it is sent with each capture as Operator notes about this host: ..., and the model is told it describes what is normal there. Whitespace and newlines in a note collapse to single spaces.

The capture window closes even when no further log lines arrive. Analysis runs on a background worker so log following stays live. The queue holds four captures. Notify-only jobs run ahead of jobs that still need the model. When the queue is full, that incident skips the model and, if notifications are on, the capture thread sends the generated-title push itself.

Lines firewatcher writes back to the journal are a single physical line containing firewatcher-self, so a summary that quotes panic or I/O error does not start another capture. The capture files themselves stay in the log stream's original format.

Development

pip install -r requirements-dev.txt
pytest
pytest --cov=firewatcher --cov-fail-under=95 --cov-report=term-missing

Author

Yufei Pan (pan@zopyr.us)

License

GPL-3.0-or-later

Metadata

Release files for firewatcher 1.61

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for firewatcher 1.61
File Size Uploaded Provenance Actions
firewatcher-1.61.tar.gz 64.9 kB Without provenance Details for firewatcher-1.61.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for firewatcher 1.61
File Interpreter ABI Platform Actions
firewatcher-1.61-py3-none-any.whl Python 3 none any Details for firewatcher-1.61-py3-none-any.whl

Total release size: 102.6 kB

Release files / firewatcher-1.61.tar.gz

Download URL firewatcher-1.61.tar.gz
Size 64.9 kB
Tags Source
SHA-256 checksum
How to use checksums
0726bf35dbd88a37892b63e2d655fecb24c72a15b4b4d726af31cba5ec5f863e
BLAKE2b-256 checksum
How to use checksums
128375b43840c5c8a6c59eb9856d09c8b7b2293a587d06cce45d0cf255080651
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"13","id":"trixie","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / firewatcher-1.61-py3-none-any.whl

Download URL firewatcher-1.61-py3-none-any.whl
Size 37.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b1fe709aea8de189e00274c0c9907496060d0ca478817561a33f17b6a00fdc22
BLAKE2b-256 checksum
How to use checksums
f988bd743740edaf8dfc83910ead5e37f3cba54229c50159f84e6d4c77ad2dc6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"13","id":"trixie","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Security for firewatcher 1.61

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page