Skip to content

Instantly share code, notes, and snippets.

@bhubbard
Forked from sabrina-zeidan/sz_ajax_logger.php
Created January 21, 2026 14:55
Show Gist options
  • Select an option

  • Save bhubbard/b3f563bc66a8475231c8aeb0f26c4b41 to your computer and use it in GitHub Desktop.

Select an option

Save bhubbard/b3f563bc66a8475231c8aeb0f26c4b41 to your computer and use it in GitHub Desktop.
Save all AJAX queries performed to track AJAX performance [WordPress]
add_action('shutdown','sz_log_all_ajax_queries');
function sz_log_all_ajax_queries() {
global $wpdb;
if ( empty($wpdb->queries) ) {
return;
}
$timestamp = date('Y-m-d H:i:s');
// Define log files
$log_full = WP_CONTENT_DIR . '/ajax-queries-full.log';
$log_filtered = WP_CONTENT_DIR . '/ajax-queries-filtered.log';
$log_options = WP_CONTENT_DIR . '/ajax-queries-options.log';
$log_summary = WP_CONTENT_DIR . '/ajax-queries-summary.log';
// Define exclusion patterns (extendable)
$exclude_patterns = [
$wpdb->prefix . 'options',
$wpdb->prefix . 'usermeta',
$wpdb->prefix . 'postmeta',
$wpdb->prefix . 'comments',
$wpdb->prefix . 'commentmeta',
$wpdb->prefix . 'users',
$wpdb->prefix . 'terms',
$wpdb->prefix . 'posts',
// families of tables
$wpdb->prefix . 'actionscheduler', // Action Scheduler
$wpdb->prefix . 'rocket', // WP Rocket
$wpdb->prefix . 'wpr', // WPR / Above the fold
$wpdb->prefix . 'wc_', // WooCommerce
$wpdb->prefix . 'woocommerce', // WooCommerce
];
// Normalize query so matching is easier
$normalize_query = function($query) {
$query = strtolower($query);
$query = str_replace(['\\_', '\_'], '_', $query);
$query = preg_replace('/\s+/', ' ', $query);
return $query;
};
// Check if query should be excluded
$is_excluded = function($query) use ($exclude_patterns, $normalize_query) {
$q = $normalize_query($query);
foreach ($exclude_patterns as $pattern) {
if (strpos($q, strtolower($pattern)) !== false) {
return true;
}
}
return false;
};
// Logs
$full_log = "=== {$timestamp} ===\n";
$filtered_log = "=== {$timestamp} ===\n";
$options_log = "=== {$timestamp} ===\n";
foreach ($wpdb->queries as $q) {
list($query, $time, $caller) = $q;
// Add to full log
$full_log .= sprintf(
"[%.5fs] %s\n-- Caller: %s\n\n",
$time,
$query,
$caller
);
// Add to filtered log
if (!$is_excluded($query)) {
$filtered_log .= sprintf(
"[%.5fs] %s\n-- Caller: %s\n\n",
$time,
$query,
$caller
);
}
// Add to options-only log
if (strpos($normalize_query($query), $wpdb->prefix . 'options') !== false) {
$options_log .= sprintf(
"[%.5fs] %s\n-- Caller: %s\n\n",
$time,
$query,
$caller
);
}
}
// Calculate totals
$total_queries = count($wpdb->queries);
$total_time = array_sum( array_column($wpdb->queries, 1) );
$avg_time = $total_queries ? $total_time / $total_queries : 0;
// Request context
$request_method = $_SERVER['REQUEST_METHOD'] ?? 'CLI';
$request_uri = $_SERVER['REQUEST_URI'] ?? '(no URI)';
// Summary log
$summary_log = "=== {$timestamp} ===\n";
$summary_log .= "Request: {$request_method} {$request_uri}\n";
$summary_log .= "Total queries: {$total_queries}\n";
$summary_log .= "Total time: " . number_format($total_time, 5) . "s\n";
$summary_log .= "Average per query: " . number_format($avg_time, 5) . "s\n\n";
// Write files
file_put_contents($log_full, $full_log . "\n", FILE_APPEND);
file_put_contents($log_filtered, $filtered_log . "\n", FILE_APPEND);
file_put_contents($log_options, $options_log . "\n", FILE_APPEND);
file_put_contents($log_summary, $summary_log, FILE_APPEND);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment