Query Monitor https://querymonitor.com The developer tools panel for WordPress and WooCommerce Sat, 28 Mar 2026 16:50:07 GMT https://validator.w3.org/feed/docs/rss2.html https://github.com/jpmonette/feed Copyright (c) 2009-2026, John Blackbourn <![CDATA[Database Queries]]> https://querymonitor.com/wordpress-debugging/database-queries.html https://querymonitor.com/wordpress-debugging/database-queries.html Fri, 27 Mar 2026 02:02:48 GMT Debugging database queries with Query Monitor

The database query panels are where Query Monitor originally earned its name. There are several sub-panels which help you understand and optimise the database queries that WordPress performs during a page load.

All Queries

The main Queries panel shows every individual SQL query that was executed during the page load. For each query you'll see:

  • The SQL statement, colour-coded by type (SELECT, INSERT, UPDATE, DELETE)
  • The caller and the full call stack so you can trace the query to its origin
  • The component responsible (WordPress core, a plugin, or a theme)
  • The number of rows affected or returned
  • The execution time, highlighted if it exceeds the slow query threshold
  • The error message if the query resulted in an error being returned from the dataabse server

You can filter the list by query type to quickly narrow down what you're looking for. The "Non-SELECT" filter is particularly useful for identifying queries that are writing to the database on a page load where you wouldn't expect them.

Queries by Caller

This panel aggregates queries by the function that called them. It shows the total number of queries and the total time for each caller, broken down by query type. This helps you answer the question "which function is responsible for the most database activity?"

Click the caller name to jump to the main Queries panel filtered to that caller.

Queries by Component

The Queries by Component panel is one of the most useful panels for identifying poorly performing plugins or themes. It groups all database queries by the component that triggered them and shows the total count and time for each.

This panel is sorted by total time, so the component with the highest database overhead will appear at the top. If you're trying to figure out why your site is slow, start here.

Duplicate Queries

The Duplicate Queries panel shows SQL queries that were executed more than once during the page load with identical query strings. This is a common source of performance problems -- it often means that a piece of code is running a query inside a loop instead of querying once and caching the result.

For each duplicate query, you'll see the query text, the number of times it was executed, and which callers and components were responsible. The "potential troublemakers" column helps you pinpoint potentially multiple sources of identical queries.

Slow Queries

Queries that exceed a time threshold are shown in the Slow Queries panel. These are the queries that are most likely to be causing performance problems on your site and should be investigated.

Query Errors

If any database query produces an error, the Query Errors panel will appear and show you the full error message alongside the query and its caller. This is invaluable for debugging queries that reference tables or columns that don't exist, or that have syntax errors.

]]>
<![CDATA[Multisite]]> https://querymonitor.com/wordpress-debugging/multisite.html https://querymonitor.com/wordpress-debugging/multisite.html Fri, 27 Mar 2026 02:02:48 GMT Debugging multisite with Query Monitor

The Multisite panel in Query Monitor tracks the blog switching that occurs during a page load on a WordPress multisite installation. Every call to switch_to_blog() and restore_current_blog() is logged so you can see exactly when and why the current site context changes.

What information is shown

For each blog switch, the panel shows calls to switch_to_blog() and restore_current_blog(), which ID was switched from and to, and which function, plugin, theme, or part of WordPress core is responsible.

When is this useful?

Blog switching can be a source of subtle bugs on multisite installations. If a plugin switches to another blog and doesn't properly restore the original blog, it can cause data to be read from or written to the wrong site. This panel helps you spot those problems.

It's also useful for performance debugging. Each switch_to_blog() call flushes parts of the object cache and reinitialises various parts of WordPress for the target site, which has a performance cost, although notably this cost is significantly less in newer versions of WordPress than it was historically. If you see a large number of switches on a single page load, there may be an opportunity to optimise.

]]>
<![CDATA[How to use Query Monitor]]> https://querymonitor.com/wordpress-debugging/how-to-use.html https://querymonitor.com/wordpress-debugging/how-to-use.html Thu, 19 Mar 2026 16:24:22 GMT How to use Query Monitor

Installation

Install and activate Query Monitor as you would any other plugin for WordPress.

Alternatively, download from GitHub or install via Composer.

Usage

While you're logged in as an Administrator you'll see a new menu in the admin toolbar:

Admin Toolbar Menu

The numbers at the top show, in order:

  1. Page generation time in seconds
  2. Peak memory usage
  3. Total time taken by SQL queries in seconds
  4. Total number of SQL queries

All of the information shown by Query Monitor is for the current page load. Historical information is not available (although this feature is planned for a future version).

Click the top of the menu to open the Overview panel, or click any menu item to open its corresponding panel.

Initially you'll probably be most interested in the Queries panel, which shows all of the database queries that were performed during the page load and allows you to filter and sort them and determine which component was responsible for each query.

Debugging a Slow Site

So your site is slow and you've decided to install Query Monitor in order to identify the cause. There are a few panels that you should look at first.

1. Queries by Component

The Queries → Queries by Component panel shows you aggregate information about the database queries that were executed by each plugin and theme during the page load. This is a good way to identify poorly performing themes or plugins.

This panel is sorted by the total time taken for all the queries executed by each component. A plugin that performs a high number of queries, or performs queries that are slow, will contribute to the time that your site takes to load.

Aggregate Database Queries by Component

2. HTTP API Calls

The HTTP API Calls panel shows you information about the server-side HTTP requests that were performed during the page load. These are usually "invisible" causes of a slow site, and can occur sporadically.

If a plugin or theme regularly triggers an HTTP API call during the loading of a page, this will increase the time your site takes to load.

HTTP API Requests

3. Object Cache

The Overview panel shows you information about several aspects of your site, one of which is the object cache. If you don't have a persistent object caching plugin active then Query Monitor will show you a message.

A persistent object cache plugin greatly improves performance by caching the result of operations such as database queries, HTTP API calls, and other slow operations. By default WordPress uses a non-persistent object cache, which means repeated operations within the same page load get cached but repeated operations across page loads don't.

If you see the message "External object cache not in use" then you should install an object caching plugin for a driver such as Redis or Memcached which will allow the object cache to persist across page loads and greatly improve the performance of your site. Speak to your hosting provider if you need help with this.

4. PHP Errors

The PHP Errors panel will appear only if some code that executed during the loading of the page caused a PHP error, such as a notice or a warning. If you see a red or orange highlight in Query Monitor's admin toolbar menu, this means an error occurred and you should investigate it.

Code that triggers a Warning means the code is not operating as expected and may be causing broken behaviour on your site. You should investigate warnings straight away. In addition, warnings can increase the time your site takes to load because each error gets logged by your server.

Code that triggers a Notice is less critical but should still be investigated as it can be indicative of poorly written code. Notices may or may not be logged by your server depending on its configuration.

5. Scripts and Styles

If your site loads many JavaScript or CSS files it won't necessarily slow down the page generation time on your server but it will slow down the page load time for your visitors.

The Scripts and Styles panels show you which files have been enqueued via the WordPress dependency system. If these panels show a large number of files you should consider installing a plugin which minifies and combines them.

Everything Else

Despite its name, Query Monitor includes a large amount of functionality unrelated to database queries. For example in the Request panel you can see information about rewrite rules and query variables; in the Template panel you can see information about the theme template hierarchy and template parts; and in the Environment panel you can see configuration settings and information about your server.

Click through each of the panels and hopefully you'll find something interesting!

]]>
<![CDATA[Request]]> https://querymonitor.com/wordpress-debugging/request.html https://querymonitor.com/wordpress-debugging/request.html Wed, 18 Mar 2026 23:04:35 GMT Debugging the request with Query Monitor

The Request panel in Query Monitor shows you detailed information about how WordPress handled the current request. This useful for debugging URL routing, rewrite rules, and query variables.

What information is shown

The full URL of the current request is shown along with the regular expression that matched the request URL, the internal query string that WordPress derived from the rewrite rule, and the parsed query variables which ultimately determine what content WordPress displays.

If multiple rewrite rules match the current URL, you'll see all of them. WordPress uses the first match, but seeing the alternatives helps when debugging rewrite rule conflicts.

The panel also shows the queried object for the current request, basic information about the currently authenticated user, and site and network information when Multisite is in use.

When is this useful?

  • Debugging custom rewrite rules that aren't matching as expected
  • Verifying which query variables WordPress is using for a given URL
  • Confirming that a URL resolves to the correct post or taxonomy term
  • Understanding why a request is returning a 404 when you expect it to match
]]>
<![CDATA[Scripts and Styles]]> https://querymonitor.com/wordpress-debugging/scripts-and-styles.html https://querymonitor.com/wordpress-debugging/scripts-and-styles.html Wed, 18 Mar 2026 23:04:35 GMT Debugging enqueued scripts and styles with Query Monitor

The Scripts and Styles panels in Query Monitor show you all of the JavaScript and CSS files that have been enqueued on the current page via the WordPress dependency system.

Why is this useful?

If your site loads a large number of JavaScript or CSS files it won't necessarily affect the page generation time on your server, but it will slow down the page load time for your visitors. These panels help you see exactly which assets are being loaded and which plugins or themes are responsible.

What information is shown

For each enqueued asset, the panels show the asset name, URL, dependencies, dependents, and which plugin, theme, or part of WordPress core registered the asset.

This panel will also highlight assets with problems such as missing dependencies, which are otherwise hard to debug as WordPress silently discards these assets.

Identifying problems

A good use for this panel is to identify plugins that are loading their assets on every page of your site when they should only be loaded where needed.

]]>
<![CDATA[Transients]]> https://querymonitor.com/wordpress-debugging/transients.html https://querymonitor.com/wordpress-debugging/transients.html Wed, 18 Mar 2026 23:04:35 GMT Debugging transients with Query Monitor

The Transients panel in Query Monitor shows you all of the transients that were written to during the current page load.

What information is shown

For each transient that was updated, the panel shows the transient name, its expiry, its approximate size, and which function, plugin, theme, or part of WordPress core set the transient.

When is this useful?

Transients should typically be set infrequently, and preferably not on the front end of the site. If you see transients being set on every page load, that's a sign that something is wrong. Either the transient is being set unnecessarily, or it's expiring immediately and being regenerated every time.

This panel is also helpful for understanding which plugins are storing data in transients and how much data they're storing.

]]>
<![CDATA[Admin Screen]]> https://querymonitor.com/wordpress-debugging/admin-screen.html https://querymonitor.com/wordpress-debugging/admin-screen.html Tue, 17 Mar 2026 23:09:36 GMT Debugging the admin screen with Query Monitor

The Admin Screen panel in Query Monitor is available when you're viewing a page in the WordPress admin area. It shows you information about the current admin screen and some useful global variables.

What information is shown

Current screen properties

The panel shows the properties of the current WP_Screen object as returned by get_current_screen(). This includes the screen ID, base, post type, taxonomy, and other properties that are often needed when you're targeting a specific admin screen with your code.

Global variables

The current values of the admin-related global variables are shown:

  • $pagenow
  • $typenow
  • $taxnow
  • $hook_suffix

List table information

If the current admin screen uses a list table (for example the Posts or Users screen), the panel helpfully shows the list table class name and the filter names for columns, sortable columns, and the column action. This saves you from having to dig through WordPress core to find the right filter name.

When is this useful?

  • Finding the correct screen ID to use with add_meta_box() or add_screen_option()
  • Determining the right hook suffix for admin_print_scripts-{$hook_suffix} or similar hooks
  • Looking up the correct filter name for customising list table columns
]]>
<![CDATA[Blocks]]> https://querymonitor.com/wordpress-debugging/blocks.html https://querymonitor.com/wordpress-debugging/blocks.html Tue, 17 Mar 2026 23:09:36 GMT Debugging blocks with Query Monitor

The Blocks panel in Query Monitor lists all of the blocks on the current page, along with their attributes and other debugging information. Nested blocks (for example for groups or columns) are fully supported.

Screenshot of the Blocks panel in Query Monitor

If you're developing dynamic blocks, the block render callback and render timing information will help you keep an eye on performance that can otherwise be difficult to measure.

