A passive log-diagnostics mod for NeoForge (Minecraft 1.21.1). LogWatch watches the session's log stream and writes a human-readable summary of things worth fixing — it never suppresses, filters, or reroutes anything on its own.
It also carries a ported, opt-in log-noise filter for silencing or summarizing known-cosmetic spam from other mods, but that's a separate, explicitly-configured feature (see Log-noise suppression below) — the core mod is observation only.
What it does
- Attaches a plain Log4j2 appender to the root logger that reads every log event and checks it against a set of "known issue" rules (ABI mismatches, registry-key errors, race conditions, and other known compatibility problems).
- Deduplicates matches per rule so a spammy issue shows up once with a count, not once per line.
- Writes
logs/logwatch-report.txt— a clean summary grouped by severity, with an explanation and a suggested action for each rule that matched. - Writes the report on world unload, client disconnect, and JVM shutdown (whichever happens first), so you get a report from a normal quit, a server disconnect, or a crash.
Example report entry
CRITICAL
--------
[nosuchmethoderror-mod-api] x3
Explanation: A class was compiled against a method signature that no longer exists at
runtime - an ABI mismatch between a mod and a hard dependency it links against.
Suggested action: Identify the mod that owns the missing method and either update the
calling mod to a version built against the current dependency version, or pin the
dependency to the older compatible version.
Unique occurrences (1):
- NoSuchMethodError: com.example.SomeMod.someMethod x3 (first seen 2026-01-01T00:00:00Z)
example: java.lang.NoSuchMethodError: com.example.SomeMod.someMethod(...)
Log-noise suppression (optional)
LogWatch also ships dev.marie.logwatch.lognoise — real Log4j2 Filters attached to specific,
named LoggerConfigs (never root) that either drop (DENY) or intercept-and-summarize
(COLLECT) known-noisy log lines from other mods. This is unrelated to the reporting appender
above and only ever acts on loggers explicitly named in a rule.
- DENY rules drop the matching message entirely. Only used for lines confirmed to be purely cosmetic.
- COLLECT rules intercept the message (it never reaches the console or main log either),
count it by a dedup key, and periodically emit one summary line instead of N raw lines. Every
collected raw line is still archived in full to
logs/logwatch-noise.log, so no detail is lost. - A periodic summary of all
COLLECTactivity (plus a warning for any rule that never matched — usually a typo'd logger name) is emitted every 15 minutes, and once more at session end alongside the main report.
Configuration
Both rule sets ship bundled defaults in the jar and can be extended or overridden without a
rebuild, by dropping files into config/logwatch/:
| Rule set | Bundled resource | User override |
|---|---|---|
| Known-issue rules (report) | logwatch-rules.json |
config/logwatch/rules-override.json (replaces bundled rules by matching id), config/logwatch/rules-append.json (adds new rules) |
| Log-noise rules (suppression) | lognoise-rules.json |
config/logwatch/lognoise-rules.json (appended to the bundled rules, never replaces them) |
Neither override file is required to exist.
Rule shape (logwatch-rules.json)
{
"id": "unique-rule-id",
"loggerPattern": "fully.qualified.Logger.Name or *",
"messageRegex": "a Java regex matched against the log message",
"dedupGroup": 1,
"severity": "LOW | MEDIUM | HIGH | CRITICAL",
"explanation": "what this means",
"suggestedAction": "what to do about it"
}
dedupGroup is optional — when set, matches are deduplicated by that regex capture group instead
of the whole message; when null, the whole message is the dedup key.
Rule shape (lognoise-rules.json)
{
"id": "unique-rule-id",
"logger": "exact.Log4j2.Logger.FQN",
"mode": "DENY | COLLECT",
"pattern": "a Java regex matched against the log message",
"dedupGroup": 1,
"description": "what this is and why it's safe to suppress/collect"
}
logger must be the exact Log4j2 logger name an event is logged under, not a package prefix — a
filter is attached to a LoggerConfig for that exact name, and a wrong name means the rule
silently never fires (the periodic "dead rule" warning will flag this, though it won't tell you
the correct name).
Requirements
- Minecraft 1.21.1
- NeoForge 21.1.0+
Installation
Drop the built jar into your mods/ folder alongside NeoForge. No other setup required — default
rules are bundled and work out of the box.
Building from source
./gradlew build
The output jar is written to build/libs/.
License
MIT