Agregar plugin WP Debug para diagnóstico sistemático
This commit is contained in:
266
wp-content/plugins/wp-debug/inc/query-analyzer.php
Normal file
266
wp-content/plugins/wp-debug/inc/query-analyzer.php
Normal file
@@ -0,0 +1,266 @@
|
||||
<?php
|
||||
/**
|
||||
* Query Analyzer Module
|
||||
*
|
||||
* Analiza queries SQL, detecta N+1 queries y queries lentas
|
||||
*
|
||||
* @package WP_Debug
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
// Exit if accessed directly
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* WP_Debug_Query_Analyzer class
|
||||
*/
|
||||
class WP_Debug_Query_Analyzer {
|
||||
|
||||
/**
|
||||
* Queries executed
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $queries_executed = array();
|
||||
|
||||
/**
|
||||
* Query patterns
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $query_patterns = array();
|
||||
|
||||
/**
|
||||
* Initialize query analyzer
|
||||
*/
|
||||
public static function init() {
|
||||
// Enable query saving
|
||||
if (!defined('SAVEQUERIES')) {
|
||||
define('SAVEQUERIES', true);
|
||||
}
|
||||
|
||||
// Track queries on shutdown
|
||||
add_action('shutdown', array(__CLASS__, 'analyze_queries'), 999);
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyze queries
|
||||
*/
|
||||
public static function analyze_queries() {
|
||||
global $wpdb;
|
||||
|
||||
if (empty($wpdb->queries)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Process each query
|
||||
foreach ($wpdb->queries as $query) {
|
||||
list($sql, $elapsed, $caller) = $query;
|
||||
|
||||
$query_info = array(
|
||||
'sql' => $sql,
|
||||
'elapsed' => $elapsed,
|
||||
'caller' => $caller,
|
||||
'timestamp' => microtime(true),
|
||||
);
|
||||
|
||||
self::$queries_executed[] = $query_info;
|
||||
|
||||
// Group by pattern
|
||||
$normalized = self::normalize_query($sql);
|
||||
if (!isset(self::$query_patterns[$normalized])) {
|
||||
self::$query_patterns[$normalized] = array(
|
||||
'count' => 0,
|
||||
'total_time' => 0,
|
||||
'queries' => array(),
|
||||
);
|
||||
}
|
||||
|
||||
self::$query_patterns[$normalized]['count']++;
|
||||
self::$query_patterns[$normalized]['total_time'] += floatval($elapsed);
|
||||
self::$query_patterns[$normalized]['queries'][] = $query_info;
|
||||
}
|
||||
|
||||
// Log statistics
|
||||
$total_queries = count($wpdb->queries);
|
||||
$total_time = array_sum(array_column($wpdb->queries, 1));
|
||||
$slow_queries = self::get_slow_queries();
|
||||
$n_plus_one = self::detect_n_plus_one();
|
||||
|
||||
if (class_exists('WP_Debug_Logger')) {
|
||||
WP_Debug_Logger::info('Query Analysis Complete', array(
|
||||
'total_queries' => $total_queries,
|
||||
'total_time' => round($total_time, 4) . 's',
|
||||
'slow_queries' => count($slow_queries),
|
||||
'n_plus_one_patterns' => count($n_plus_one),
|
||||
));
|
||||
|
||||
// Log slow queries
|
||||
foreach ($slow_queries as $query) {
|
||||
WP_Debug_Logger::warning('Slow Query Detected', array(
|
||||
'elapsed' => round($query['elapsed'], 4) . 's',
|
||||
'sql' => substr($query['sql'], 0, 200),
|
||||
'caller' => $query['caller'],
|
||||
));
|
||||
}
|
||||
|
||||
// Log N+1 patterns
|
||||
foreach ($n_plus_one as $pattern) {
|
||||
WP_Debug_Logger::warning('N+1 Query Pattern Detected', array(
|
||||
'count' => $pattern['count'],
|
||||
'total_time' => round($pattern['total_time'], 4) . 's',
|
||||
'example' => substr($pattern['example'], 0, 200),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Store for frontend panel
|
||||
set_transient('wp_debug_queries_data', array(
|
||||
'total_queries' => $total_queries,
|
||||
'total_time' => $total_time,
|
||||
'queries' => self::$queries_executed,
|
||||
'slow_queries' => $slow_queries,
|
||||
'n_plus_one' => $n_plus_one,
|
||||
'patterns' => self::$query_patterns,
|
||||
), 3600);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize query for pattern detection
|
||||
*
|
||||
* @param string $query SQL query
|
||||
* @return string Normalized query
|
||||
*/
|
||||
private static function normalize_query($query) {
|
||||
// Remove numeric literals
|
||||
$query = preg_replace('/\b\d+\b/', '?', $query);
|
||||
|
||||
// Remove string literals
|
||||
$query = preg_replace("/'[^']*'/", '?', $query);
|
||||
|
||||
// Remove extra whitespace
|
||||
$query = preg_replace('/\s+/', ' ', $query);
|
||||
|
||||
return trim($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect N+1 query patterns
|
||||
*
|
||||
* @return array N+1 patterns
|
||||
*/
|
||||
public static function detect_n_plus_one() {
|
||||
$patterns = array();
|
||||
|
||||
// Find patterns executed 5+ times
|
||||
foreach (self::$query_patterns as $normalized => $pattern) {
|
||||
if ($pattern['count'] >= 5) {
|
||||
$patterns[] = array(
|
||||
'count' => $pattern['count'],
|
||||
'total_time' => $pattern['total_time'],
|
||||
'avg_time' => $pattern['total_time'] / $pattern['count'],
|
||||
'example' => $pattern['queries'][0]['sql'],
|
||||
'normalized' => $normalized,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Sort by count descending
|
||||
usort($patterns, function($a, $b) {
|
||||
return $b['count'] - $a['count'];
|
||||
});
|
||||
|
||||
return $patterns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get slow queries
|
||||
*
|
||||
* @param float $threshold Threshold in seconds
|
||||
* @return array Slow queries
|
||||
*/
|
||||
public static function get_slow_queries($threshold = 0.05) {
|
||||
return array_filter(self::$queries_executed, function($query) use ($threshold) {
|
||||
return floatval($query['elapsed']) > $threshold;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get queries by caller
|
||||
*
|
||||
* @param string $caller Caller filter
|
||||
* @return array Queries
|
||||
*/
|
||||
public static function get_queries_by_caller($caller) {
|
||||
return array_filter(self::$queries_executed, function($query) use ($caller) {
|
||||
return strpos($query['caller'], $caller) !== false;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get query statistics
|
||||
*
|
||||
* @return array Statistics
|
||||
*/
|
||||
public static function get_statistics() {
|
||||
global $wpdb;
|
||||
|
||||
if (empty($wpdb->queries)) {
|
||||
return array(
|
||||
'error' => 'SAVEQUERIES not enabled or no queries executed',
|
||||
);
|
||||
}
|
||||
|
||||
$total_queries = count($wpdb->queries);
|
||||
$total_time = array_sum(array_column($wpdb->queries, 1));
|
||||
$avg_time = $total_time / $total_queries;
|
||||
|
||||
return array(
|
||||
'total_queries' => $total_queries,
|
||||
'total_time' => $total_time,
|
||||
'avg_time' => $avg_time,
|
||||
'slow_queries' => count(self::get_slow_queries()),
|
||||
'n_plus_one_patterns' => count(self::detect_n_plus_one()),
|
||||
'unique_patterns' => count(self::$query_patterns),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get query report
|
||||
*
|
||||
* @return array Query report
|
||||
*/
|
||||
public static function get_query_report() {
|
||||
$stats = self::get_statistics();
|
||||
$slow_queries = self::get_slow_queries();
|
||||
$n_plus_one = self::detect_n_plus_one();
|
||||
|
||||
$suggestions = array();
|
||||
|
||||
// Suggestions based on analysis
|
||||
if (isset($stats['total_queries']) && $stats['total_queries'] > 100) {
|
||||
$suggestions[] = 'High number of queries detected (' . $stats['total_queries'] . '). Consider using object caching.';
|
||||
}
|
||||
|
||||
if (isset($stats['total_time']) && $stats['total_time'] > 1) {
|
||||
$suggestions[] = 'Total query time exceeds 1 second. Optimize slow queries.';
|
||||
}
|
||||
|
||||
if (!empty($n_plus_one)) {
|
||||
$suggestions[] = 'N+1 query patterns detected. Consider using WP_Query with proper parameters or caching.';
|
||||
}
|
||||
|
||||
if (!empty($slow_queries)) {
|
||||
$suggestions[] = count($slow_queries) . ' slow queries detected. Add indexes or optimize WHERE clauses.';
|
||||
}
|
||||
|
||||
return array(
|
||||
'statistics' => $stats,
|
||||
'slow_queries' => array_slice($slow_queries, 0, 10),
|
||||
'n_plus_one_patterns' => array_slice($n_plus_one, 0, 5),
|
||||
'suggestions' => $suggestions,
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user