Nested blocks are fully supported and display hierarchical numbering so you can see exactly how the block tree is structured. This is helpful when working with layouts using Group, Columns, or custom container blocks.

When is this useful?

  • Verifying that your block attributes are being saved and passed through correctly
  • Debugging nested block structures that aren't rendering as expected
  • Identifying slow dynamic blocks that are contributing to page generation time
  • Checking which render callback is being used for a dynamic block
]]>
<![CDATA[Conditionals]]> https://querymonitor.com/wordpress-debugging/conditionals.html https://querymonitor.com/wordpress-debugging/conditionals.html Tue, 17 Mar 2026 23:09:36 GMT Debugging conditional tags with Query Monitor

WordPress has a large number of conditional tags such as is_single(), is_home(), and is_admin(). These are used extensively in themes and plugins to determine what type of request is being handled, but it's not always obvious which conditionals are true for a given page.

The Conditionals panel in Query Monitor shows you the result of all of the main WordPress conditional functions for the current page load. True conditionals are highlighted so you can see at a glance what WordPress thinks the current request is.

When is this useful?

  • Figuring out which conditional to use in your theme or plugin for a particular page
  • Debugging template logic that isn't behaving as expected because a conditional returns a different value than you assumed
  • Quickly confirming the request type without adding var_dump() calls to your code
]]>
<![CDATA[Doing it Wrong]]> https://querymonitor.com/wordpress-debugging/doing-it-wrong.html https://querymonitor.com/wordpress-debugging/doing-it-wrong.html Tue, 17 Mar 2026 23:09:36 GMT Debugging "Doing it Wrong" notices with Query Monitor

WordPress has an internal function called _doing_it_wrong() which is triggered when code uses the WordPress API incorrectly. These notices can easily go unnoticed. Query Monitor surfaces them.

What information is shown

The Doing it Wrong panel shows the explanation of what's being done incorrectly, which function or method triggered the notice, and which plugin, theme, or part of WordPress core was responsible.

When is this useful?

These notices are WordPress telling you that your code (or a plugin's code) is not following the expected API conventions. Common examples include:

  • Calling a function too early in the WordPress loading process
  • Using a deprecated argument in a function call
  • Registering something incorrectly, such as a post type or taxonomy
]]>
<![CDATA[Environment]]> https://querymonitor.com/wordpress-debugging/environment.html https://querymonitor.com/wordpress-debugging/environment.html Tue, 17 Mar 2026 23:09:36 GMT Viewing environment information with Query Monitor

The Environment panel in Query Monitor gives you an overview of the server and software configuration for your WordPress site. This saves you from having to dig through phpinfo() output or SSH into your server.

What information is shown

The panel is divided into sections:

PHP

Key PHP configuration values including the version number, memory limit, error reporting level, extensions, and other settings that affect how PHP runs.

Database

Your database server type, version, host, user, and various other configuration values.

WordPress

The WordPress version, environment information, and various WordPress-level configuration values.

Server

Information about the HTTP server software (Apache, Nginx, etc.) and the operating system.

When is this useful?

  • Checking the PHP version or memory limit without needing server access
  • Verifying that required PHP extensions are loaded
  • Confirming database server details when debugging query issues
]]>
<![CDATA[Headers]]> https://querymonitor.com/wordpress-debugging/headers.html https://querymonitor.com/wordpress-debugging/headers.html Tue, 17 Mar 2026 23:09:36 GMT Debugging HTTP headers with Query Monitor

The Headers panels in Query Monitor show you the HTTP request headers sent by the browser and the response headers sent by your server for the current page load.

Request Headers

The Request Headers panel shows all of the HTTP headers that the browser sent with the request. This includes headers like Accept, User-Agent, Cookie, and any custom headers. This can be useful when debugging caching behaviour, content negotiation, or authentication.

Response Headers

The Response Headers panel shows all of the HTTP headers that your server sent back in the response. This includes headers like Content-Type, Cache-Control, X-Powered-By, and any custom headers added by WordPress, plugins, or your server configuration.

When is this useful?

  • Debugging caching issues by checking Cache-Control, Expires, and ETag headers
  • Verifying that security headers like X-Content-Type-Options or Content-Security-Policy are being sent
  • Checking redirect-related headers
  • Confirming that a plugin or server configuration is setting the headers you expect
]]>
<![CDATA[Hooks]]> https://querymonitor.com/wordpress-debugging/hooks.html https://querymonitor.com/wordpress-debugging/hooks.html Tue, 17 Mar 2026 23:09:36 GMT Debugging WordPress hooks, actions, and filters with Query Monitor

The Hooks panel in Query Monitor shows you every action and filter that fired during the page load, along with all the callbacks attached to each one.

What information is shown

For each hook, the panel lists the name of the action or filter, the function, method, or closure attached at each priority, and which plugin, theme, or part of WordPress core is responsible for the callback.

This is useful for understanding the order of execution on a page and for finding out which code is hooked onto a particular action or filter.

Finding what's hooked where

If you're trying to figure out why something is behaving unexpectedly, the Hooks panel lets you search for a specific hook name and see everything that's attached to it. This is often faster than searching through plugin source code.

You can also filter by component to see all the hooks that a particular plugin is using.

The all hook

If something is hooked onto the special all hook, Query Monitor will warn you about it. The all hook fires for every single action and filter in WordPress and has a significant performance cost. It's sometimes used for debugging but should never be present in production.

Many of Query Monitor's panels also include a sub-menu which shows hooks related to that panel's functionality. Read more about related hooks.

]]>
<![CDATA[PHP Errors]]> https://querymonitor.com/wordpress-debugging/php-errors.html https://querymonitor.com/wordpress-debugging/php-errors.html Tue, 17 Mar 2026 23:09:36 GMT Debugging PHP errors with Query Monitor

The PHP Errors panel appears in Query Monitor whenever PHP code triggers an error, warning, notice, or deprecation during the page load. If you see a bright red or orange highlight in Query Monitor's admin toolbar, this is usually why.

What information is shown

