Why You Should be Using WP_DEBUG_LOG in Your WordPress Development

#

Whether you’re a developer, or a site owner troubleshooting a problem with your site, understanding the WP_DEBUG_LOG constant for turning on the WordPress debug log is very important. In this post, I’ll cover how WP_DEBUG_LOG is used, how to find hidden errors, and why we don’t recommend third-party WordPress debugging plugins.

How is WP_DEBUG_LOG Used By WordPress?

Like many things related to WordPress development and debugging, often the best way to understand WP_DEBUG_LOG is to take a peek into the WordPress core codebase.

For WP_DEBUG_LOG, the most important bit is in wp-includes/load.php:

function wp_debug_mode() {
    /**
    * Filters whether to allow the debug mode check to occur.
    *
    * This filter runs before it can be used by plugins. It is designed for
    * non-web run-times. Returning false causes the `WP_DEBUG` and related
    * constants to not be checked and the default PHP values for errors
    * will be used unless you take care to update them yourself.
    *
    * @since 4.6.0
    *
    * @param bool $enable_debug_mode Whether to enable debug mode checks to occur. Default true.
    */
    if ( ! apply_filters( 'enable_wp_debug_mode_checks', true ) ) {
        return;
    }

    if ( WP_DEBUG ) {
        error_reporting( E_ALL );

        if ( WP_DEBUG_DISPLAY ) {
            ini_set( 'display_errors', 1 );
        } elseif ( null !== WP_DEBUG_DISPLAY ) {
            ini_set( 'display_errors', 0 );
        }

        if ( in_array( strtolower( (string) WP_DEBUG_LOG ), array( 'true', '1' ), true ) ) {
            $log_path = WP_CONTENT_DIR . '/debug.log';
        } elseif ( is_string( WP_DEBUG_LOG ) ) {
            $log_path = WP_DEBUG_LOG;
        } else {
            $log_path = false;
        }

        if ( $log_path ) {
            ini_set( 'log_errors', 1 );
            ini_set( 'error_log', $log_path );
        }
    } else {
        error_reporting( E_CORE_ERROR | E_CORE_WARNING | E_COMPILE_ERROR | E_ERROR | E_WARNING | E_PARSE | E_USER_ERROR | E_USER_WARNING | E_RECOVERABLE_ERROR );
    }

    if ( defined( 'XMLRPC_REQUEST' ) || defined( 'REST_REQUEST' ) || ( defined( 'WP_INSTALLING' ) && WP_INSTALLING ) || wp_doing_ajax() || wp_is_json_request() ) {
        ini_set( 'display_errors', 0 );
    }
}

I’ll spare you the line by line detail, but essentially if you set both WP_DEBUG and WP_DEBUG_LOG to true, and set WP_DEBUG_DISPLAY to false, the effect will be that you will get errors logged to a debug.log file in your wp-content folder and no WordPress debug information will be displayed in PHP output (i.e., in your browser).

How to Enable WP_DEBUG_LOG

To enable the WordPress debug log, you add the following to your wp-config.php file:

define( 'WP_DEBUG', true );
define( 'WP_DEBUG_DISPLAY', false );
define( 'WP_DEBUG_LOG', true );

These lines must be added above the /* That's all, stop editing! Happy publishing. */ line in the wp-config.php file in order to work properly.

Even if you’re using WordPress Core installed in a subdirectory like we do, you’ll get a debug.log file in the WP_CONTENT_DIR you configure.

Why Enable WP_DEBUG_LOG

If you have turned on WP_DEBUG but do not explicitly turn off WP_DEBUG_DISPLAY and then turn on WP_DEBUG_LOG, by default, WordPress will display errors on the site.

If you take another look at the wp_debug_mode() function’s code above, the very last block of code makes sure that any errors are not displayed while processing an XML-RPC, WP-REST-API, or admin-ajax.php request. In this day and age of plugins being developed with technologies such as React or Svelte for their frontends, it’s critical that you are able to catch errors during Ajax and REST-API requests to the WordPress backend.

Sure enough, you do not want any errors injected into your WP-REST-API response, it could make quite a mess, but you do need to make sure you catch them. While there are functions provided by WordPress for returning errors in a WP-REST-API response, what if the code “Fatal Errors” without returning a response, or the code doesn’t use those standard functions?

The UI is not a reliable place to be catching WordPress errors. The PHP log file reveals all.

Should you use a WordPress Debugging Plugin?

Maybe you like to use plugins like Debug Bar or Query Monitor to capture errors and are happy with that. I don’t blame you. Those plugins are awesome.

However, I’ve found that they miss some errors. This makes sense when you consider that as they are standard plugins they load later than “Must-use” plugins that may be throwing errors, not to mention WordPress core may throw errors even earlier. Also, last I checked, Strict Standards errors weren’t picked up by these plugins.

As great as these plugins are for other things, I think it’s a mistake to rely upon them to catch all PHP errors. PHP itself is the most reliable source of error reporting, and that’s what you get in debug.log.

How to Monitor debug.log for Errors

It’s one thing if your code is spitting out errors. You can fix those.

