Sistema de logging personalizado en aplicaciones JavaScript y las limitaciones de la consola del navegador .
En el desarrollo de aplicaciones web es muy importante estar al corriente de los problemas que nos alerta la consola del navegador. Sin embargo, esa información suele quedarse en el mismo navegador del usuario que mira nuestra web y para nosotros es inaccesible cuando algo falla en producción. Además, lo que vemos como errores (los típicos Uncaught ReferenceError
, TypeError
, etc.) no siempre es todo lo que necesitamos.
Por eso tiene sentido agregar una capa de registro personalizada. Nos permite interceptar eventos clave, capturar más contexto y enviar los datos a servicios que sí podemos consultar después, como Sentry, TrackJS o incluso una API propia.
El navegador hace un buen trabajo mostrando errores en tiempo real mientras desarrollamos una página web. Pero en producción, salvo que tengamos acceso al dispositivo del usuario, no podemos qué está ocurriendo. Además:
console.warn
) y mensajes informativos (console.info
) también pueden indicar problemas futuros.Capturar y registrar errores de manera personalizada permite reaccionar cuando los usuarios se vean afectados. También ayuda a identificar patrones o fallos recurrentes que, de otro modo, pasarían desapercibidos.
Generalmente, se recogen los errores globales con window.onerror
o window.addEventListener('error', ...)
, se enriquecen con información adicional (como el navegador, la URL actual, el usuario autenticado si lo hay) y luego se envían a servicios como:
También conviene filtrar los errores para evitar ruido (por ejemplo, errores de extensiones del navegador o dominios externos).
Un ejemplo mínimo de cómo registrar errores no capturados y enviarlos mediante una api a un servicio propio podría ser el siguiente:
window.onerror = function(message, source, lineno, colno, error) { const errorData = { message, source, lineno, colno, stack: error?.stack || null, userAgent: navigator.userAgent, url: window.location.href, timestamp: new Date().toISOString() };
// Enviar a un endpoint propio o a un servicio externo fetch('/api/log-error', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(errorData) });};
Además, podemos capturar errores en promesas no gestionadas:
window.addEventListener('unhandledrejection', function(event) { const errorData = { reason: event.reason, type: 'unhandledrejection', timestamp: new Date().toISOString() };
fetch('/api/log-error', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(errorData) });});
Y si queremos registrar advertencias también, sobreescribiendo console.warn
para que nos envía el mensaje cuando ocurra un warning:
const originalWarn = console.warn;console.warn = function(...args) { fetch('/api/log-warning', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ type: 'warn', args, timestamp: new Date().toISOString() }) }); originalWarn.apply(console, args);};
breadcrumbs
) para reconstruir el flujo antes del fallo.El registro de errores personalizado nos da visibilidad sobre lo que realmente ocurre en producción. Nos permite detectar problemas antes de que escalen o errores que se nos habían escapado, reunir más contexto para diagnosticar y, en algunos casos, incluso anticipar errores gracias al análisis de advertencias. Aunque puede empezar con unas pocas líneas de código, su impacto a largo plazo en la estabilidad del proyecto es significativo.