For each PHP error, the panel shows the error message, error level, the caller, the component responsible (plugin, theme, or WordPress core), and the number of times the error occurred during the page load if one error is triggered multiple times.

Errors are aggregated by their unique call point, so if the same error occurs 50 times during a page load you'll see it once with a count of 50 rather than 50 separate entries.

Why you should care

Warnings and notices don't just indicate code quality problems. Each error can get logged by your server (depending on its configuration), which adds overhead to every page load. A plugin that generates errors can measurably slow down your site.

]]>
<![CDATA[Add-on plugins]]> https://querymonitor.com/help/add-on-plugins.html https://querymonitor.com/help/add-on-plugins.html Thu, 28 Aug 2025 10:20:14 GMT Query Monitor add-on plugins

Third-party plugins that extend Query Monitor

Debug Bar add-ons

Query Monitor also supports all Debug Bar add-on plugins. You can deactivate the core Debug Bar plugin, and Query Monitor will take over the display of any add-ons you have installed.

]]>
<![CDATA[DebuggingGuzzleHTTPRequests]]> https://querymonitor.com/wordpress-debugging/guzzle-http-requests.html https://querymonitor.com/wordpress-debugging/guzzle-http-requests.html Wed, 23 Jul 2025 17:49:52 GMT Debugging Guzzle HTTP Requests

New

This feature is new in Query Monitor 3.19.0

Query Monitor can log HTTP requests made with the Guzzle HTTP client library, a popular PHP HTTP client used by many WordPress plugins and applications. Since Guzzle bypasses WordPress's HTTP API, these requests don't normally appear in Query Monitor's HTTP panel. The Guzzle middleware solves this problem.

Basic Usage

To log Guzzle requests in Query Monitor, add the middleware to your Guzzle client:

php
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Client;

// Create a handler stack with Query Monitor middleware
$stack = HandlerStack::create();
$stack->push( QM_Collector_HTTP::guzzle_middleware() );

// Create your Guzzle client
$client = new Client( [ 'handler' => $stack ] );

// Make requests as normal - they'll appear in Query Monitor
$response = $client->get( 'https://api.example.com/users' );
$response = $client->post( 'https://api.example.com/data', [
    'json' => [ 'key' => 'value' ]
] );

Advanced Examples

Using with Existing Handler Stack

If you already have a custom handler stack, simply add the Query Monitor middleware:

php
$stack = HandlerStack::create();
$stack->push( Middleware::retry( /* retry config */ ) );
$stack->push( QM_Collector_HTTP::guzzle_middleware() ); // Add QM middleware
$stack->push( Middleware::log( $logger, $formatter ) );

$client = new Client( [ 'handler' => $stack ] );

Plugin Integration

For WordPress plugins using Guzzle, you can conditionally add the middleware only when Query Monitor is available:

php
class MyPlugin {
    private function createHttpClient() {
        $stack = HandlerStack::create();

        // Add Query Monitor middleware if available
        if ( method_exists( 'QM_Collector_HTTP', 'guzzle_middleware' ) ) {
            $stack->push( QM_Collector_HTTP::guzzle_middleware() );
        }

        return new Client( [
            'handler' => $stack,
            'timeout' => 30,
            'base_uri' => 'https://api.example.com/'
        ] );
    }
}

What Information is Captured

When using the Guzzle middleware, Query Monitor captures the same detailed information as WordPress HTTP API requests:

Request Information

  • URL - The complete request URL
  • Method - HTTP method (GET, POST, PUT, DELETE, etc.)
  • Headers - All request headers
  • Body - Request body content
  • Timeout - Request timeout setting
  • SSL Settings - Certificate verification settings

Response Information

  • Status Code - HTTP response code (200, 404, 500, etc.)
  • Headers - All response headers
  • Body - Response content
  • Size - Response size in bytes

Performance & Debugging

  • Execution Time - How long the request took
  • Stack Trace - Which code made the request
  • Component - Which plugin/theme triggered the request
  • Errors - Any exceptions or failed requests

Error Handling

Failed Guzzle requests are properly handled and displayed:

php
try {
    $response = $client->get( 'https://api.example.com/nonexistent' );
} catch ( GuzzleHttp\Exception\ClientException $e ) {
    // Exception message will still be shown in Query Monitor
}
]]>
<![CDATA[REST API requests]]> https://querymonitor.com/wordpress-debugging/rest-api-requests.html https://querymonitor.com/wordpress-debugging/rest-api-requests.html Wed, 23 Apr 2025 14:21:06 GMT Debugging WordPress REST API requests with Query Monitor

Query Monitor includes a feature which allows you to see comprehensive performance information about a REST API request on your site.

Authentication

Just like requests to the front end or the admin area of your site, in order to see debugging information for the REST API you need to perform a request which is authenticated as a user who has permission to view Query Monitor’s output, for example an Administrator.

  • This usually means including a valid _wpnonce parameter in the URL, the value of which you can get by visiting wp-admin/admin-ajax.php?action=rest-nonce
  • Alternatively you can pass an Application Password if you’re using WordPress 5.6 or later

Overview and PHP error information

The following additional HTTP headers will be included in the response:

  • x-qm-overview-time-taken – Response generation time in seconds
  • x-qm-overview-time-usage – Response generation time as a percentage of PHP’s max execution time limit
  • x-qm-overview-memory – Memory usage in kB
  • x-qm-overview-memory-usage – Memory usage as a percentage of PHP’s memory limit
  • x-qm-php-errors-count – Number of PHP errors that occurred (0 or more)
  • x-qm-php-errors-error-{n} – Details about each individual PHP error

Full performance and debugging information

When a REST API request is performed which requests an enveloped response via the ?_envelope parameter, an additional qm property will be present in the JSON response with information about:

  • qm.db_queries.dbs – All database queries
  • qm.db_queries.dupes – Duplicate database queries
  • qm.db_queries.errors – Database queries with errors
  • qm.cache – Object cache stats for hits and misses
  • qm.http – HTTP API requests and response details
  • qm.logger – Logged messages and variables
  • qm.transients – Updated transients

The information is somewhat trimmed down from the information that you would see in the main Query Monitor panel for a regular HTML request, but it contains the key information that you need to investigate performance issues.

