Del Error 500 al Control Total: Cómo creé mi propio monitor de WordPress en una tarde gracias a la IA
Introducción: el problema tras la actualización de WordPress
Todo comenzó como suele empezar cualquier lunes complicado para un webmaster: con una actualización mayor en WordPress. Esperas que todo vaya bien, cruzas los dedos, pero de repente, la pantalla se queda en blanco o aparece el temido mensaje:
500 Internal Server Error
The server encountered an internal error or misconfiguration and was unable to complete your request.
Please contact the server administrator to inform them of the time this error occurred…
Este problema es muy común después de una actualización importante. El Error 500 es la forma que tiene el servidor de decirte: «Algo ha salido mal, pero no te diré exactamente qué». En mi caso, apareció justo después de un aviso que indicaba que era necesaria una actualización de la base de datos de WordPress.

Generalmente, esto ocurre por tres razones principales:
- Un plugin o tema incompatible con la nueva versión.
- Un archivo .htaccess corrupto.
- El problema silencioso: falta de memoria PHP en el servidor.
Para saber qué pasa realmente, no podemos ir a ciegas. Lo primero que hay que hacer es «encender la luz». Entré vía FTP, busqué el archivo wp-config.php y cambié esta línea:
define( 'WP_DEBUG', false );
Por esta:
define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );
Esto genera un archivo de registro. Al abrirlo, pude ver los errores, desactivar los plugins conflictivos y borrar la caché. Sin embargo, me di cuenta de algo frustrante: el log nativo de WordPress es farragoso. Es un muro de texto interminable que te dice el error, pero no te dice cuánta memoria se estaba consumiendo en ese momento, ni si el servidor estaba saturado.
La necesidad de una herramienta propia
Necesitaba un monitor de recursos WordPress que actuara como un monitor de signos vitales para mi web.
Mi objetivo era claro: quería tener una visión precisa del consumo de memoria real, los tiempos de ejecución y la carga de la CPU en cada petición. Pero no quería instalar un plugin pesado de monitoreo (como Query Monitor o New Relic) que, irónicamente, consumiera más recursos de los que quería ahorrar.
Decidí desarrollar un monitor de recursos WordPress ligero. El problema es que programar esto desde cero me habría llevado días, no es lo mio…
El papel de la inteligencia artificial
Aquí es donde entraron en juego las versiones gratuitas de Copilot y Gemini.
En lugar de escribir cada línea de código PHP, actué como el «arquitecto» y utilicé a la IA como mi «albañil senior». Mi enfoque fue utilizar la IA para hacer pair programming (programación en pareja):
- Yo definía la lógica: «Necesito que filtres por tipo de petición», «quiero que el log se limpie solo para no llenar el disco», «ponme colores si pasa de 100MB».
- La IA escribía la sintaxis: Generaba el código base, las expresiones regulares y las funciones de WordPress.
- Yo depuraba: «Oye, esto da error si la variable no existe».
- La IA corregía: Refinando el código al instante.
Fue una experiencia reveladora. Lo que antes era un fin de semana de trabajo, se convirtió en una tarde creativa.
Nace «Memory Logger»: Un monitor de recursos WordPress a medida
El resultado de esta colaboración humano-IA es Memory Logger.
Es una herramienta de monitorización y diagnóstico diseñada específicamente para detectar cuellos de botella y optimizar los recursos del servidor. Su filosofía es simple: ser invisible hasta que la necesitas.
Características Clave:
- 🧠 Registro Inteligente de Memoria: No registra todo (eso saturaría el disco). Tú configuras un «Umbral» (ej. 70 MB) y el plugin solo guarda los datos si una visita supera ese consumo. Si la web va ligera, el plugin calla. Si la web sufre, el plugin apunta.
- ⚡ Semáforo de Tiempo: Un sistema visual (Negro/Naranja/Rojo) que te avisa si la página tarda más de lo debido en generarse, ayudándote a distinguir entre falta de caché o procesos bloqueados.
- 🗄️ Monitor de Consultas SQL (Nuevo): Cuenta cuántas veces pregunta WordPress a la base de datos para generar una sola página. Si ves una línea con más de 200 consultas, has encontrado un cuello de botella causado por un plugin ineficiente.
- 🖥️ Detección de Hardware (CPU): El plugin detecta automáticamente cuántos núcleos tiene tu servidor (en mi caso, descubrí que eran 128). Esto es vital para calcular si la «Carga de CPU» es normal o si el servidor está saturado.
- 🚦 Clasificación de Solicitudes: Diferencia automáticamente si la carga viene del tráfico web (Frontend), de alguien trabajando en el panel (Backend) o de tareas programadas (Cron).
- 🪶 Ultraligero (Zero Impact): A diferencia de otros monitores pesados, este plugin tiene apenas 500 líneas de código y no consume recursos. Solo se activa, mide y se apaga.
Funcionamiento del plugin: Invisible y Seguro
El plugin opera de manera automática y silenciosa. Cada vez que alguien visita tu web o WordPress ejecuta una tarea, el plugin mide los signos vitales del servidor justo antes de terminar la ejecución.
Lo mejor es que está diseñado para no requerir mantenimiento. Desde el panel Monitor WP, ofrece:
- Panel de Control Visual: Una tabla limpia que muestra las últimas 200 entradas relevantes, sin tener que bucear en archivos de texto.
- Truncamiento Inteligente (Auto-limpieza): Esta es una función de seguridad clave. Si el archivo de log crece demasiado (por defecto 2MB), el plugin borra automáticamente las entradas antiguas. Nunca llenará tu espacio en disco, puedes dejarlo activo meses sin miedo.
- Filtros Rápidos: Un desplegable para aislar el ruido y ver solo lo que te interesa: «¿Es el Cron el que me tira la web? ¿O es el tráfico del usuario?».
- Exportación CSV: Un botón para descargar los datos y analizarlos cómodamente en Excel o Google Sheets si necesitas hacer gráficas o enviárselo a tu soporte de hosting.
Cómo se instala (Instalación Rápida)
Para que esta herramienta sea realmente efectiva y pueda medir la memoria desde el inicio, se ha diseñado como un mu-plugin (Must-Use plugin).
- Accede a tu servidor vía FTP o Gestor de Archivos.
- Ve a la carpeta wp-content.
- Si no existe, crea una carpeta llamada mu-plugins.
- Sube el archivo memory-logger.php ahí dentro.
Ventaja: Al estar en mu-plugins, WordPress lo carga automáticamente antes que cualquier otro plugin normal. Esto significa que siempre estará activo, incluso si otros plugins fallan o si cambias de tema.
Guía de la Interfaz
Simplemente refresca tu panel de administración y busca en el menú lateral izquierdo. Verás una nueva opción llamada «Monitor WP».
(Nota: Aunque el plugin se llama Memory Logger, he etiquetado el menú como Monitor WP para que sea más fácil de identificar y ocupe menos espacio).
Al hacer clic, accederás al panel principal donde podrás:
- Configuración de Umbrales: Ajusta los valores según tu servidor. Si tienes un hosting modesto con 256MB de RAM, quizás quieras que te avise en rojo si una petición supera los 120MB.
- Acciones de Mantenimiento: Tienes botones para «Vaciar registro» (recomendado tras instalar actualizaciones) y «Exportar registro» (para descargar un CSV y analizar los datos en Excel).
Entendiendo los datos: Guía de columnas del Memory Logger
Una vez el plugin empieza a registrar datos, verás una tabla con varias columnas. Aquí te explico cómo interpretar cada una para diagnosticar tu web:
- 📅 Fecha: El momento exacto del registro. Útil para correlacionar picos de consumo con campañas de email marketing, publicación de posts o ataques de bots.
- 📂 Tipo: Quién está haciendo la petición:
- Frontend: Visitas de usuarios (o bots) a la web pública.
- Backend: Tú o tus editores trabajando en el panel de administración.
- Cron: Tareas de mantenimiento automático de WordPress.
- 🔗 URL: La dirección exacta que causó el consumo. Vital para saber si el problema es la «Portada», la página de «Checkout» o un archivo sospechoso (como me pasó a mí).
- 🧠 Pico Memoria: Máxima RAM usada. El plugin usa colores automáticos:
- Negro (< 80 MB): Consumo normal.
- Naranja (80-120 MB): Consumo alto. Típico de páginas pesadas.
- Rojo (> 120 MB): Muy alto. Riesgo de error fatal.
- ⚡ Tiempo (s): Cuánto tardó el servidor en «pensar» y entregar la página:
- < 0.5s: Excelente (generalmente gracias a la caché).
- 1.0s – 2.0s: Aceptable para zonas sin caché (admin/carrito).
- > 2.0s – 5.0s: Problema de lentitud grave o bloqueo.
- 🗄️ SQL (Consultas): (Nueva en v3.0) Número de veces que WordPress preguntó a la Base de Datos para generar esa página.
- < 100: Optimizado.
- 100 – 200: Pesado.
- > 200: Ineficiente. Un plugin está «martilleando» tu base de datos innecesariamente.
- 🖥️ CPU (1m): La carga del procesador en el último minuto.
Nota importante: Este valor depende de los núcleos de tu servidor (el plugin te dice cuántos tienes arriba).- Si tienes 4 núcleos, una carga de 4.0 es el 100%.
- Si tienes 128 núcleos (como en mi hosting), una carga de 30.0 es apenas un 23%.
- Regla de oro: Si el número es menor que tus núcleos, todo va bien. Si es mayor, hay saturación.
Veamos un ejemplo:

