How to track errors in JavaScript Web applications


Your wonderful one-of-a-kind web application just had a successful launch and your user base is rapidly growing. To keep your customers satisfied, you have to know what issues they face and address those as fast as possible.

One way to do that could be being reactive and waiting for them to call in – however, most customers won’t do this; they might just stop using your app. On the flip side, you could be proactive and log errors as soon as they occur in the browser to help roll out fixes.

But first, what error kinds exist in the browser?

Errors

There are two kinds of errors in JavaScript: runtime errors which have the window object as their target and then resource errors which have the source element as the target.

Since errors are events, you can catch them by using the addEventListener methods with the appropriate target (window or sourceElement). The WHATWG standard also provides onerror methods for both cases that you can use to grab errors.

Detecting Errors

One of JavaScript’s strengths (and also a source of much trouble too) is its flexibility. In this case, it’s possible to write wrappers around the default onerror handlers or even override them to instrument error logging automation.

Thus, these can serve as entry points for logging to external monitors or even sending messages to other application handlers.

//logger is error logger
var original = window.onerror; //if you still need a handle to this
window.onerror = function(message,source,lineNo,columnNo,errObject){
    logger.log('error', {
        message: message,
        stack: errObject && errObject.stack
    });
    original() //if you want to log the original
    return;
}

var elemOriginal = element.onerror;
element.onerror = function(event) {
    logger.log('error', {
        message: event.message,
        stack: event.error.stack
    });
    elemOriginal();
    return;
}

The Error Object

The interface for this contains the error message and optional values: fileName and lineNumber. However, the most important part of this is the stack which provides information about the stack.

Note: Stack traces vary from browser to browser as there exists no formatting standard.

Browser compatibility woes

Nope, you ain’t getting away from this one.

Not all browsers pass in the errorObject (the 5th parameter) to the window.onerror function. Arguably, this is the most important parameter since it provides the most information.

Currently the only big 5 browser that doesn’t pass in this parameter is the Edge browser – cue the only ‘edge’ case. Safari finally added support in June.

The good news though is there is a workaround! Hurray! Let’s go get our stack again.

window.addEventListener('error', function(errorEvent) {
    logger.log('error', {
        message: event.message,
        stack: event.error.stack
    });
});

And that wraps it up! You now have a way to track errors when they happen in production. Happier customers, happier you.

Note: The eventListener and window.onError approaches might need to be used in tandem to ensure enough coverage across browsers. Also the error events caught in the listener will still propagate to the onError handler so you might want to filter out duplicated events or cancel the default handlers.

Related

Tips for printing from web applications

Liked this article? Please share, subscribe or drop a comment.

Advertisements

For Devs only


I try to do less to achieve more – it is good; it makes me do my job faster and more easily; you should do so too. Automate, use shortcuts, innovate; well the initial investment might take a lot of time but it’s something you will be glad you did.

You can learn a lot by just exploring the tools you use daily. Just try to find a simpler way: some previously unknown feature in your favourite IDE, a macro to allow you repeat a series of actions or just something that saves you some keystrokes. So, I decided to post a couple of tips; I do hope you find them cool and pick up one or two new ideas.

Warning: Some of these work on linux only.

1. Bash Shortcuts

I stumbled upon these while going across someone’s .bashrc file and it stuck me as awesome; yes it is plain awesome. For example, accessing my /var/www/my-web-project folder (and I do access it often) is a pain; why do I have to type cd /var/www/my-web-project all the time? Solution? Just add the following shortcut to your .bashrc:

alias myproj=’cd /var/www/my-web-project’

and that’s it; I just type myproj in bash and am routed to that location. And yes, it doesn’t matter what directory I am in.

2. Bookmarks

Bookmarks are cool, I think they are absolutely essential. Now, if you don’t know how to use them or don’t use them in your IDE/Editor; stop reading and find how to use this wondrous time saver. I use bookmarks a lot in vim and Netbeans (and all other editors if possible); this allows me to jump around and move around. Yes, find out how to use bookmarks and use them!

Talking about vim; I think it’s really awesome and allows you to effortlessly edit anything that is text: I use it for code, latex, writing anything everything. At times, it might be easier to use a less powerful editor however most times, it does all and really cool too. And if you’re a ‘vimmer’, then try out these plugins: nerdtree, nerdcommenter, syntastic and latexbox (if you write in latex).

3. Debugger Statement (JavaScript)