The qm.db_queries property contains overview information as well as full details for each individual SQL query, including timing, stack traces, and returned rows, and the qm.http property includes details about the each requested URL, response code, timing, and stack traces.

Example data

Given a GET request to a default endpoint such as example.com/wp-json/wp/v2/posts/?_envelope&_wpnonce=<nonce>, you would not typically expect to see server-side HTTP API requests or transients being updated on every request, but QM will now expose this information so you can investigate!

Here’s an example of the qm property in a response:

json
{
  "db_queries": {
    "dbs": {
      "$wpdb": {
        "total": 15,
        "time": 0.0108,
        "queries": [
          {
            "sql": "SELECT option_name, option_value FROM wp_options WHERE autoload = 'yes'",
            "time": 0.0011,
            "stack": [
              "wp_load_alloptions()",
              "is_blog_installed()",
              "wp_not_installed()"
            ],
            "result": 317
          },
          {
            "sql": "SELECT * FROM wp_users WHERE ID = '1' LIMIT 1",
            "time": 0.0003,
            "stack": [
              "WP_User::get_data_by()",
              "WP_User->__construct()",
              "wp_set_current_user()",
              "_wp_get_current_user()",
              "wp_get_current_user()",
              "get_current_user_id()",
              "get_user_option()",
              "Classic_Editor::get_settings()",
              "Classic_Editor::init_actions()",
              "do_action('plugins_loaded')"
            ],
            "result": 1
          },
          {
            "sql": "SELECT wp_posts.ID FROM wp_posts WHERE 1=1  AND wp_posts.post_type = 'post' AND ((wp_posts.post_status = 'publish')) ORDER BY wp_posts.post_date DESC LIMIT 0, 5",
            "time": 0.0003,
            "stack": [
              "WP_Query->get_posts()",
              "WP_Query->query()",
              "get_posts()",
              "DoubleUnderscore\\entrypoint()",
              "do_action('init')"
            ],
            "result": 5
          }
        ]
      }
    },
    "errors": {
      "total": 1,
      "errors": [
        {
          "caller": "do_action('init')",
          "caller_name": "do_action('init')",
          "sql": "SELECT * FROM table_that_does_not_exist",
          "ltime": 0,
          "result": {
            "errors": {
              "1146": [
                "Table 'wp.table_that_does_not_exist' doesn't exist"
              ]
            }
          }
        }
      ]
    },
    "dupes": {
      "total": 1,
      "queries": {
        "SELECT wp_posts.ID FROM wp_posts WHERE 1=1 AND wp_posts.post_type = 'post' AND ((wp_posts.post_status = 'publish')) ORDER BY wp_posts.post_date DESC LIMIT 0, 5": [
          3,
          14,
          35
        ]
      }
    }
  },
  "cache": {
    "hit_percentage": 67.8,
    "hits": 931,
    "misses": 442
  },
  "http": {
    "total": 1,
    "time": 0.6586,
    "requests": [
      {
        "url": "https://example.org",
        "method": "GET",
        "response": {
          "code": 200,
          "message": "OK"
        },
        "time": 0.6586,
        "stack": [
          "WP_Http->request()",
          "WP_Http->get()",
          "wp_remote_get()",
          "DoubleUnderscore\\entrypoint()",
          "do_action('init')"
        ]
      }
    ]
  },
  "logger": {
    "warning": [
      {
        "message": "Preloading was not found, generating fresh",
        "stack": [
          "DoubleUnderscore\\dispatcher()",
          "DoubleUnderscore\\entrypoint()",
          "do_action('init')"
        ]
      }
    ],
    "debug": [
      {
        "message": "Language: en_US",
        "stack": [
          "DoubleUnderscore\\do_logs()",
          "DoubleUnderscore\\entrypoint()",
          "do_action('init')"
        ]
      }
    ]
  }
}
]]>
<![CDATA[Clickable stack traces]]> https://querymonitor.com/help/clickable-stack-traces-and-function-names.html https://querymonitor.com/help/clickable-stack-traces-and-function-names.html Mon, 03 Feb 2025 00:25:57 GMT Clickable stack traces and function names in Query Monitor

Many panels in Query Monitor display function names or stack traces. Wouldn't it be great if you could click the function name and the file opens up in your text editor or IDE at the correct position? With the clickable file links feature you can, and you'll wonder how you lived without it:

Screenshot of clickable function names in Query Monitor

You just need to open up the Settings panel in Query Monitor (click the cog next to the Close icon) and choose your editor in the "Editor" section. That's it!

Screenshot of the Editor setting in Query Monitor

If you use one of the following editors you may need to configure its URL scheme handler:

Remote File Path Mapping

If you're debugging a remote site or using Docker or a virtual machine, you'll need to map the path on the server to its path on your local machine so your editor doesn't try to load a non-existent file. You can do this using a filter on the qm/output/file_path_map hook which accepts an array of remote paths and the local path they map to.

Here are examples for various local development environments:

Altis local server

No need to do anything, the path mapping is handled automatically.

WordPress core development environment

No need to do anything, the path mapping is handled automatically.

WordPress VIP on Lando

No need to do anything, the path mapping is handled automatically.

VVV

php
add_filter( 'qm/output/file_path_map', function( $map ) {
	$map['/srv/'] = '/path/to/vvv/';
	return $map;
} );

Chassis or another Vagrant-based VM

php
add_filter( 'qm/output/file_path_map', function( $map ) {
	$map['/vagrant/'] = '/path/to/local/project/';
	return $map;
} );

If you've changed the paths configuration in Chassis, you may need to map the /chassis directory instead:

php
add_filter( 'qm/output/file_path_map', function( $map ) {
	$map['/chassis/'] = '/path/to/local/project/';
	return $map;
} );

Xdebug

Xdebug supports an xdebug.file_link_format configuration directive. Query Monitor will use this as the default value for the file link format if it's present.

Customisation

You can use the qm/output/file_link_format filter to override the file link format. If this filter is in use then the editor selection control will be hidden.

That's it!

I hope you enjoy clicking on your newly-clickable file links.

]]>
<![CDATA[db.php symlink]]> https://querymonitor.com/help/db-php-symlink.html https://querymonitor.com/help/db-php-symlink.html Wed, 27 Nov 2024 14:08:44 GMT db.php symlink

In addition to the main plugin files, Query Monitor includes a file named db.php which gets symlinked into your wp-content directory when the plugin is activated. This special file is a WordPress dropin plugin and it allows Query Monitor to provide extended functionality such as the result count, full stack trace, and error detection for all database queries.

Occasionally the PHP process won't be able to put this symlink in place. Some common causes are:

  • The file permissions of the wp-content directory means it isn't writable by PHP
  • Another wp-content/db.php file is already in place
  • Files for the site were copied from elsewhere (eg. during a migration from another hosting provider) and the existing symlink no longer points to a valid location

Query Monitor will still work fine in this situation but you won't see extended information that makes Query Monitor much more useful.

In this situation you can create the symlink manually using one of the methods below.

Relax the file permissions

Relax the file permissions on the wp-content directory so it's writable by the PHP process, then de-activate and re-activate Query Monitor and it'll attempt to create the symlink again.

Use WP-CLI

Query Monitor includes a WP-CLI command for putting the symlink into place:

wp qm enable

Use the command line

If you don't have access to WP-CLI you can run a command to create the symlink manually:

macOS / Linux:

ln -s /path/to/wordpress/wp-content/plugins/query-monitor/wp-content/db.php /path/to/wordpress/wp-content/db.php

Windows (requires administrator privileges):

mklink C:\path\to\wordpress\wp-content\db.php C:\path\to\wordpress\wp-content\plugins\query-monitor\wp-content\db.php

Via your hosting control panel

If you're unable to do any of the above you should be able to use your web hosting control panel (such as Plesk or cPanel) to create the symlink. Contact your web host if you're unsure.

When an existing db.php file is already in place

The db.php file will sometimes conflict with another plugin that also uses a db.php file. Such plugins include:

  • W3 Total Cache
  • LudicrousDB
  • HyperDB
  • SQLite Database Integration

There is nothing that can be done about this. This a WordPress core limitation due to the fact that the dropin plugin file must live at wp-content/db.php and only one file can exist there.

]]>
<![CDATA[Assertions]]> https://querymonitor.com/wordpress-debugging/assertions.html https://querymonitor.com/wordpress-debugging/assertions.html Tue, 02 Apr 2024 19:56:56 GMT Performing assertions in Query Monitor

New

This feature is new in Query Monitor 3.15

Query Monitor allows developers to perform assertions which will log an error in the Logs panel in Query Monitor when they fail. This is a convenience wrapper around the logging feature which allows you to get alerted to problems without performing conditional logic.

Here's what assertions look like in the Logs panel:

Query Monitor's Logging Panel

Let's take a look at how to use this feature and what it's useful for.

Basic usage

php
do_action( 'qm/assert', $value === 5 );
do_action( 'qm/assert', $value === 5, 'Value is 5' );
do_action( 'qm/assert', $value === 5, 'Value is 5', $value );

The qm/assert action accepts an assertion value as its first parameter which you'll usually provide in the form of an expression. This should be a boolean true or false value, although technically anything truthy or falsey is accepted.

If the assertion fails then Query Monitor will show an error in the Logs panel, which in turn causes a red warning to appear in the admin toolbar so you get notified about the failure. If the assertion passes then a debug level message will be shown in the Logs panel, which helps you confirm that your assertion is being executed.

The second parameter is an optional short description of the assertion. If provided, this will be shown along with the assertion failure or pass message.

The third parameter is an optional value of any type that will get output below the error message if the assertion fails. This is useful for debugging an unexpected value.

WARNING

Be careful not to log very large values such as an array of post objects or the raw response from an HTTP request. If you really need to debug the value of something large, use a tool such as step debugging in Xdebug or debugging in Ray.

More examples

You can use this assertion feature to ensure your code is behaving as expected, for example to assert how many database queries are being performed or not performed:

php
foreach ( $posts as $post ) {
	$before = $wpdb->num_queries;
	$this->process_post( $post );
	$after = $wpdb->num_queries;

	// Assert that no database queries are performed as we process each post:
	do_action( 'qm/assert', $after === $before );
}

Preconditions can be used to assert a certain state before performing logic based on expectations:

php
do_action( 'qm/assert', is_array( $data ), 'Data is an array', $data );
do_action( 'qm/assert', array_key_exists( 'foo', $data ), 'Data contains foo', $data );

Postconditions can be used to assert that a particular outcome occurred:

php
do_action( 'qm/assert', did_action( 'my-action' ) );

The static assertion method on the QM class can be used instead of calling do_action():

php
QM::assert( $value === 5 );
QM::assert( $value === 5, 'Value is 5' );
QM::assert( $value === 5, 'Value is 5', $value );

Differences from assert()

This feature differs from the native assert() function in PHP because they serve different purposes.

  • The assert() function in PHP will terminate execution of the script if the assertion fails, this is not true for an assertion in Query Monitor. Think of this like a soft assertion that raises an error instead. Code should behave as expected regardless of whether the assertion passes.
  • Query Monitor logs passed assertions too. This is useful for verifying that your assertion is being executed.
  • Assertions in Query Monitor will always be performed and logged as necessary. The assert() function in PHP will only perform the assertion if assertions are enabled in the php.ini configuration.
  • Assertions in Query Monitor can be passed an optional value to output for debugging purposes, which is not possible with assert().

Notes on usage

Assertions are primarily a development tool to identify bugs or sub-optimal behaviour in your code. This is distinct from error handling or data validation, which assertions are not intended for.

Just as with the assert() function in PHP, your code must handle the situation where your assertion fails because in a production environment the code will continue to execute past the assertion.

Profiling and logging

Read more about the profiling and logging functionality in Query Monitor.

]]>
<![CDATA[User Capabilities]]> https://querymonitor.com/wordpress-debugging/user-capabilities.html https://querymonitor.com/wordpress-debugging/user-capabilities.html Wed, 21 Feb 2024 23:03:35 GMT Debugging user capability checks with Query Monitor

