Tracking Jquery Ajax Errors and Exceptions

| Comments

The web had already shifted from static web pages to ajax driven websites for good. This shift have made the web awesome and faster but have introduced some new problems which need to taken care of . One of these is failed ajax requests. There are many reasons for a ajax request to fail, some of them are

  • Api End points are down or unreachable
  • Unauthorized / Invalid data requested
  • Some error happened at the API back-end
  • Edge cases are not handed properly in the code

Normally the ajax pull data from self hosted as well 3rd party hosted APIs. In both cases, the failure of ajax request has to handled at both ends, but front end is more important because of its high impact on the user experience. In case of 3rd party APIs, there is any way no possibility to have control over back-end, so front-end is the default choice.

The browser behaves differently when it comes to make a ajax calls for same origin and ajax call for cross origin, and therefore exceptional handling also have to different.

Same Origin

In jQuery, the simplest way to catch all the ajax errors and exceptions on a web page

$(document).ajaxError(function(event, jqXHR, ajaxSettings, thrownError) {

  // This is the default error handler for ajax request.

  // Extract all the information required to understand.
  var requestResponse = {
    url: ajaxSettings.url,
    method: ajaxSettings.type,
    data: ajaxSettings.data,
    httpStatus: jqXHR.status,
    error: thrownError || jqXHR.statusText,
    data: ajaxSettings.data
  };

  console.error(requestResponse)

  // Notify the user so he might not wonder.
  alert('Something went wrong, Please try again');

  // Report it back for fixing it
  // Only for debuggify users
  var ajaxErrors = debuggify.Logger.get('ajax_errors');
  ajaxErrors.attach(requestResponse);
  ajaxErrors.error('Caught ajax error');

});

One important thing to note here is this handler is not called for cross-domain scripts and cross-domain JSONP requests which made its easy to avoid any ajax errors happening in application other than your native javascript code.

Note If $.ajax() or $.ajaxSetup() is called with the global option set to false, the .ajaxError() method will not fire.

Apart from this generic handler, it is possible to have a dedicated error handler for each type of request.

$.ajax({

  type: "POST",
  url: "https://api.twitter.com/1.1/statuses/user_timeline.json",
  error: function(jqXHR, textStatus,errorThrown) {
    var requestResponse = {
      httpStatus: jqXHR.status,
      error: thrownError || jqXHR.statusText,
    };

    console.log(requestResponse);

    // Notify the user so he might not wonder.
    alert('Something went wrong, Please try again');


    // Report it back for fixing it
    // Only for debuggify users
    var ajaxErrors = debuggify.Logger.get('ajax_errors');
    ajaxErrors.attach(requestResponse);
    ajaxErrors.error('Caught ajax error');

  }
});

Event this dedicated handler is not called for cross-domain script and cross-domain JSONP requests.

Cross Origin

The limitation for ajax exception handlers is not just a jQuery limitation, but it is a javascript limitation. The reason behind this limitation is that most browser are not giving permissions to access error messages for cross domain scripts due to security reasons.

There are some solutions (or workarounds) for this. (Reference stackoverflow )

First one, if backend is accessible, set (in my example PHP) headers to allow a cross domain call. When you do this, then JavaScript accepts the call, and no crossdomain tricks are needed.

    header('Access-Control-Allow-Origin: http://domain1.com, http://domain2.com'); //whitelist

Secondly if there is no access to backend, a timeout based workaround can be used to get a error callback

$.ajax({
    type: "POST",
    url: "https://api.twitter.com/1.1/statuses/user_timeline.json",
    success: start_map,
    timeout: 2000, // 2 seconds timeout before error function will be called
    dataType: 'script',
    crossDomain: true
});

This is a nasty trick to solve the problem as it can definitely screw up things for slow internet connection.

Another alternative option is using the jQuery plugin jquery-jsonp. This plugin is using script onload / onerror method along with timeout to get the error callback.

New API’s to Add Custom Data Points

| Comments

In the past we have received many requests related to attach some custom data along with every messages collected.

We have added this functionality, and to simplify it, we have come with 3 new APIs

The Alias API

Using this api, its possible to set an alias to the unique user identification used by the debuggify. This make it easy to map a debuggify user identifier to your site username or email id. Remember the old data will not have alias.

debuggify.alias('foo@example.com');

Its pretty straight forward to use the above api.

Note Make sure every unique user should be given the one alias to avoid any data inconsistency.

The Metadata API

To attach custom data along with every message

debuggify.metadata({'login': true});

The Attach API

To attach custom data along with a single message. This api need to be called on the logger object

var ajaxErrors = debuggify.Logger.get('ajax_errors');

ajaxErrors.attach({
  type: 'POST',
  url: '/api/notification'
});

ajaxErrors.error('Error in Notification Api');

After calling attach, it must be followed by the one of the logging Apis like .log, .error, .warn, .debug or .message to push that data to the server.

Note: The attached data will expire after its once used by logging Api’s. To send data with every request use .metadata api

We soon be adding use cases on how to use this APIs more powerfully.

Install Phpmyadmin Behind Nginx on Ubuntu 12.04 LTS

| Comments

Setup dependencies

Setup Php, mysql & phpmyadmin:

  sudo apt-get install php5-fpm php-apc mysql-server mysql-client phpmyadmin nginx

Enter your MySQL password and phpmyadmin webinterface password when prompted

Enable mysql extension by editing /etc/php5/fpm/php.ini

sudo vim /etc/php5/fpm/php.ini

Add the following line and save

extension=mysql.so

Restart to use new config

  sudo /etc/init.d/php5-fpm restart
  sudo /etc/init.d/nginx restart

Setup Nginx config:

Next create a basic nginx vhost configuration in /etc/nginx/sites-available/ directory as follows:

sudo vim /etc/nginx/sites-available/phpmyadmin

And add the following:

server {

       listen 80;
       server_name admin.lvh.me lvh.me;
       root /var/www/phpmyadmin;
       if ($http_host != "admin.lvh.me") {
                 rewrite ^ http://admin.lvh.me$request_uri permanent;
       }
       index index.php index.html;
       location = /favicon.ico {
                log_not_found off;
                access_log off;
       }
       location = /robots.txt {
                allow all;
                log_not_found off;
                access_log off;
       }
       # Make sure files with the following extensions do not get loaded by nginx because nginx would display the source code, and these files can contain PASSWORDS!
        location ~* \.(engine|inc|info|install|make|module|profile|test|po|sh|.*sql|theme|tpl(\.php)?|xtmpl)$|^(\..*|Entries.*|Repository|Root|Tag|Template)$|\.php_ {
                deny all;
        }
       # Deny all attempts to access hidden files such as .htaccess, .htpasswd, .DS_Store (Mac).
       location ~ /\. {
                deny all;
                access_log off;
                log_not_found off;
       }
       location ~*  \.(jpg|jpeg|png|gif|css|js|ico)$ {
                expires max;
                log_not_found off;
       }
       location ~ \.php$ {
                try_files $uri =404;
                include /etc/nginx/fastcgi_params;
                fastcgi_pass 127.0.0.1:9000;
                fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
       }

       location /phpmyadmin {
               root /usr/share/;
               index index.php index.html index.htm;
               location ~ ^/phpmyadmin/(.+\.php)$ {
                       try_files $uri =404;
                       root /usr/share/;
                       fastcgi_pass 127.0.0.1:9000;
                       fastcgi_index index.php;
                       fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
                       include /etc/nginx/fastcgi_params;
               }
               location ~* ^/phpmyadmin/(.+\.(jpg|jpeg|gif|css|png|js|ico|html|xml|txt))$ {
                       root /usr/share/;
               }
        }
        location /phpMyAdmin {
               rewrite ^/* /phpmyadmin last;
        }
}

To enable that vhost, we create a symlink to it from the /etc/nginx/sites-enabled/ directory:

cd /etc/nginx/sites-enabled/
sudo ln -s /etc/nginx/sites-available/phpmyadmin phpmyadmin

Reload nginx for the new configuration to take effect:

sudo /etc/init.d/nginx reload

Open the link in the browser http://admin.lvh.me/phpmyadmin

Error in Javascript Comments

| Comments

Last week, I was writing code in javascript when I found something that completly blew off my mind. This is what I found

Stupid IE

Yeah, IE thowing error in javascript comment. For some time I didnot believe what I am seeing, but gradually I came to my senses. Like most developers, first thing I did was started cursing IE on twitter, but later reality struct me and that is to find a solution to this mess.

I dig in a lot of wrong places until I found conditional comments in IE. A sample of how the conditional logic works is below

<script>
/*@cc_on

  @if (@_jscript_version == 10)
    document.write("You are using IE10");

  @elif (@_jscript_version == 9)
    document.write("You are using IE9");

  @elif (@_jscript_version == 5.8)
    document.write("You are using IE8");

  @elif (@_jscript_version == 5.7 && window.XMLHttpRequest)
    document.write("You are using IE7");

  @elif (@_jscript_version == 5.6 || (@_jscript_version == 5.7 && !window.XMLHttpRequest))
    document.write("You are using IE6");

  @elif (@_jscript_version == 5.5)
    document.write("You are using IE5.5");

  @else
    document.write("You are using IE5 or older");

  @end

@*/
</script>

To fix this all I need to do is to remove the whole comment. This comment is anyways useful for sourcemaps supported browsers which is long way to go for IE.

Anyways this is just a hack for the time being. The sourcemap specs has been updated due to this problem. To know more check the uglifyjs issue on github

Finally all I have to say is God Bless IE Developers

Debug Javscript in Production

| Comments

Today I had great time sharing my experience on javascript debugging with the javascript ninja community at Bangalore

Here is the presentation


I will be explaining each point in details in the future posts.

I know I have abandoned the blog for long time but not any more. Stay connected for more.

Happy Debugging !!

Window.onerror Is Not Enough

| Comments

Scott Hanselman blogged about JavaScript the assembly language for the web. There a plenty of languages that compiles to javascript. With the addition of Dart from Google and Typescript from Microsoft a war has broke out for better.

With the rise of compiled to javascript languages it hard to traceback the errors to original source. Almost all modern provides the window.onerror api to catch all uncaught exceptions on a page. This api provides very limited information (error message, filename and line number only) which is the root cause of many problems.

With modern web development practices the amount of javascript code per page is growing rapidly. Also this code is minified by the minification tools(like Google Closure Compiler, UglifyJS2 etc) to reduce the code size. After minification whole code ends in one single line. Here is where most problem starts. I am listing some really annoying issues related to window.onerror below

  • Missing Char No: The char no is very important for the minified code to point at the exact statement as there is only one line in minified code. Even source maps cannot be helpful in tracking back to original line.

  • No Call Stack: The call stack is completely hidden from the developer so its hard to identify the functions flow. However there is a trick to extract stack in IE9 only.

  • Cross Origin Errors: These types are thown when some cross origin permission is broken. They are very common on pages with facebook like and google plus widget installed. For such errors window.error message throw a single message “Script Error”. As per stackoverflow post this behavior is intentional to avoid some security risks but its at expense of useful debugging information.

Most of above mentioned problems are roadblock in debugging production websites / applications. There is a need for better tools and libraries for the job.

I will soon be writing a post on Best Debugging Practices For Javascript In Production Environment

Why Debuggify?

| Comments

Who am I?

I was born 6 years back when I bought my first computer and a internet connection. To be frank, Internet is a second home to me. For most of people out there who feel same, are continuously trying to make Internet a better place.

I am also a computer science engineering grad, so I am surrounded by technology. In order to feed my engineering inquisitiveness I have explored the black, white and gray sections of the internet.

Where it all started?

I am a full stack web developer by profession and have authored / co-authored many 3rd party social plugins for Shareaholic Inc which runs on thousands of different blogs and websites driving 1+ billion pageviews per month. These plugins can be installed on different CMS/blogging platform like Wordpress, Drupal, Tumblr, Blogger etc. While working on these plugins I closely interact with the different elements of the modern internet ecosystem.

What’s my use case of 3rd party plugin ?

A 3rd party plugin has to be robust enough to work in 3rd party environments while interacting with 3rd party apis and support every major browser vendor out there

Elaborating the above quote

  • As plugins runs on 3rd party websites, it has to be robust so it doesn't break itself and neither the website
  • It has to be fast enough to deliver good page load time
  • It should not conflict with other existing plugins
  • It should support major browser vendors including internet explorer
  • It should also support legacy browser versions like ie6 ie7 etc.
  • As plugins interact with over 200+ 3rd party apis, it’s hard to keep track when something break
  • It should be easy to setup with multiple cms/blogging environments for better distribution

Technical challenges faced ?

  • In 3rd party environment, developers have limited or no control over things, so it’s hard to debug and reproduce bugs / issues
  • If some plugin feature is not working its hard to know whether
    • it’s not working for a particular website,
    • or not working for a set of URLs
    • or for a particular CMS/Blogging environment
    • or for a particular screen resolution
    • or for a particular browser vendor
    • or even for a particular version of a browser vendor
    • or for all cases
  • Its hard to judge the impact of every release, whether it fixed the problem or introduced some new side effects
  • The 3rd party API’s are continuously changing, upgrading & depreciating. Companies like Facebook, Google, Twitter are continuously bashing developers with bleeding edges of their API’s
  • With HTML5, ES Harmony and CSS3, browsers are evolving faster than ever, so they are also releasing bleeding edge features and API’s

Who else is facing problems?

As all wise developers know

Zero Bugs is a Myth

I have talked to a few friends about the problems. All are encountering similar set of problems on the daily basic. A majority their development time is spend in debugging and fixing bugs. The debugging is also becoming hard due to the evolving nature of the web.

What to do about it?

So I finally decided to go on a quest to make Internet a better place for developers.

Why Developers ?

Developers are the keepers of the modern internet.

The web is evolving because of the developers. There are around 1+ million developers on the planet. I can build something that make developers more productive and can save up to 1hr/day of their time, it’s going to be revolutionary. Developers can reinvest this time in open source projects, new innovations or anything that eventually make Internet a better place

So that is Debuggify is all about. Contact me if you want to join me on my quest.