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) |
--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
[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
notifyto 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.
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.60
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| firewatcher-1.60.tar.gz | 52.5 kB | Without provenance | Details for firewatcher-1.60.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| firewatcher-1.60-py3-none-any.whl | Python 3 | none | any | Details for firewatcher-1.60-py3-none-any.whl |
Total release size: 86.4 kB
Release files / firewatcher-1.60.tar.gz
| Download URL | firewatcher-1.60.tar.gz |
|---|---|
| Size | 52.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b0c83253973db0308278bc23f85d15958073bcc13eb55085286a4515c1e1d6f5
|
|
BLAKE2b-256 checksum How to use checksums |
812ae60ff6b3638d53983440c51e2a75184dd075545550b2f4361298bd70abee
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.13.1 {"installer":{"name":"uv","version":"0.13.1","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.60-py3-none-any.whl
| Download URL | firewatcher-1.60-py3-none-any.whl |
|---|---|
| Size | 33.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
4a3c192131d2b7640aae53282074246e49dfce50666c938985dc2c1a0e175221
|
|
BLAKE2b-256 checksum How to use checksums |
a4ad7d1dc3d0dd7873edbc9aea10c1532a146d723cd41f9287bb5a6abb6088f6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.13.1 {"installer":{"name":"uv","version":"0.13.1","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.60
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.