Query Monitor can log all of the user capability checks that are performed during a page load of WordPress. It will show you the result of the capability check, information about the user, and where the check was called from. This can be very helpful when testing functionality on your site for users who are not Administrators.

Step 1: Enable the panel

The user capabilities panel is not enabled by default because it can cause performance issues on sites that perform a large number of user capability checks. To enable this panel, add the following code to your wp-config.php file:

php
define( 'QM_ENABLE_CAPS_PANEL', true );

Step 2: Authenticate

From the Settings panel in Query Monitor (click the cog next to the Close icon), click the "Set authentication cookie" button. This will allow you to view Query Monitor output while you're logged in as a different user.

Step 3: Log in as another user

Log in to your site using a user account with a lower level role, for example an Editor or Author. To save yourself time you can use the User Switching plugin to instantly swap between user accounts in WordPress at the click of a button.

Step 4: View the Capability Checks panel

You're now able to test the functionality on your site as a lower level user and the "Capability Checks" panel in Query Monitor will show you all the capability checks performed on the page, along with the result, the caller, and the component. No more guesswork!

Screenshot of the "Capability Checks" panel in Query Monitor

]]>
<![CDATA[Configuration constants]]> https://querymonitor.com/help/configuration-constants.html https://querymonitor.com/help/configuration-constants.html Thu, 11 Jan 2024 23:03:19 GMT Configuration constants

The following PHP constants can be defined in your wp-config.php file in order to control the behaviour of Query Monitor:

QM_DB_EXPENSIVE

If an individual database query takes longer than this time to execute, it's considered "slow" and triggers a warning.

Default 0.05

QM_DISABLED

Disable Query Monitor entirely.

Default false

QM_DISABLE_ERROR_HANDLER

Disable the handling of PHP errors.

Default false

QM_ENABLE_CAPS_PANEL

Enable the Capability Checks panel.

Default false

QM_HIDE_CORE_ACTIONS

Hide WordPress core on the Hooks & Actions panel.

Default false

QM_HIDE_SELF

Hide Query Monitor itself from various panels. Set to false if you want to see how Query Monitor hooks into WordPress.

Default true

QM_SHOW_ALL_HOOKS

In the Hooks & Actions panel, show every hook that has an action or filter attached (instead of every action hook that fired during the request).

Default false

Allow the wp-content/db.php file symlink to be put into place during activation. Set to false to prevent the symlink creation.

Default true

]]>
<![CDATA[Translation files]]> https://querymonitor.com/wordpress-debugging/javascript-translation-files.html https://querymonitor.com/wordpress-debugging/javascript-translation-files.html Wed, 10 Jan 2024 13:49:03 GMT Debugging translation files with Query Monitor

WordPress 5.0 introduced the ability to use internationalisation functions in JavaScript and provide Jed translations for messages that use them.

Query Monitor fully supports debugging the loading of these translation files in the Languages panel, so you know which files WordPress is attempting to load:

Screenshot of the Languages panel in Query Monitor showing the Jed translation files that WordPress attempts to load

]]>
<![CDATA[Profiling and logging]]> https://querymonitor.com/wordpress-debugging/profiling-and-logging.html https://querymonitor.com/wordpress-debugging/profiling-and-logging.html Wed, 10 Jan 2024 13:49:03 GMT Profiling and logging with Query Monitor

Query Monitor allows developers to profile the running time and memory usage of a piece of code on your WordPress site and to log debugging messages to the Query Monitor interface.

Let's take a look at profiling and logging in detail.

Profiling

Basic profiling can be performed and displayed in the Timings panel in Query Monitor using actions in your code:

php
// Start the 'foo' timer:
do_action( 'qm/start', 'foo' );

// Run some code
my_potentially_slow_function();

// Stop the 'foo' timer:
do_action( 'qm/stop', 'foo' );

The time taken and approximate memory usage used between the qm/start and qm/stop actions for the given function name will be recorded and shown in the Timings panel. Timers can be nested, although be aware that this reduces the accuracy of the memory usage calculations.

Timers can also make use of laps with the qm/lap action:

php
// Start the 'bar' timer:
do_action( 'qm/start', 'bar' );

// Iterate over some data:
foreach ( range( 1, 10 ) as $i ) {
    my_potentially_slow_function( $i );
    do_action( 'qm/lap', 'bar' );
}

// Stop the 'bar' timer:
do_action( 'qm/stop', 'bar' );

Here's what the Timing panel looks like:

Query Monitor's Timing Panel

Note that the times and memory usage displayed in the Timings panel should be treated as approximations, because they are recorded at the PHP level and can be skewed by your environment and by other code. If you require highly accurate timings, you'll need to use a low level profiling tool such as XHProf.

Logging

Messages and variables can be logged in Query Monitor similarly to how you can call console.log in JavaScript to log data from WordPress to the console. This can be used as a replacement for var_dump().

php
do_action( 'qm/debug', 'This happened!' );

You can use any of the following actions which correspond to PSR-3 and syslog log levels:

  • qm/debug
  • qm/info
  • qm/notice
  • qm/warning
  • qm/error
  • qm/critical
  • qm/alert
  • qm/emergency

A log level of warning or higher will trigger a notification in Query Monitor's admin toolbar.

Here's what the Logs panel looks like:

Query Monitor's Logging Panel

Contextual interpolation can be used via the curly brace syntax:

php
do_action( 'qm/warning', 'Unexpected value of {foo} encountered', [
    'foo' => $foo,
] );

A WP_Error, Exception, or Throwable object can be passed directly into the logger:

php
if ( is_wp_error( $response ) ) {
    do_action( 'qm/error', $response );
}
php
try {
    // your code
} catch ( Exception $e ) {
    do_action( 'qm/error', $e );
}

Variables of any type can be logged and they'll be formatted appropriately:

WARNING

Be careful not to log very large values such as an array of post objects or the raw response from an HTTP request. If you really need to debug the value of something large, use a tool such as step debugging in Xdebug or debugging in Ray.

php
$var = [ 1, 2, 3 ];
do_action( 'qm/debug', $var );

