Contents
Introducción
Este artículo explica, paso a paso y con todo detalle técnico, cómo instrumentar errores de JavaScript en un sitio WordPress y reportarlos a un endpoint en el propio WordPress. Verás la implementación front-end (captura y envío de errores), el backend en WordPress (ruta REST, validación, almacenamiento), consideraciones de seguridad, rendimiento, privacidad y cómo visualizar los registros desde el panel de administración.
Visión general
La idea general es:
- Capturar errores JS y promesas no gestionadas en el cliente.
- Enriquecer el evento con contexto (URL, navegador, user agent, usuario WP si aplica, tags personalizados).
- Enviar los eventos al servidor mediante la REST API de WordPress o un endpoint admin-ajax/privado.
- Validar y almacenar los eventos en la base de datos (tabla propia) o externalizar a un servicio de terceros.
- Proveer un panel de administración para consultar y filtrar los eventos.
Recomendaciones iniciales
- Uso de source maps: Sin source maps las trazas están ofuscadas/minificadas. Para una trazabilidad correcta, habilita source maps en producción (con protección) o sube las versiones map a un bucket privado o servicio de resolución de mapas.
- Muestreo (sampling): No envíes el 100% de errores en sites de alto tráfico aplica sampling, por ejemplo 1% o 5% por tipo de error, con reglas para errores críticos.
- Protección de datos y GDPR: No envíes datos personales (PII). Si es necesario, anonimiza (usuario_id en lugar de email) y documenta en la política de privacidad.
- Rendimiento: Haz el envío en background, utiliza batching y backoff exponencial para reintentos.
Captura de errores en el cliente (JavaScript)
Debes capturar:
- Errores sin capturar: window.addEventListener(error)
- Promesas rechazadas no gestionadas: window.addEventListener(unhandledrejection)
- Error global legacy: window.onerror (opcional, pero útil para navegadores antiguos)
Ejemplo de implementación client-side
Este script captura errores, construye una carga útil (payload) y la envía a la REST API de WordPress. Se incluyen batching, reintentos simples y protección de sampling.
// Código: instrumentación de errores en cliente (function(){ // Configuración inyectada por WP (localize_script) var ENDPOINT = window.__WP_ERROR_ENDPOINT /wp-json/my-errors/v1/collect var NONCE = window.__WP_ERROR_NONCE null var SAMPLE_RATE = (window.__WP_ERROR_SAMPLE_RATE 1) // 1 == 100% var BATCH_SIZE = 10 var BATCH_INTERVAL = 5000 // ms var queue = [] var sending = false function shouldSend() { return Math.random() < SAMPLE_RATE } function buildPayload(errData) { return { site_url: location.origin, page: location.href, referrer: document.referrer null, ua: navigator.userAgent, timestamp: new Date().toISOString(), error: errData } } function enqueue(event) { queue.push(event) if (queue.length >= BATCH_SIZE) { flush() } } function flush() { if (sending queue.length === 0) return sending = true var batch = queue.splice(0, BATCH_SIZE) var body = JSON.stringify({events: batch}) var headers = {Content-Type:application/json} if (NONCE) headers[X-WP-Nonce] = NONCE fetch(ENDPOINT, {method: POST, body: body, headers: headers, credentials: same-origin}) .then(function(res){ sending = false if (!res.ok) { // Re-enqueue with simple retry/backoff (push front) queue = batch.concat(queue) // Optional: schedule retry setTimeout(flush, 5000) } }).catch(function(){ sending = false // Put events back and retry later queue = batch.concat(queue) setTimeout(flush, 5000) }) } // Handler para window.onerror window.onerror = function(message, source, lineno, colno, error) { try { if (!shouldSend()) return var err = { type: error, message: message, source: source, lineno: lineno, colno: colno, stack: error error.stack ? String(error.stack) : null, } enqueue(buildPayload(err)) } catch(e) {} // No bloquear ejecución } // Handler para promesas no gestionadas window.addEventListener(unhandledrejection, function(e) { try { if (!shouldSend()) return var reason = e e.reason var err = { type: unhandledrejection, message: (reason reason.message) String(reason) unknown, stack: (reason reason.stack) ? String(reason.stack) : null } enqueue(buildPayload(err)) } catch (ex) {} }) // Handler de errores capturados por event listeners (p.ej. recursos) window.addEventListener(error, function(e) { try { if (!shouldSend()) return // event.target puede ser,