I picked up this while watching an EmberJS tutorial and it has become a major part of my arsenal. Maybe I overuse it (well, I have to write so much JavaScript nowadays). Just put a debugger statement in any part of your JavaScript code and fire up that page in your browser; whenever execution gets to that point, it’ll automatically stop. I use this virtually everytime nowadays and for JavaScript devs; it’s a must use. Another cool JS tip involves using the console object; I use console.log too often but there are a couple of other cool functions like console.trace(), console.dir() etc.

4. Picking an object with a particular Characteristic

I picked up this trick while working in Python on my thesis: I often needed to pick up objects with certain properties out of long lists of objects. The interpreter is essential and this is what I do:

for obj in objectList:

if obj.ppty == criteria:

break

obj

Obj is now equal to my desired object.

5. Testing for -1 (Nice Discovery from SO)

I picked up this explanation from stack overflow and couldn’t help sharing it. I do hope you wouldn’t write code like this but it is always great to gain some insight into how things work.

Assuming you want to test if something is equal to -1; in 2’s complement systems, the representation of -1 is 11111111 (for whatever number of bits the system supports). Just take the bitwise inverse of that number and all you get is zeros. Now for the cool part, zero is a falsy value which can be used in your tests. Here is the link.

Hope you picked up something or the other from this; if you did; do share it with someone who might pick a point or another from it.

Did you like this post? Try out some of my other posts:

Events in JavaScript


JavaScript events are created in response to user actions such as clicks, mouse moves or key presses; not all events are triggered by user actions though, some are automatic such as the onPageLoad event. JavaScript’s event model allows developers to write event handlers which respond to these events and provide the interactivity we have come to love.

Most JavaScript code is written around events; when an event is fired, your handling code is triggered and a response is created for that action. For example, when a user clicks a send button, an AJAX call is initiated and the page is updated with new data. Or when you like this blog post, event handling is also involved. :)

Event models were created while the browser wars were raging – hence it’s not surprising both parties chose incompatible models; the W3C model came out even more recently. Due to these inconsistencies, it is quite difficult to get event handlers to work across all browsers however the good news is that a lot of libraries (e.g. the ubiquitous jQuery) handle the gory details for you.

Whose Event is it?

Suppose an element and one of its parents have event handlers for the same event (say a click or hover event), which one should fire first? The parent? The child?

As you’ll expect, the Microsoft and Netscape models differ: the Microsoft approach is called event bubbling i.e. events start from the child and propagate up to the parent (i.e. bubbling). The Netscape approach is called event capturing, the parent’s handler is triggered first and then the event is passed on to children.

Assuming a click event happens on the click area below:

<div>
    <span>Click area </span>
</div>

Capture mode: The div’s click handlers will be triggered first followed by the span’s handlers.

Bubble mode: The span’s click handlers will be triggered first and then the div’s handlers will fire.

The W3C specification supports both approaches: events are first ‘captured’ progressively until the target element is reached; next, the events bubble up from the target element. I wonder why this two-stage approach is preferred; sounds like more work to me.

Catch that event!

The addEventListener(type, listener, useCapture) method is the way to go about it, you specify the event type, handler function and whether to use the capture or bubble mode as the three parameters. Passing in false as the third argument creates the bubbling mode while passing in true initiates the capture model.

You can also stop events from bubbling or propagating beyond a certain element. Just call event.stopPropagation()  (W3C browsers) or set event.cancelBubble = true (for IE < 9) in the event handler.

Whose event art thou?

To access the original source of the event, i.e. the element on which the event was triggered, you use the event.target (Netscape-y browsers) or event.srcElement (IE-y browsers). What if it’s being captured or bubbling? It doesn’t matter – this reference always points to the culprit.

However, say your event is bubbling up or capturing down ( I just invented that ), and you want to check the current element, you can use the event’s currentTarget property. This has a reference to the HTML element that the event is being handled by, for example a parent element. Unfortunately, this is not supported in the Microsoft event model.

To Capture or to Bubble?

Capturing has been shown to have a slight performance advantage over bubbling while bubbling can be used to cut the number of event handlers you need. For example, you can put a handler on a container that will capture all events on child elements when they bubble up. I like this approach because it saves you from having to add new listeners whenever a new child is added to the container.

Excellent Event Model Demo

You most probably won’t need to write your event handling code these days; but knowing how things work is always good, not so?