Finally, the static logging methods on the QM class can be used instead of calling do_action():

php
QM::error( 'Everything is broken' );

The QM class is PSR-3 compatible, although it doesn't actually implement Psr\Log\LoggerInterface.

Assertions

New

New in Query Monitor 3.15

Query Monitor allows developers to perform assertions which will log an error in the Logs panel in Query Monitor when they fail. Read more about using assertions in Query Monitor.

]]>
<![CDATA[wp_die()]]> https://querymonitor.com/wordpress-debugging/wp-die.html https://querymonitor.com/wordpress-debugging/wp-die.html Wed, 10 Jan 2024 13:49:03 GMT wp_die() debugging with Query Monitor

The wp_die() output in WordPress is a thing of beauty... if you’re into minimalism.

Screenshot of the useless output of a call to wp_die()

Query Monitor adds some debugging information to the output of wp_die(), including the component responsible and the call stack, to help you identify the source of the message:

Screenshot of a slightly more useful output of a call to wp_die() with Query Monitor enabled

]]>
<![CDATA[Silencing errors]]> https://querymonitor.com/help/silencing-errors.html https://querymonitor.com/help/silencing-errors.html Wed, 10 Jan 2024 11:18:57 GMT Silencing errors from certain plugins and themes in Query Monitor

When a PHP warning or notice occurs during the page load, Query Monitor displays a coloured notification in the admin toolbar that links to the PHP Errors panel. This is great for debugging but can be an annoyance if a third party plugin or theme continually triggers errors that aren't your responsibility to fix.

Screenshot of a PHP error in Query Monitor

Query Monitor allows you to silence errors from specified plugins or themes. Errors will still be shown in the PHP Errors panel but they won't trigger a coloured notification in the admin toolbar.

Here's how you hide PHP notices from a plugin named "foo":

php
add_filter( 'qm/collect/php_error_levels', function( array $levels ) {
	$levels['plugin']['foo'] = ( E_ALL & ~E_NOTICE );
	return $levels;
} );

This code hooks into the qm/collect/php_error_levels filter and specifies the error levels which should get reported by Query Monitor for the specified plugin. The error levels are specified using the same bitmask syntax used for PHP's error_reporting() function, and in this example is telling Query Monitor to report all errors except notices.

The name to use for the plugin array's index is what Query Monitor shows as the name for the "Component", for example "Plugin: foo" ends up as "foo".

You could also tell Query Monitor to only report warnings from your child theme, and completely silence errors from its parent theme (probably not a good idea):

php
add_filter( 'qm/collect/php_error_levels', function( array $levels ) {
	$levels['theme']['stylesheet'] = ( E_WARNING & E_USER_WARNING );
	$levels['theme']['template']   = ( 0 );
	return $levels;
} );

Any plugin or theme which doesn't have an error level specified via this filter is assumed to have the default level of E_ALL, which shows all errors.

To silence deprecated errors from WordPress core:

php
add_filter( 'qm/collect/php_error_levels', function( $levels ) {
	$levels['core']['core'] = ( E_ALL & ~E_DEPRECATED );
	return $levels;
} );

Finally, if you have special PHP error handling in place on your site and you don't want Query Monitor to handle errors at all, you can disable the error handling functionality completely:

php
define( 'QM_DISABLE_ERROR_HANDLER', true );
]]>
<![CDATA[Related hooks]]> https://querymonitor.com/wordpress-debugging/related-hooks.html https://querymonitor.com/wordpress-debugging/related-hooks.html Wed, 10 Jan 2024 11:18:57 GMT Related hooks with filters or actions attached

Many of the panels in Query Monitor include a sub-menu which lists related hooks where filters or actions are attached to them. This can greatly reduce the amount of time you spend trying to find out what's making changes to certain behaviour on your site.

I've found this to be particularly helpful on the Request panel, which tracks hooks related to rewrite rules, query parsing, request handling, query vars, and more.

Screenshot of the "Hooks in Use" sub-menu of the Request panel in Query Monitor

It's also very useful for figuring out what's making changes to user role and capability handling on your site:

Screenshot of the "Hooks in Use" sub-menu of the Capability Checks panel in Query Monitor

Some panels track certain option names too, which means all the filters related to that option get automatically tracked. For example, you'll be able to see if something is hooked onto the pre_option_stylesheet filter on the Template panel, or the site_option_WPLANG filter on the Languages panel.

Not all of the panels are tracking related hooks yet. I'll continue expanding and improving this feature in future releases of QM, including adding a way to expose all of the hooks that each panel is tracking, regardless of whether filters or actions are attached to them.

]]>
<![CDATA[Template part loading]]> https://querymonitor.com/wordpress-debugging/template-part-loading.html https://querymonitor.com/wordpress-debugging/template-part-loading.html Wed, 10 Jan 2024 11:18:57 GMT Debugging WordPress template part loading with Query Monitor

Template parts are a fundamental part of building WordPress themes, but sometimes it can be difficult to find out why a given template part is or isn't loading.

Query Monitor makes debugging template part loading easier by exposing the list of template parts that either were or were not loaded. Here's a screenshot of it in action:

Screenshot of the Template Parts section of the Template panel in Query Monitor

What this allows you to do is to see the value of the $slug and $name parameters that were passed to get_template_part() when an unsuccessful request for a template part was made. Query Monitor will show you the file name and line number where the call was made, so you can find it easily and investigate if necessary, or if you've got clickable stack traces enabled from the settings screen you can just click it to be taken straight there.

I hope this feature is useful to you! I've certainly been finding it useful myself.

]]>
<![CDATA[Cache hit rate]]> https://querymonitor.com/help/cache-hit-rate.html https://querymonitor.com/help/cache-hit-rate.html Sun, 12 Nov 2023 19:52:26 GMT Cache hit rate

Does your object cache hit rate always show as 100%? If so, this is possibly due to a bug in the Memcached object cache controller.

The bug was fixed here: https://github.com/Ipstenu/memcached-redux/pull/2 . You may need to update your object cache drop-in.

If you're not using the Memcached object cache controller and you're always seeing either 0% or 100% cache hit rate, please let me know!

]]>