But what about third party plugins you have activated? What if they’re puking out warnings and notices all over your UI? Since WP_DEBUG turns on reporting of all errors, you get warnings, notices, strict standards, everything. The kitchen sink. It can be overwhelming and nearly impossible to work with certain plugins activated.

It would be nice to just deactivate the offending plugin, but if it’s critical to what you’re working on, that’s not a solution. Neither is just disabling WP_DEBUG as then you lose sight of your own plugin or theme’s errors.

At least if you’ve added the constants above to your wp-config.php file, all the errors from the third party plugins and your own code will be logged to debug.log, which is much more manageable than if they were displayed on the site.

But how do you monitor the debug.log for errors, and how do you filter out all the noise created by the third party plugins? You don’t want your legitimate errors to get lost in the noise.

Our WP Migrate DB Pro, WP Offload Media and WP Offload SES plugins are careful to try and catch errors and log them with a specific prefix. For example, WP Offload Media uses the AS3CF Error: prefix when outputting error messages (AS3CF is a legacy thing kept for consistency). This means our developers can just tail -f debug.log | grep AS3CF to see WP Offload Media’s error log output.

Unfortunately, that doesn’t work for all errors though, some aren’t caught and therefore don’t get output with a dedicated prefix. And it’s hard to build up a positive grep search filter when you don’t know what unexpected errors might be output, the clue there is “unexpected”. 😉️

Apparently, some people use the Console app in macOS to read the log file and use its filtering to filter out expected errors from third party plugins. Sounds neat, though I haven’t tried it. I’ve solved this in a much more roundabout way.

I wrote a PHP script. It simply monitors a log file (uses tail -f) for changes and dings when there’s an error. It can also filter out whole Xdebug blocks based on regular expressions you define, allowing you to ignore third party plugin warnings and notices. I blogged about it a long time ago and made quite a few refinements since then.

One debug.log For All Sites

I used to have a debug.log for every site I had in my development environment. When I switched to work on a different site, I had to switch to monitoring another log file. Not ideal.

Now I have all my sites configured to log to a single file. To do this, originally I had to use a “Must-use” plugin. I added the following code to wp-content/mu-plugins/custom-debug-log-path.php for each of my sites:

<?php
/**
 * Plugin Name: Custom Debug Log Path
 */

ini_set( 'error_log', '/srv/www/all-debug.log' );

However, since WordPress 5.1 it’s been possible to define WP_DEBUG_LOG with a log file path as its value. So now I just use the following in my wp-config.php file:

define( 'WP_DEBUG', true );
define( 'WP_DEBUG_DISPLAY', false );
define( 'WP_DEBUG_LOG', '/srv/www/all-debug.log' );

Thanks to Bart Kuijper for mentioning this in the comments.

Now I only need to monitor a single log file for any errors in any of my dev sites.

And yes, even if you’re using Windows for local WordPress development, you can specify a log file using the usual disk letter syntax such as 'C:\www\all-debug.log'. As long as PHP can understand the path and has permission to write to it, you’re good to go. Of course my PHP script won’t work on Windows as is.

I should mention that on some hosting services the error_log ini value is effectively hard-wired, so sometimes it doesn’t matter what you enter as a path in WP_DEBUG_LOG, all PHP errors will end up in a system level log file. We see this occasionally in support and have to remind the customer to go look in the system PHP log file if nothing turns up in wp-content/debug.log. It gets very tricky when the path is syslog, as that means a central log server is usually getting the output.

But for me, this setup works exceptionally well. I’m sure there are still some refinements to be made going forward, but I’m happier than ever with my error reporting.

Performance & Security

When you’re working on a local development site, turning on WordPress debug logging is a no-brainer, the marginal performance hit is irrelevant, and there’s no security concerns as the site is not accessible to the public internet. But what about on a production or publicly available staging site, surely you shouldn’t turn on WP_DEBUG_LOG there?

Well, yes, you should, as long as you do it properly. Production sites have errors too, and you should definitely be monitoring them.

On SpinupWP the WordPress debug.log is turned on by default. To address performance issues around large log files, SpinupWP rotates the logs daily, compressing the old logs, with a 14 day retention period. The performance impact is further reduced with a proper cache setup, meaning PHP is executed as little as possible. And for security, the error logs are written to a different non-public location, with Nginx rules set up to deny public access to the symlink that is placed in the default directory for compatibility reasons.

No Excuse

There really isn’t an excuse for not using WP_DEBUG_LOG when developing or testing with WordPress. It doesn’t matter whether you’re working on the next blockbuster plugin, contributing to WordPress Core itself, or working on a client’s site, catching errors is critical.

And I’d strongly recommend using it on production too.

I’ve found having all my WordPress errors written to an error log invaluable while developing, and I know that having WP_DEBUG_LOG turned on by default for SpinupWP sites has helped solve many mysteries for our customers.

Do you use a similar setup for error reporting? Are there any improvements you would suggest?

About the Author

Brad Touesnard

As founder of Delicious Brains Inc, Brad has worn many hats. He now spends most of his time managing the product teams and growing the business. Before starting this company, Brad was a freelance web developer, specializing in front-end development.