Do you like this post? Check out my other posts on JavaScript, its functional programming parts and a book review.

Access a webpage inside an Android Activity


While working on an Android app, I needed to get people to sign into Facebook however I didn’t want them to leave my app. The simple solution was to launch the browser through an intent; however, this was going to stop my activity which was unacceptable. I wanted a solution that enabled users to sign into the Facebook platform from inside the app. I had to embed a webview inside a dialog and pop up the required webview. How? Read on

Create the Activity that pops up the dialog

public class WebviewActivity extends Activity {
    private static final String REGISTRATION_URL = "http://10.0.2.2/test.php";
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.blog_learning);
        new blog(this, REGISTRATION_URL, new DialogListener() {
            @Override
            public void onComplete(JSONObject values) {
                // Do processing here with the JSONObject, you can change it if you so desire
                Log.d("Oncomplete","called");
            }
            @Override
            public void onError(DialogError e) {}
            @Override
            public void onStop() {}
        }).show();
    }
}

Create a Listener interface that links your Activity to the Dialog

public interface DialogListener {
    public void onComplete(JSONObject values);
    public void onError();
    public void onStop();
}

The listener provides a callback pathway that monitor the state of the dialog; you can use it to get responses, check errors and do similar activities.

The Dialog class

Also, one thing, the Layouts and buttons are declared programmatically in the code, I couldn’t find a way to automatically load layout files in the onCreate method of the Dialog. If you know how to do this, kindly explain in the comments. Making a call to shouldOverrideUrlLoading in the WebViewClient doesn’t seem to work either, so you should do your processing in onPageFinished. Here is the code:


public class blog extends Dialog {
static final FrameLayout.LayoutParams FILL = new FrameLayout.LayoutParams(
                 ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT);
    private static final String RESULT_URL = "XXX.php"; // Url for retrieving results
    private String mUrl; //URL you are making a request to
    private DialogListener mListener;
    private ProgressDialog mSpinner;
    private Button mButton;
    private WebView mWebView;
    private LinearLayout mWebViewContainer;
    public blog(Context context, String url, DialogListener listener) {
        super(context, android.R.style.Theme_NoTitleBar);
        mUrl = url;
        mListener = listener;
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mSpinner = new ProgressDialog(getContext());
        mSpinner.requestWindowFeature(Window.FEATURE_NO_TITLE);
        mSpinner.setMessage("Loading...");
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        mButton = new Button(getContext());
        mButton.setText("Close");
        mWebViewContainer = new LinearLayout(getContext());
        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mListener.onStop();
                blog.this.dismiss();
            }
        });
        //Close button should only become visible after the webview is fully loaded
        mButton.setVisibility(View.INVISIBLE);
        mWebView = new WebView(getContext());
        mWebView.setVerticalScrollBarEnabled(false);
        mWebView.setHorizontalScrollBarEnabled(false);
        mWebView.setWebViewClient(new blog.blogWebViewClient());
        mWebView.getSettings().setJavaScriptEnabled(true);
        mWebView.loadUrl(mUrl);
        mWebView.setVisibility(View.INVISIBLE);
        mWebViewContainer.addView(mButton);
        mWebViewContainer.addView(mWebView);
        addContentView(
            mWebViewContainer,
            new LayoutParams(LayoutParams.FILL_PARENT,LayoutParams.FILL_PARENT)
        );
    }

    private class blogWebViewClient extends WebViewClient {
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            return false;
        }
        @Override
        public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
            super.onReceivedError(view, errorCode, description, failingUrl);
            mListener.onError(new DialogError(description, errorCode,failingUrl));
            blog.this.dismiss();
        }
        @Override
        public void onPageStarted(WebView view, String url, Bitmap favicon) {
             super.onPageStarted(view, url, favicon);
             mSpinner.show();
        }
        @Override
        public void onPageFinished(WebView view, String url) {
             super.onPageFinished(view, url);
             mSpinner.dismiss();
             mWebViewContainer.setBackgroundColor(Color.TRANSPARENT);
             mWebView.setVisibility(View.VISIBLE);
             mButton.setVisibility(View.VISIBLE);
             //Do result processing here
             if (url.contains(RESULT_URL)) {
                 JSONObject obj = null;
                 try { URL json = new URL(url); }
                 catch (IOException e) { e.printStackTrace(); }
                 mListener.onComplete(obj);
                 blog.this.dismiss();
             }
         }
    }
}

For more information on this, check this: