Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / ASP.NET

Logging JavaScript Errors at Server Side

4.78/5 (12 votes)
2 Dec 2015CPOL3 min read 19.6K  
Capturing JavaScript errors occured in user's browser at Server side

Introduction

When an exception occurs in the code which is running on the server, either from log file (if handled and logged) or from event log (unhandled exception in case of Windows), we would be able to get error details for investigation. It's fine on the server side but capturing JavaScript errors occurring at client browser is not that easy. At times, we get complaints from users saying a web page or a functionality in it is not working properly. As a web developer, we find it hard to find out the issue.

I personally ran into such a situation recently and thought how easy it would be if we were able to track the JavaScript errors occurring at users' browser.

Will go step by step to implement a solution for this.

1. Create a HTTP Service

To send error details from browser to server, a simple http service is required. Just create a Web API project in VS and copy paste the below piece of code inside the Web API controller.

C#
static readonly string logFile=WebConfigurationManager.AppSettings["LogFilePath"];
C#
static readonly object _syncObject = new object();
C#
// api/<your_controller>/trackLog
public void trackLog()
{
    try
    {
        string exData = Request.Headers.GetValues("Data").FirstOrDefault();
        Log(exData);
    }
   catch (Exception e) { }
}
C#
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);
    }
}
  1. Create a key 'LogFilePath' inside appSetting in .config file and assign it with absolute file path.
    Example: D:\somefolder\log.txt
  2. Since we are performing I/O operation by writing data to single file, synchronization needs to be applied. Especially, in our scenario, multiple http requests may be invoked which in turn will call our controller method trackLog() by different process.
  3. In this example, Custom HTTP Header (Data) is used to carry the error data. POST can also be used based on our convenience. Please note that HTTP GET request using query string will expose the data that are being passed in the URL which I don't want. Prefer to be a silent task.
  4. Extract data from header and write the same to the disk or any other source, say SQL.

Once all is done, Web API address should be something like below

protocol://<your_site_name>/api/<your_controller_name>/trackLog

2. Detect JS Exceptions and Fire a Request to Server

It's pretty simple, Just subscribe to window.onerror event on the web page. So, whenever an exception occurs, assigned function will be invoked with error details which in turn will fire AJAX call.

JavaScript
var browserDetails=getBrowser();
JavaScript
var errList=['SyntaxError','TypeError','RangeError','URIError'];//ReferenceError excluded
JavaScript
window.onerror = function(error) {
    logErr(error);
};
JavaScript
function logErr(error){
    if(errList.indexOf(error.name)>=0){
       $.ajax({
             url: 'api/<controller_name>/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];
}

Pack the above code into a js file and include reference in master page if you require logging for the entire website or in a specific page based on your requirement. By placing it in a separate js file, we can switch logging on/off adding/removing this js file reference.

  1. In addition to the exception details, browser name & version can also be sent as illustrated above. Make sure that this code will be executed only once by placing it inside PageLoad or document.ready, so that browser details will be get only once and not everytime when exception occurs.
  2. JS Exceptions are classified into various kinds. We can include only those exceptions which we target to log. I have excluded Reference error as I don't want to track it.
  3. As and when an exception occurs, window.onerror will invoke the assigned function which will invoke logErr() ultimately.
  4. A note here. I have placed AJAX call in a separate JS function, making not tightly coupled with onerror(). So that, in case if we want to log our own messages to the server, logErr() can be invoked programmatically.

Example: logErr('data is null');

Captured JS Exceptions in Log File

logging snapshot

 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)