Got a call from a client last week. Total panic. Their WooCommerce store’s checkout was throwing a 500 error, but only sometimes. And only, it seemed, when a customer was trying to spend a lot of money. The `debug.log` file was a 200MB mess of PHP notices from a dozen different plugins, completely useless. They were losing sales and had no idea why. This is the kind of fire drill that makes you a better developer, fast. We needed a real system for custom error logging in WordPress, not just more noise.
The default WordPress debugging tools are fine for a simple blog, but on a complex e-commerce site, they fall apart. Turning on WP_DEBUG_LOG on a high-traffic site with a few legacy plugins is like trying to hear a whisper in a hurricane. You get thousands of lines of warnings, notices, and deprecation messages burying the one critical fatal error you actually need to see. It’s a total nightmare.
My first thought? Just use the native PHP error_log() function to send the important stuff to a separate file. And yeah, that worked… for about five minutes. The problem is, you’re still just creating another log file. It doesn’t tell you if an error is a one-off glitch or a recurring nightmare that’s happened 5,000 times in the last hour. You still have no way to automatically notify the team when a genuinely *new* issue appears. It was a band-aid on a bullet wound.
A Real-World WordPress Error Logging Fix
The only real fix is to stop passively logging and start actively managing errors. This means intercepting them before WordPress does. You do that with PHP’s set_error_handler() function. It lets you route all PHP errors through a custom function you control. Once you have that control, you can do something smart. Instead of just printing the error, you generate a unique ID for it—a hash based on the error message, the file, and the line number. Then, you store it in a custom database table. This approach was partly inspired by an old post about building an error-tracking app I saw on carlalexander.ca years ago; the core challenges are still the same.
function custom_error_handler($severity, $message, $file, $line) {
if (!(error_reporting() & $severity)) {
return; // This error is not in error_reporting
}
// Create a unique hash for the error type
$error_hash = md5($message . $file . $line);
global $wpdb;
$table_name = $wpdb->prefix . 'app_errors';
$existing_error = $wpdb->get_row(
$wpdb->prepare("SELECT * FROM $table_name WHERE error_hash = %s", $error_hash)
);
if ($existing_error) {
// Recurring error: update count and timestamp
$wpdb->update(
$table_name,
['count' => $existing_error->count + 1, 'last_seen' => current_time('mysql', 1)],
['id' => $existing_error->id]
);
} else {
// New error: insert it and send a notification
$wpdb->insert(
$table_name,
[
'error_hash' => $error_hash,
'message' => $message,
'filename' => $file,
'line_number'=> $line,
'count' => 1,
'first_seen' => current_time('mysql', 1),
'last_seen' => current_time('mysql', 1),
]
);
// You would uncomment this in production
// wp_mail('dev-team@example.com', 'New Production Error', $message);
}
// Stop the standard PHP error handler from running.
return true;
}
set_error_handler('custom_error_handler');
So, What’s the Point?
Here’s the kicker. With this system, you’re not just logging—you’re triaging. You now have a database table of unique errors. You can see which ones are new, which ones happen most often, and you only get one email when a new type of error appears. No more alert fatigue. You’ve turned a firehose of useless data into a targeted list of actionable problems. You’re not guessing anymore; you’re working from a clear, prioritized list. That’s the difference between an amateur and a professional.
Look, this stuff gets complicated fast. If you’re tired of debugging someone else’s mess and just want your site to work, drop my team a line. We’ve probably seen it before.
The question is, are you just logging errors, or are you actually managing them?
Leave a Reply