Capturar errores de Javascript en el lado del servidor

Cuando se produce una excepción en el código que se ejecuta en el servidor, ya sea desde el log de errores (si se maneja y se registrado) o en el event log (excepción no controlada en el caso de Windows), deberíamos ser capaces de obtener los detalles de un error para que puedas investigarlos. Esto está muy bien en el lado del servidor, pero capturar errores de JavaScript que ocurren en navegador del cliente no es tan sencillo. A veces, tenemos quejas de usuarios que dicen que una página web o una funcionalidad en concreto no les está funcionando correctamente. Como desarrolladores web, nos resulta difícil descubrir el problema si viene del lado del cliente.

Últimamente he pensado lo fácil que sería si fuéramos capaces de realizar un seguimiento de los errores de JavaScript que se producen en el navegador de los usuarios mediante un log. Y llamadme loco, pero lo he llevado a cabo y lo quiero compartir con todos vosotros.

Iré paso por paso para explicaros como implementar lo que os acabo de contar.

Crear un servicio HTTP

Para enviar los detalles de un error desde el navegador al servidor, necesitamos un simple servicio http. Basta con crear un proyecto Web API en VS y copiar y pegar el fragmento de código que puedes ver justo más abajo dentro del API controller.

static readonly string logFile=WebConfigurationManager.AppSettings["LogFilePath"];
static readonly object _syncObject = new object();
// api//trackLog
public void trackLog()
{
    try
    {
        string exData = Request.Headers.GetValues("Data").FirstOrDefault();
        Log(exData);
    }
   catch (Exception e) { }
}
private static void Log(string msg)
{
    if (!System.IO.File.Exists(logFile))
        System.IO.File.Create(logFile).Dispose();
    lock (_syncObject)
    {
        System.IO.File.AppendAllText(logFile, msg + " " + 
        DateTime.Now + Environment.NewLine + Environment.NewLine);
    }
}
  • Creamos un key 'LogFilePath' dentro de appSetting en el archivo .config y la asignamos con una ruta absoluta. Ejemplo: D:directoriolog.txt
  • Desde que estamos llevando a cabo la operación de I/O de escritura de datos en un solo archivo, se debe aplicar la sincronización. Sobre todo, en nuestro escenario, múltiples peticiones http podrán invocarse a la vez llamando a nuestro método Tracklog() en diferentes procesos.
  • En este ejemplo, hemos utilizado un http header personalizado para enviar los datos de error. Los parámetros POST también pueden ser utilizados, no habría problema alguno. Ten en cuenta que pasar parámetros GET utilizando cadenas para la consulta expondrá los datos que se integran la url. Prefiero que esta tarea sea totalmente invisible.
  • Extrae los datos del header y escribe en cualquier fuente, como por ejemplo SQL.

Una vez que esté todo realizado, la dirección Web APi debe ser algo parecido a esto:

protocolo:///api//trackLog

Detectar errores JS y enviarlos al servidor

Es bastante sencillo, basta con suscribirse al evento window.onerror de la página web. Así, cada vez que se produce una excepción, se invocará la función asignada con los detalles del error que a su vez disparará la llamada AJAX.

var browserDetails=getBrowser();
var errList=['SyntaxError','TypeError','RangeError','URIError'];
window.onerror = function(error) {
    logErr(error);
};
function logErr(error){
    if(errList.indexOf(error.name)>=0){
       $.ajax({
             url: 'api//trackLog',
             async: true,
             type: "GET",
             beforeSend: function(xhr){xhr.setRequestHeader
             ('Data', browserDetails + ' : ' + error);},
             success: function() { console.log('error logged : '+error); }
          });
    }
}
function getBrowser(){
    var ua=navigator.userAgent,tem,M=ua.match
    (/(opera|chrome|safari|firefox|msie|trident(?=/))/?s*(d+)/i) || []; 
    if(/trident/i.test(M[1])){
        tem=/brv[ :]+(d+)/g.exec(ua) || []; 
        return 'IE'+' '+(tem[1]||'');
    }   
    if(M[1]==='Chrome'){
        tem=ua.match(/bOPR/(d+)/)
        if(tem!=null) 
            return 'Opera'+tem[1];
    }   
    M=M[2]? [M[1], M[2]]: [navigator.appName, navigator.appVersion, '-?'];
    if((tem=ua.match(/version/(d+)/i))!=null) {M.splice(1,1,tem[1]);}
    return M[0]+' '+ M[1];
}

Introduce el código anterior en un archivo js e incluye la referencia en la página principal si quieres que se recojan los errores de toda la web, o si por el contrario solo quieres de una página específica. Al colocar esto en un archivo js separado, podemos activar y desactivar el logging, añadiendo o quitando la referencia al archivo js.

  • Además de los detalles del error, se pueden enviar otros datos como el nombre del navegador y la versión tal y como puedes ver en el código de arriba. Asegúrate que el código solo se ejecutará una vez colocándolo dentro del pageLoad o del document.ready, de modo que los datos del navegador se obtengan solo una vez y no cada vez que se produce un error de Javascript.
  • Los errores de JS se clasifican en varios tipos. Podemos incluir solo aquellos errores que deseamos que figuren en el log. He excluido los errores reference ya que no deseo seguirlos.
  • Un apunte importante. He colocado una llamada AJAX en una función JS separada, por lo que no se acopla con el onerror(). De modo que, en caso de si queremos registrar nuestros propios mensajes en el servidor, logErr() puede invocarlos mediante programación.

Y este ha sido el artículo en el que trataba explicaros cómo capturar errores de Javascript en el lado del servidor, esperamos que te haya gustado y sepas aplicarlo en tus futuros proyectos. Ya sabes que si nos quieres proponer un tema que quieres ver reflejado como un tutorial o como una práctica, solo tienes que hacer uso del área de comentarios de un poco más abajo. Por el contrario, si quieres enviarnos tus propios tutoriales, puedes hacerlo a través de la intranet de usuarios que está habilitada para ello, a través del menú Enviar Tutorial. Ya sabes, ayúdanos a crecer con tus conocimientos. ¡Un saludo y feliz código!

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP
ARTÍCULO ANTERIOR