Análisis de la captura: ¿Qué nos dice el Memory Logger?
En esta imagen, capturada mientras sometíamos la web a un escaneo de seguridad y regeneración de caché, la herramienta ha revelado los verdaderos culpables de la lentitud, que no tenían nada que ver con la potencia del servidor:
- El «Cron» pesado (Mantenimiento): Fíjate en la línea del tipo Cron. Muestra un tiempo de ejecución de 28.375 s. Esto confirma que hay tareas en segundo plano (como copias de seguridad o escaneos de Wordfence) que están acaparando la ejecución de PHP durante casi medio minuto.
-
Bloqueo en el Backend (Ajax):
La primera línea muestra una llamada a
admin-ajax.phpque tardó casi 14 segundos. Este es el motivo por el que, a veces, el panel de administración se siente lento o se queda «pensando» cuando guardamos cambios. -
El cuello de botella SQL (Base de Datos):
Esta es la métrica más reveladora. Al editar una entrada (línea
post.php), WordPress realizó 372 consultas SQL. Esto indica una sobrecarga de plugins (SEO, campos personalizados, maquetadores) que están «bombardeando» la base de datos para cargar una sola pantalla. -
El servidor ni se inmuta (CPU):
A pesar de todos estos números rojos en Tiempo y SQL, la columna de CPU (46-49) sigue en color negro (valores normales). Como mi hosting tiene 128 núcleos, el servidor está relajado.
Conclusión del diagnóstico: El problema no es que falte servidor («hierro»), sino que el software (WordPress + Plugins) necesita optimización.
¿Qué pasa si la web no carga?
Si el consumo de memoria es tan alto que tira el servidor, no podrás entrar a wp-admin para ver tu bonita tabla.
La buena noticia: Tu plugin ya está diseñado para esto. Como escribe los datos en un archivo físico en el servidor antes de mostrarlos en pantalla, el registro existe independientemente de si puedes ver el backend o no.
Cómo acceder a los datos «a la antigua»: Simplemente entra por FTP a tu carpeta /wp-content/ y encontrarás un archivo llamado memory-usage.log. Ahí verás la última línea registrada justo antes de que la web colapsara, lo cual te dirá qué URL o proceso fue el culpable.
Conclusión: lo que se puede lograr en una tarde con IA
La creación de este monitor de recursos WordPress (Memory Logger): la IA nos permite dar un salto gigante y pasar de ser simples consumidores de software a creadores de nuestras propias soluciones.
Obviamente, soy consciente de que ya existen herramientas fantásticas en el mercado, gratuitas como Query Monitor o WP Crontrol, y soluciones de pago nivel enterprise como New Relic. Pero a veces, esas herramientas son como «matar moscas a cañonazos»: ofrecen demasiados datos o añaden una carga extra al servidor que queríamos evitar.
Memory Logger es diferente. Es un desarrollo extremadamente ligero (apenas 500 líneas de código) diseñado para no consumir recursos del servidor. Está hecho a medida, es «quirúrgico» y se centra exclusivamente en lo que necesitaba ver: Memoria, CPU, Tiempo y SQL. Y, sinceramente, desarrollarlo mano a mano con la IA ha sido un reto personal mucho más gratificante que simplemente instalar otro plugin más.
Pero esto no acaba aquí. Gracias a la velocidad de desarrollo con IA, ya tengo en la hoja de ruta las próximas funcionalidades que implementaré:
- 🕵️ Detección de «User Agent»: Para identificar si quien consume la memoria es un usuario real, Googlebot o un bot malicioso.
- 🚦 Códigos de Estado HTTP: Para ver de un vistazo si la visita terminó en éxito (200), página no encontrada (404) o error (500).
- 🛡️ Modo Silencioso por IP: Para excluir mi propia IP del registro y no «ensuciar» los datos mientras trabajo en el panel.
Te invito a descargar el código, instalarlo en tu carpeta mu-plugins y descubrir por fin qué es lo que realmente está consumiendo los recursos de tu WordPress.
📥 Descarga el Código
<?php
/**
* Plugin Name: Memory Logger
* Plugin URI: https://www.posicionamientowebysem.com
* Description: Monitor de rendimiento: Memoria, Tiempo, CPU y Consultas SQL. Diagnóstico ligero para WordPress.
* Version: 3.2
* Author: Josep Maria Tapia Estaragues
* Author URI: https://www.posicionamientowebysem.com
* License: GPLv2 or later
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
*
* Uso:
* - Copiar en `wp-content/mu-plugins/`.
* - Registra datos en `wp-content/memory-usage.log`.
*/
// Tiempo de inicio
if ( !defined('WP_START_TIME') ) {
define('WP_START_TIME', microtime(true));
}
// Ruta del archivo
if ( !defined('MEMORY_LOG_FILE') ) {
define('MEMORY_LOG_FILE', WP_CONTENT_DIR . '/memory-usage.log');
}
// Opciones guardadas
function memory_logger_get_options() {
$defaults = [
'threshold_mb' => 70,
'max_size' => 2097152,
'time_warn' => 2.0,
'time_risk' => 5.0,
'cpu_warn' => 1.0,
'cpu_risk' => 2.0,
];
$opts = get_option('memory_logger_options', []);
return wp_parse_args($opts, $defaults);
}
// FUNCIONES AUXILIARES
function memory_logger_get_request_type($url) {
if (strpos($url, 'wp-cron.php') !== false || (defined('DOING_CRON') && DOING_CRON)) {
return 'Cron';
}
if (strpos($url, '/wp-admin') !== false || is_admin() || (defined('WP_ADMIN') && WP_ADMIN)) {
return 'Backend';
}
return 'Frontend';
}
function memory_logger_get_cpu_count() {
$numCpus = 1;
if (is_file('/proc/cpuinfo')) {
$cpuinfo = @file_get_contents('/proc/cpuinfo');
if ($cpuinfo) {
preg_match_all('/^processor/m', $cpuinfo, $matches);
$count = count($matches[0]);
if ($count > 0) $numCpus = $count;
}
}
return $numCpus;
}
// ====================================================================
// 1. REGISTRADOR (LOGGER)
// ====================================================================
add_action('shutdown', function() {
$opts = memory_logger_get_options();
$to_mb = fn($bytes) => round($bytes / 1024 / 1024, 2) . ' MB';
$mem_pic_real = memory_get_peak_usage(true);
// Filtro por umbral
if ( $mem_pic_real < ($opts['threshold_mb'] * 1024 * 1024) ) return;
$mem_actual_real = $to_mb(memory_get_usage(true));
$mem_pic_real_mb = $to_mb($mem_pic_real);
$wp_limit = defined('WP_MEMORY_LIMIT') ? WP_MEMORY_LIMIT : 'no definido';
$wp_max = defined('WP_MAX_MEMORY_LIMIT') ? WP_MAX_MEMORY_LIMIT : 'no definido';
$php_limit = ini_get('memory_limit');
$php_time_limit = ini_get('max_execution_time');
$exec_time = round(microtime(true) - WP_START_TIME, 3);
// Contador SQL (Seguro)
$num_queries = function_exists('get_num_queries') ? get_num_queries() : 0;
global $wp_version;
$php_version = phpversion();
$cpu_load = 'no disponible';
if ( function_exists('sys_getloadavg') ) {
$loads = sys_getloadavg();
if ( is_array($loads) ) {
$loads = array_map(function($val) { return round($val, 2); }, $loads);
$cpu_load = implode(', ', $loads);
}
}
$url = $_SERVER['REQUEST_URI'] ?? '(sin URL)';
$request_type = memory_logger_get_request_type($url);
// Formato interno v3
$msg = sprintf(
"[MEMORY-LOG] %s | TYPE: %s | URL: %s | Real: %s | Pic Real: %s | EXEC_TIME: %s | SQL: %d | WP_LIMIT: %s | WP_MAX: %s | PHP_LIMIT: %s | PHP_TIME: %s | WP_VERSION: %s | PHP_VERSION: %s | CPU_LOAD: %s\n",
gmdate("Y-m-d H:i:s"),
$request_type,
$url,
$mem_actual_real,
$mem_pic_real_mb,
$exec_time,
$num_queries,
$wp_limit,
$wp_max,
$php_limit,
$php_time_limit,
$wp_version ?? 'desconocida',
$php_version,
$cpu_load
);
if ( file_exists(MEMORY_LOG_FILE) && filesize(MEMORY_LOG_FILE) > $opts['max_size'] ) {
$content = @file_get_contents(MEMORY_LOG_FILE);
if ($content !== false) {
$lines = explode("\n", trim($content));
$last_lines = array_slice($lines, -200);
@file_put_contents(MEMORY_LOG_FILE, implode("\n", $last_lines) . "\n");
}
}
@file_put_contents(MEMORY_LOG_FILE, $msg, FILE_APPEND);
});
// ====================================================================
// 2. EXPORTACIÓN CSV
// ====================================================================
add_action('admin_post_memory_logger_export', 'memory_logger_export_csv');
function memory_logger_export_csv() {
if ( ! current_user_can('manage_options') || ! isset($_POST['export_nonce_field']) || ! wp_verify_nonce($_POST['export_nonce_field'], 'export_memory_log_action') ) {
wp_die('Acceso denegado.');
}
$file = MEMORY_LOG_FILE;
if ( ! file_exists($file) ) wp_die('El archivo de registro no existe o está vacío.');
$content = @file_get_contents($file);
$csv_data = "Fecha,Tipo,URL,Memoria Actual,Pico Real,Tiempo (s),Consultas SQL,Limite WP,Max WP,Limite PHP,Tiempo Limite,Version WP,Version PHP,Carga CPU (1m)\n";
$lines = explode("\n", trim($content));
foreach ($lines as $line) {
$line = trim($line);
if (empty($line) || strpos($line, '[MEMORY-LOG]') !== 0) continue;
$raw_data = substr($line, strlen('[MEMORY-LOG] '));
$parts = explode(' | ', $raw_data);
if (count($parts) >= 13) {
$date = $parts[0];
$type = trim(str_replace('TYPE: ', '', $parts[1]));
$url = str_replace(['"', ','], ['', ';'], trim(str_replace('URL: ', '', $parts[2])));
$mem_actual = trim(str_replace(['Real: ', ' MB'], '', $parts[3]));
$mem_pic = trim(str_replace(['Pic Real: ', ' MB'], '', $parts[4]));
$exec_time = trim(str_replace(['EXEC_TIME: ', ' s'], '', $parts[5]));
if (strpos($parts[6], 'SQL:') !== false) {
$sql_queries = trim(str_replace('SQL: ', '', $parts[6]));
$idx_offset = 1;
} else {
$sql_queries = 'N/A';
$idx_offset = 0;
}
$wp_limit = trim(str_replace('WP_LIMIT: ', '', $parts[6 + $idx_offset]));
$wp_max = trim(str_replace('WP_MAX: ', '', $parts[7 + $idx_offset]));
$php_limit = trim(str_replace('PHP_LIMIT: ', '', $parts[8 + $idx_offset]));
$php_time = trim(str_replace(['PHP_TIME: ', ' s'], '', $parts[9 + $idx_offset]));
$wp_version = trim(str_replace('WP_VERSION: ', '', $parts[10 + $idx_offset]));
$php_version = trim(str_replace('PHP_VERSION: ', '', $parts[11 + $idx_offset]));
$cpu_load = trim(str_replace('CPU_LOAD: ', '', $parts[12 + $idx_offset]));
$csv_data .= "\"$date\",\"$type\",\"$url\",\"$mem_actual\",\"$mem_pic\",\"$exec_time\",\"$sql_queries\",\"$wp_limit\",\"$wp_max\",\"$php_limit\",\"$php_time\",\"$wp_version\",\"$php_version\",\"$cpu_load\"\n";
}
}
header('Content-Type: text/csv; charset=utf-8');
header('Content-Disposition: attachment; filename="registro-memoria-' . date('Ymd_His') . '.csv"');
header('Pragma: no-cache');
header('Expires: 0');
echo "\xEF\xBB\xBF";
echo $csv_data;
exit;
}
// ====================================================================
// 3. PÁGINA DE ADMINISTRACIÓN
// ====================================================================
add_action('admin_menu', function() {
add_menu_page('Monitor WP', 'Monitor WP', 'manage_options', 'memory-log-viewer', 'memory_logger_admin_page', 'dashicons-chart-area', 80);
});
function memory_logger_admin_page() {
$opts = memory_logger_get_options();
// Guardar
if ( isset($_POST['save_memory_logger']) && check_admin_referer('memory_logger_save_action') ) {
$opts['threshold_mb'] = floatval($_POST['threshold_mb']);
$opts['max_size'] = intval($_POST['max_size']);
$opts['time_warn'] = floatval($_POST['time_warn']);
$opts['time_risk'] = floatval($_POST['time_risk']);
$opts['cpu_warn'] = floatval($_POST['cpu_warn']);
$opts['cpu_risk'] = floatval($_POST['cpu_risk']);
if ($opts['time_risk'] <= $opts['time_warn']) $opts['time_risk'] = $opts['time_warn'] + 0.1;
if ($opts['cpu_risk'] <= $opts['cpu_warn']) $opts['cpu_risk'] = $opts['cpu_warn'] + 0.1;
update_option('memory_logger_options', $opts);
echo '<div class="updated notice"><p>Configuración guardada.</p></div>';
$opts = memory_logger_get_options();
}
// Vaciar
if ( isset($_POST['clear_memory_log']) && check_admin_referer('clear_memory_log_action') ) {
if ( file_exists(MEMORY_LOG_FILE) ) {
@file_put_contents(MEMORY_LOG_FILE, "");
echo '<div class="updated notice"><p>Registro vaciado correctamente.</p></div>';
}
}
if ( ! function_exists( 'get_plugin_data' ) ) require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
$plugin_info = get_plugin_data( __FILE__ );
// --- OBTENCIÓN DE DATOS DIRECTA DEL SISTEMA (Fix v3.2) ---
global $wp_version;
$wp_version_display = isset($wp_version) ? $wp_version : 'No disponible';
$php_version_display = phpversion();
$cpu_count = memory_logger_get_cpu_count();
$file = MEMORY_LOG_FILE;
// --- HTML ---
echo '<div class="wrap"><h1>Monitor de Recursos (v3.2)</h1>';
// Info Plugin
echo '<div style="background:#fff; border:1px solid #c3c4c7; padding:15px; margin-bottom: 20px;">';
$author_display = esc_html($plugin_info['AuthorName']);
if (!empty($plugin_info['AuthorURI'])) {
$author_display = sprintf('<a href="%s" target="_blank">%s</a> - <a href="%s" target="_blank">%s</a>', esc_url($plugin_info['AuthorURI']), esc_html($plugin_info['AuthorName']), esc_url($plugin_info['AuthorURI']), esc_url($plugin_info['AuthorURI']));
}
echo '<p><strong>Plugin:</strong> ' . esc_html($plugin_info['Name']) . ' ' . esc_html($plugin_info['Version']) . ' | <strong>Autor:</strong> ' . $author_display . '</p>';
echo '<p><strong>Sistema:</strong> WP ' . esc_html($wp_version_display) . ' | PHP ' . esc_html($php_version_display) . ' | <strong>Núcleos CPU:</strong> ' . intval($cpu_count) . '</p>';
echo '</div>';
// Configuración
echo '<h2>Configuración</h2>';
echo '<form method="post">';
wp_nonce_field('memory_logger_save_action');
echo '<table class="form-table">
<tr><th scope="row">Umbral Memoria (MB) / Max Log</th><td>
<input type="number" name="threshold_mb" value="'.$opts['threshold_mb'].'" step="1" style="width:80px;"> MB |
<input type="number" name="max_size" value="'.$opts['max_size'].'" step="1024" style="width:100px;"> bytes
<p class="description">Si el registro no muestra datos, prueba a bajar el umbral a 0 temporalmente.</p>
</td></tr>
<tr><th scope="row">Tiempo (s) [Aviso / Riesgo]</th><td>
<input type="number" name="time_warn" value="'.$opts['time_warn'].'" step="0.1" style="width:70px;"> /
<input type="number" name="time_risk" value="'.$opts['time_risk'].'" step="0.1" style="width:70px;">
</td></tr>
<tr><th scope="row">CPU Load [Aviso / Riesgo]</th><td>
<input type="number" name="cpu_warn" value="'.$opts['cpu_warn'].'" step="0.1" style="width:70px;"> /
<input type="number" name="cpu_risk" value="'.$opts['cpu_risk'].'" step="0.1" style="width:70px;">
<span class="description" style="margin-left:10px;">(Sugerido para '.$cpu_count.' cores: Aviso <strong>'.($cpu_count).'</strong> / Riesgo <strong>'.($cpu_count+5).'</strong>)</span>
</td></tr>
</table>';
echo '<p><input type="submit" name="save_memory_logger" class="button button-primary" value="Guardar configuración"></p>';
echo '</form>';
// --- AYUDA v3.1 ---
echo '<div style="margin-top:20px; margin-bottom:20px;">';
echo '<a href="#" onclick="var d=document.getElementById(\'memory_logger_help_box\'); d.style.display=(d.style.display===\'none\')?\'block\':\'none\'; return false;" style="text-decoration:none; font-size:14px; font-weight:600;">';
echo '<span class="dashicons dashicons-info"></span> Ayuda Completa: Cómo interpretar los datos y colores';
echo '</a>';
echo '<div id="memory_logger_help_box" style="display:none; background:#fff; border-left:4px solid #72aee6; padding:15px; margin-top:10px; box-shadow:0 1px 1px rgba(0,0,0,0.04);">';
echo '<h3>1. El Semáforo del Tiempo (Tiempo de Ejecución)</h3>';
echo '<p>Indica cuánto tarda el servidor en generar la página (PHP) antes de enviarla. Es clave para la experiencia de usuario y SEO.</p>';
echo '<ul style="list-style:disc; margin-left:20px; margin-bottom:15px;">';
echo '<li><strong>Referencia:</strong> Una página en caché debería tardar menos de <strong>0.5s</strong>. Una página dinámica normal, menos de <strong>1.0s</strong>.</li>';
echo '<li><strong>Configuración:</strong> El valor por defecto de Aviso (2s) es conservador. Si tu web está optimizada, puedes bajarlo a <strong>1s</strong>.</li>';
echo '</ul>';
echo '<p><strong>Guía de Colores:</strong></p>';
echo '<ul style="list-style:disc; margin-left:20px;">';
echo '<li><strong>⚫ Color Negro (Normal):</strong> Carga rápida. El servidor va sobrado.</li>';
echo '<li><strong style="color:orange">🟠 Color Naranja (Aviso):</strong> ¡Atención! La web va lenta (supera el 1er valor). Causa: Plugin pesado o falta de caché.</li>';
echo '<li><strong style="color:red">🔴 Color Rojo (Riesgo):</strong> ¡Peligro! Supera el 2º valor (ej. 5s). Riesgo inminente de error "Timeout" (pantalla blanca).</li>';
echo '</ul>';
echo '<hr style="margin:20px 0; border:0; border-top:1px solid #ddd;">';
echo '<h3>2. El Semáforo de la CPU (Carga del Servidor)</h3>';
echo '<p>Indica el estrés del procesador global. El valor de "Carga" (Load Average) depende del número total de núcleos (CPUs) del servidor.</p>';
echo '<ul style="list-style:disc; margin-left:20px; margin-bottom:15px;">';
echo '<li><strong>Hosting Compartido:</strong> Es normal ver valores altos (ej. 20, 30 o más) si el servidor físico tiene muchos núcleos (en este servidor detectamos <strong>' . intval($cpu_count) . '</strong>).</li>';
echo '<li><strong>Configuración:</strong> Debes ajustar el umbral de Aviso ligeramente por encima del "ruido de fondo" habitual que veas en el log. Si suele estar en 29, pon el aviso en 35.</li>';
echo '</ul>';
echo '<p><strong>Guía de Colores:</strong></p>';
echo '<ul style="list-style:disc; margin-left:20px;">';
echo '<li><strong>⚫ Color Negro (Normal):</strong> El servidor tiene capacidad de sobra.</li>';
echo '<li><strong style="color:orange">🟠 Color Naranja (Aviso):</strong> Carga alta. El servidor empieza a estar muy ocupado.</li>';
echo '<li><strong style="color:red">🔴 Color Rojo (Riesgo):</strong> Servidor saturado. La carga supera la capacidad física de los núcleos. La web irá lenta para todos.</li>';
echo '</ul>';
echo '<hr style="margin:20px 0; border:0; border-top:1px solid #ddd;">';
echo '<h3>3. El Semáforo de la Memoria (RAM)</h3>';
echo '<p>Indica cuánta RAM consume cada petición. El Umbral de configuración sirve para filtrar el log.</p>';
echo '<ul style="list-style:disc; margin-left:20px; margin-bottom:15px;">';
echo '<li><strong>Referencia:</strong> Un WordPress normal consume entre 60MB y 90MB. Si superas los 128MB, es un sitio pesado.</li>';
echo '</ul>';
echo '<ul style="list-style:disc; margin-left:20px;">';
echo '<li><strong>⚫ Color Negro:</strong> Consumo normal (menos de 80MB).</li>';
echo '<li><strong style="color:orange">🟠 Color Naranja:</strong> Consumo alto (más de 80MB). Típico en WooCommerce o Admin.</li>';
echo '<li><strong style="color:red">🔴 Color Rojo:</strong> Consumo muy alto (más de 120MB). Riesgo de agotar la memoria asignada.</li>';
echo '</ul>';
echo '<hr style="margin:20px 0; border:0; border-top:1px solid #ddd;">';
echo '<h3>4. El Semáforo de Consultas SQL (Base de Datos)</h3>';
echo '<p>Indica cuántas veces ha "preguntado" WordPress a la base de datos para construir la página. Muchas consultas = lentitud.</p>';
echo '<ul style="list-style:disc; margin-left:20px;">';
echo '<li><strong>⚫ Color Negro (Eficiente):</strong> Menos de 100 consultas. La página está bien optimizada.</li>';
echo '<li><strong style="color:orange">🟠 Color Naranja (Pesado):</strong> Entre 100 y 200 consultas. Puede ser normal en páginas complejas, pero vigilalo.</li>';
echo '<li><strong style="color:red">🔴 Color Rojo (Ineficiente):</strong> Más de 200 consultas. Algún plugin está haciendo demasiadas peticiones. Esto ralentiza mucho la carga.</li>';
echo '</ul>';
echo '<hr style="margin:20px 0; border:0; border-top:1px solid #ddd;">';
echo '<h3>5. Diagnóstico Avanzado: ¿CPU en negro pero Web lenta?</h3>';
echo '<p>Si <strong>CPU = Bien</strong> pero <strong>Tiempo = Mal</strong>, el servidor tiene potencia pero tu web está "atascada" por:</p>';
echo '<ul style="list-style:disc; margin-left:20px;">';
echo '<li><strong>Límites de tu cuenta:</strong> En compartidos, aunque el servidor tenga '.intval($cpu_count).' CPUs, tu cuenta puede estar limitada.</li>';
echo '<li><strong>Disco Lento (I/O Wait):</strong> Hay muchas webs usando el disco a la vez. Tu web espera turno (atasco en el peaje).</li>';
echo '<li><strong>Bloqueo PHP/DB:</strong> Un plugin o consulta SQL lenta (mira la columna SQL) está frenando todo.</li>';
echo '</ul>';
echo '</div>';
echo '</div>'; // fin ayuda
// Botones Acción
echo '<form method="post" style="margin-top:15px;display:inline-block;">';
wp_nonce_field('clear_memory_log_action');
echo '<input type="submit" name="clear_memory_log" class="button button-secondary" value="Vaciar registro (Recomendado al actualizar)">';
echo '</form>';
echo '<form method="post" action="' . admin_url('admin-post.php') . '" style="margin-top:15px;display:inline-block;margin-left:10px;">';
wp_nonce_field('export_memory_log_action', 'export_nonce_field');
echo '<input type="hidden" name="action" value="memory_logger_export" />';
echo '<input type="submit" name="export_memory_log_btn" class="button button-secondary" value="Exportar registro (.csv)">';
echo '</form>';
// Filtros
$current_log_type = isset($_GET['log_type']) ? sanitize_text_field($_GET['log_type']) : 'all';
$types = ['all' => 'Todo', 'Frontend' => 'Frontend', 'Backend' => 'Backend', 'Cron' => 'Cron'];
$admin_url = admin_url('admin.php?page=memory-log-viewer');
echo '<div class="tablenav top" style="margin-top:15px;"><div class="alignleft actions">';
echo '<form method="get" action="' . esc_url($admin_url) . '">';
echo '<input type="hidden" name="page" value="memory-log-viewer" />';
echo '<select name="log_type" id="log_type_filter">';
foreach ($types as $value => $label) {
printf('<option value="%s"%s>%s</option>', esc_attr($value), selected($current_log_type, $value, false), esc_html($label));
}
echo '</select> <input type="submit" class="button" value="Filtrar">';
echo '</form></div><br class="clear"></div>';
// Tabla
echo '<h2>Datos del Registro (Últimas 200)</h2>';
if ( file_exists($file) ) {
$content = @file_get_contents($file);
$lines = explode("\n", trim($content));
$last_lines = array_slice($lines, -200);
$filtered_lines_parts = [];
foreach ($last_lines as $line) {
$line = trim($line);
if (empty($line) || strpos($line, '[MEMORY-LOG]') !== 0) continue;
$data_part = substr($line, strlen('[MEMORY-LOG] '));
$parts = explode(' | ', $data_part);
// Filtro versión
if (count($parts) < 13) continue;
$request_type = trim(str_replace('TYPE: ', '', $parts[1]));
if ($current_log_type === 'all' || $request_type === $current_log_type) {
$filtered_lines_parts[] = $parts;
}
}
echo '<table class="widefat fixed striped">';
echo '<thead><tr><th>Fecha</th><th>Tipo</th><th>URL</th><th>Pico Memoria</th><th>Tiempo</th><th>SQL</th><th>Límite WP/Max</th><th>PHP</th><th>CPU (1m)</th></tr></thead><tbody>';
foreach ($filtered_lines_parts as $parts) {
$date = $parts[0];
$request_type = trim(str_replace('TYPE: ', '', $parts[1]));
$raw_url = trim(str_replace('URL: ', '', $parts[2]));
$url_display = (strlen($raw_url) > 50) ? '<span title="' . esc_attr($raw_url) . '">' . substr($raw_url, 0, 47) . '...</span>' : $raw_url;
$mem_pic_real_mb = trim(str_replace('Pic Real: ', '', $parts[4]));
$exec_time = trim(str_replace('EXEC_TIME: ', '', $parts[5]));
// Detección SQL v3
if (isset($parts[6]) && strpos($parts[6], 'SQL:') !== false) {
$sql_count = (int)trim(str_replace('SQL: ', '', $parts[6]));
$idx_offset = 1;
} else {
$sql_count = '-';
$idx_offset = 0;
}
$wp_limit = trim(str_replace('WP_LIMIT: ', '', $parts[6 + $idx_offset]));
$wp_max = trim(str_replace('WP_MAX: ', '', $parts[7 + $idx_offset]));
$php_limit = trim(str_replace('PHP_LIMIT: ', '', $parts[8 + $idx_offset]));
$cpu_load_string = trim(str_replace('CPU_LOAD: ', '', $parts[12 + $idx_offset]));
$cpu_parts_display = explode(',', $cpu_load_string);
$cpu_1m_display = isset($cpu_parts_display[0]) ? trim($cpu_parts_display[0]) : '-';
// Estilos
$pic_val = (float)filter_var($mem_pic_real_mb, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION);
$pic_style = ($pic_val > 120) ? 'color:red;font-weight:bold;' : (($pic_val > 80) ? 'color:orange;font-weight:bold;' : '');
$time_val = (float)filter_var($exec_time, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION);
$time_style = ($time_val > $opts['time_risk']) ? 'color:red;font-weight:bold;' : (($time_val > $opts['time_warn']) ? 'color:orange;font-weight:bold;' : '');
$cpu_val = (float)$cpu_1m_display;
$cpu_style = ($cpu_val > $opts['cpu_risk']) ? 'color:red;font-weight:bold;' : (($cpu_val > $opts['cpu_warn']) ? 'color:orange;font-weight:bold;' : '');
$sql_style = '';
if (is_numeric($sql_count)) {
if ($sql_count > 200) $sql_style = 'color:red;font-weight:bold;';
elseif ($sql_count > 100) $sql_style = 'color:orange;font-weight:bold;';
}
echo "<tr>
<td>{$date}</td>
<td><strong>{$request_type}</strong></td>
<td>{$url_display}</td>
<td><span style='{$pic_style}'>{$mem_pic_real_mb}</span></td>
<td><span style='{$time_style}'>{$exec_time} s</span></td>
<td><span style='{$sql_style}'>{$sql_count}</span></td>
<td>{$wp_limit} / {$wp_max}</td>
<td>{$php_limit}</td>
<td><span style='{$cpu_style}'>{$cpu_1m_display}</span></td>
</tr>";
}
echo '</tbody></table>';
} else {
echo '<p>No hay datos registrados aún.</p>';
}
echo '</div>';
}



