Got a frantic call from a client a while back. Their WooCommerce site was throwing a 500 error on the checkout page, but only for some users, some of the time. Total mess. They were losing sales, and every time I checked, the page worked perfectly for me. The server error logs were no help—just generic timeout messages. This is the kind of intermittent bug that makes you want to tear your hair out.
My first move was the standard one: pop open wp-config.php and set WP_DEBUG_LOG to true. I tailed the log file and waited. And yeah, errors started trickling in—a few PHP notices from a badly coded plugin, a warning here and there—but nothing that would explain a fatal 500 error. I was chasing a ghost, and the client was getting impatient. This is a classic case where the standard approach to WordPress error logging just falls flat.
Beyond the Debug Log: A Real WordPress Error Logging Strategy
Look, WP_DEBUG_LOG is fine for local development when you’re actively working on a feature. But for a live production server, it’s a firehose of noise. It doesn’t catch everything, and it certainly doesn’t help you diagnose complex issues that might be caused by resource limits or race conditions. When you’re managing a real business, you need something better. You need to capture everything, including the fatal errors that don’t get logged gracefully.
The real fix is to implement a custom error handler that grabs every single error, warning, and notice, and formats it properly. Here’s the kicker: you use PHP’s register_shutdown_function(). This function runs right before the script execution finishes, which means it can catch fatal errors that other handlers miss. This approach is something I’ve seen refined over the years, building on concepts I first read about on sites like Carl Alexander’s blog.
<?php
/**
* Plugin Name: Centralized Error Catcher
* Description: A simple mu-plugin to catch all PHP errors and log them.
*/
register_shutdown_function( function () {
$error = error_get_last();
if ( ! $error ) {
return;
}
// We only care about the serious stuff for this example
if ( ! in_array( $error['type'], [ E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR ], true ) ) {
return;
}
$log_message = sprintf(
"[%s] PHP Fatal Error: %s in %s on line %d",
date( 'Y-m-d H:i:s' ),
$error['message'],
$error['file'],
$error['line']
);
// Log to a separate, clean file instead of the noisy debug.log
error_log( $log_message . "\n", 3, WP_CONTENT_DIR . '/central-error-log.log' );
} );
So, What’s the Point?
By dropping that into an mu-plugin, I finally caught the error. It turned out a shipping plugin was running out of memory when calculating rates from a third-party API, but only for certain zip codes. The shutdown function caught the memory exhaustion fatal error, logged it cleanly, and pointed me right to the file and line number. Problem solved in five minutes after hours of guesswork.
- Stop Relying on WP_DEBUG_LOG for Production: It’s the wrong tool for the job.
- Catch Fatal Errors: Use
register_shutdown_functionto see the errors that bring your site down. - Log to a Clean File: Don’t mix critical errors with minor PHP notices. Separate your logs for faster diagnostics. Even better, send them to a service API like Sentry or AWS Lambda for processing.
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 real lesson is that professional WordPress development requires professional tools and processes. A simple, robust logging strategy is non-negotiable.
Leave a Reply