Skip to content

WordPress and WooCommerce sites

wp-debuglog-triage

A busy WordPress debug.log hides one fatal error among thousands of repeated notices. This tool groups the log and ranks each problem by source and urgency.

Kind
Open-source tool
Language
JavaScript
License
MIT
Status
First release
  • cli
  • debug-log
  • jev
  • php
  • typesafe
  • wordpress

One fatal error in a log full of notices

On a busy WordPress site, debug.log can grow by thousands of lines a day. Most of them are the same few deprecation notices, and the one fatal error that matters sits somewhere in the middle. wp-debuglog-triage reads the log and prints a short list instead: each problem once, tied to the plugin, theme or core file behind it, and ranked by how soon it needs attention.

Grouping and attribution stay in code

Reading a log is exact work, so code does it. The file is read line by line, so a large log never has to fit in memory, and every entry is parsed: the timestamp, the PHP level (notice, warning, deprecated, fatal error, parse error), WordPress database errors, lines a plugin wrote itself, and stack traces.

Entries that differ only in line numbers, numbers, hex values, hashes or long quoted values form one group. The same warning on line 212 and, after a theme update, on line 215 is one problem, and so are memory errors with different byte counts. Short quoted identifiers stay, so Undefined array key "a" and Undefined array key "b" remain two groups.

Each group is then attributed from its file paths: WordPress core, a plugin, a must-use plugin, a theme, a drop-in such as object-cache.php, wp-config.php, or a PHP file under wp-content/uploads/, which the report flags because PHP should never run from there. A fatal error raised inside core points at the first plugin or theme frame in its stack trace, and "headers already sent" points at the file where the output started. Bedrock's app/plugins layout and Windows paths are recognised too.

Two questions for Jev per group

Jev is TypeSafe AI's System One model: it answers typed questions with probabilities and writes no text. Each group gets two questions:

  • which kind of problem it is: noise, deprecation, warning, fatal, security-relevant or performance, each with a one-line description (running out of memory or time counts as performance, not fatal);
  • how urgent it is, on four levels: can wait, routine maintenance, fix this week, fix now.

The groups go into as few requests as TypeSafe's limits allow (32k tokens for the state plus the longest question, 64k for the whole request), each under its own key in the state. A group whose kind or urgency confidence falls below the threshold (0.8 unless you set --threshold) goes to a review section instead of being ranked as if the answer were certain. Every group is printed with its probabilities, and every word in the report comes from your log or from fixed text in the tool.

TypeSafe's notes on jev-1.13 say accuracy falls as the state fills with detail unrelated to a question, and one request here can hold up to about 80 groups. If the answers look off, --batch sends fewer groups per request.

What leaves your server

Nothing, unless you run it with a key and without --dry-run. Then each request goes to TypeSafe's API and nowhere else, carrying each group's level, its first message (at most 500 characters), up to six stack frames, the phrase naming its source, how often it occurred in words, and whether it was seen in the last 24 hours of the log.

Cleaning happens first, on your machine. Server paths are cut back to the WordPress folder, email addresses and public IP addresses are replaced, URL query strings are masked, and values in SQL queries and string arguments become placeholders. Timestamps, exact counts and the rest of the log are never sent, and --dry-run --json prints the exact body of every request it would send.

Where it stops

  • It sees the log and nothing else. It cannot tell whether a plugin is still active, whether an error has been fixed since, or which PHP version ran.
  • Notices that WordPress raises through _doing_it_wrong() stay with core, because the calling code is not in the log. Database errors without a plugin among the callers stay with core or "unknown".
  • Grouping can merge two different bugs that print the same message from the same file.
  • Log lines can hold text an attacker wrote, such as a URL or a form value, and such text can move an answer. Check every security-relevant group yourself.
  • Up to 20,000 distinct messages are grouped; entries with new messages after that are counted, not grouped. English messages work best.
  • The example report in the repository comes from a synthetic log of 422 invented entries, with hand-written answers in place of Jev: its probabilities show the format, not a live run.

Install and run

It needs Node.js 20 or later and installs no other packages. The tests (25, on Node's test runner) need no key: a fixture stands in for Jev, and any attempt to reach the network fails the test.

npm install -g github:hamzaahmadaslam/wp-debuglog-triage
export TYPESAFE_API_KEY="..."
wp-debuglog-triage wp-content/debug.log --dry-run
wp-debuglog-triage wp-content/debug.log --top 10

A log on another server can come through standard input: ssh example-host cat /srv/site/wp-content/debug.log | wp-debuglog-triage -.

Related