1. #1
    Sencha User harley.333's Avatar
    Join Date
    Mar 2007
    Posts
    286
    Vote Rating
    5
    harley.333 is on a distinguished road

      0  

    Default Exception Handling class

    Exception Handling class


    We've written the following error handling class, and it works pretty well.

    I'm submitting it here for anyone to play with. I'm hoping the you guys can help me catch more types of errors

    For example, I'm not catching image tags that throw errors.

    To use the class:

    Code:
    ErrorHandler.init();
    Code:
    // the following is a global, singleton class
    ErrorHandler = function() {
    	return {
    		init: function() {
    			window.onerror = !window.onerror ? ErrorHandler.handleError : window.onerror.createSequence(ErrorHandler.handleError);
    		},
    		getFormattedMessage: function(args) {
    			var lines = ["The following error has occured:"];
    			if (args[0] instanceof Error) { // Error object thrown in try...catch
    				var err = args[0];
    				lines[lines.length] = "Message: (" + err.name + ") " + err.message;
    				lines[lines.length] = "Error number: " + (err.number & 0xFFFF); //Apply binary arithmetic for IE number, firefox returns message string in element array element 0
    				lines[lines.length] = "Description: " + err.description;
    			} else if ((args.length == 3) && (typeof(args[2]) == "number")) { // Check the signature for a match with an unhandled exception
    				lines[lines.length] = "Message: " + args[0];
    				lines[lines.length] = "URL: " + args[1];
    				lines[lines.length] = "Line Number: " + args[2];
    			} else {
    				lines = ["An unknown error has occured."]; // purposely rebuild lines
    				lines[lines.length] = "The following information may be useful:"
    				for (var x = 0; x < args.length; x++) {
    					lines[lines.length] = Ext.encode(args[x]);
    				}
    			}
    			return lines.join("\n");
    		},
    		displayError: function(args) {
    			// purposely creating a new window for each exception (to handle concurrent exceptions)
    			var errWindow = new Ext.Window({
    				autoScroll: true,
    				bodyStyle: {padding: 5},
    				height: 150,
    				html: this.getFormattedMessage(args).replace(/\n/g, "<br />").replace(/\t/g, " &nbsp; &nbsp;"),
    				modal: true,
    				title: "An error has occurred",
    				width: 400
    			});
    			errWindow.show();
    		},
    		logToServer: function(args) {
    			Ext.Ajax.request({
    				params: {
    					a: "PostErrorInfo",
    					error: Ext.encode(args)
    				},
    				url: "Default.aspx"
    			});
    		},
    		handleError:  function() {
    			var args = [];
    			for (var x = 0; x < arguments.length; x++) {
    				args[x] = arguments[x];
    			}
    			try {
    				this.displayError(args);
    				this.logToServer(args);
    			} catch(e) {
    				// if the errorHandler is broken, let the user see the browser's error handler
    				return false;
    			}
    			return true;
    		}
    	};
    }();
    // the following line ensures that the handleError method always executes in the scope of ErrorHandler
    ErrorHandler.handleError = ErrorHandler.handleError.createDelegate(ErrorHandler);

  2. #2
    Sencha User harley.333's Avatar
    Join Date
    Mar 2007
    Posts
    286
    Vote Rating
    5
    harley.333 is on a distinguished road

      0  

    Default


    Here are the test-cases I've been using. You'll notice that proper try...catch style code has better results.

    Code:
    // ErrorHandler-Test1 = success
    var test = null;
    test.arg = 5;
    // ErrorHandler-Test2 = success
    throw (new Error("Hello"));
    // ErrorHandler-Test3 = success (however, thrown information was not retained)
    throw "Hello again";
    // ErrorHandler-Test4 = success (however, thrown information was not retained)
    throw {
    	myMessage: "stuff",
    	customProperty: 5,
    	anArray: [1, 2, 3]
    };
    // ErrorHandler-Test5 = success
    try {
    	var test2 = null;
    	test2.arg = 5;
    } catch(e) {
    	ErrorHandler.handleError(e);
    }
    // ErrorHandler-Test6 = success
    try {
    	throw (new Error("Goodbye"));
    } catch(e) {
    	ErrorHandler.handleError(e);
    }
    // ErrorHandler-Test7 = success
    try {
    	throw "Goodbye again";
    } catch(e) {
    	ErrorHandler.handleError(e);
    }
    // ErrorHandler-Test8 = success
    try {
    	throw {
    		myMessage: "stuff",
    		customProperty: 5,
    		anArray: [1, 2, 3]
    	};
    } catch(e) {
    	ErrorHandler.handleError(e);
    }
    Like I said earilier, the following will show the alert, but it won't bubble up to my ErrorHandler:
    HTML Code:
    <img src="fake.gif" onerror="alert(0);" />

  3. #3
    Sencha User loeppky's Avatar
    Join Date
    May 2007
    Location
    Seattle, WA
    Posts
    230
    Vote Rating
    0
    loeppky is on a distinguished road

      0  

    Default


    harley.333: very nice work. I like your design, and good stuff with the test cases.

  4. #4
    Sencha User nak1's Avatar
    Join Date
    Jan 2008
    Posts
    266
    Vote Rating
    0
    nak1 is an unknown quantity at this point

      0  

    Default Question

    Question


    I was wondering if there was any way to extract more information from the error that occurred?
    No longer a Newbie

  5. #5
    Sencha User harley.333's Avatar
    Join Date
    Mar 2007
    Posts
    286
    Vote Rating
    5
    harley.333 is on a distinguished road

      0  

    Default


    @nake1: Sorry I missed your remark (I've been in server-side land for the past few months).

    I've improved upon and formalized this error handling class.
    http://extjs-ux.org/docs/index.html?...x.ErrorHandler
    I'm disappointed that Ext 3.0 doesn't have a class like this. Exception Handling should be a top priority for all developers.

    If you look at the docs for the "error" event, you'll see all the information I am capable of gathering from the error that occurred. Let me know if there's something else that you'd like to see.
    harley.333 - Harley Jones
    harley.333@gmail.com - Find me on Google Talk.

  6. #6
    Ext User
    Join Date
    Jul 2007
    Location
    Florida
    Posts
    9,996
    Vote Rating
    8
    mjlecomte will become famous soon enough mjlecomte will become famous soon enough

      0  

    Default


    Did you notice the Ext.Error class in Ext 3? I doubt as robust as your version.

    If you'd like to improve upon that class I might suggest creating a new thread with that title, perhaps suggestions would be folded into it?

  7. #7
    Sencha - Community Support Team mschwartz's Avatar
    Join Date
    Nov 2008
    Location
    San Diego, Peoples' Republic of California
    Posts
    2,053
    Vote Rating
    18
    mschwartz will become famous soon enough mschwartz will become famous soon enough

      0  

    Default


    What Ext.Error class in 3.0?

    I grep through the sources and 'Ext.Error' is not found.

  8. #8
    Ext User
    Join Date
    Jul 2007
    Location
    Florida
    Posts
    9,996
    Vote Rating
    8
    mjlecomte will become famous soon enough mjlecomte will become famous soon enough

      0  

    Default


    Looks like it's still svn only.

  9. #9
    Sencha User harley.333's Avatar
    Join Date
    Mar 2007
    Posts
    286
    Vote Rating
    5
    harley.333 is on a distinguished road

      0  

    Default


    @mjlecomte: No, I haven't seen the Ext.Error class (I don't have SVN access). But Jack and the boys are welcome to steal it and make it their own.
    harley.333 - Harley Jones
    harley.333@gmail.com - Find me on Google Talk.

  10. #10
    Ext JS Premium Member christocracy's Avatar
    Join Date
    Oct 2006
    Location
    Montreal
    Posts
    381
    Vote Rating
    0
    christocracy is on a distinguished road

      0  

    Default


    Hey, Harley. I put a class called Ext.Error into SVN. It's pretty simplistic currently and doesn't really have a home yet (it currently resides in data/Api.js in SVN).

    I extend it in certain important widget class like Store and DataProxy.

    Eg:
    Ext.data.Store.Error = Ext.extend(Ext.Error, {...});
    Ext.data.DataProxy.Error = Ext.extend(Ext.Error, {...});

    Code:
    /**
     * @class Ext.Error
     * @extends Object
     * <p>The Ext.Error class wraps the native Javascript Error class similar to how Ext.Element wraps a DomNode.
     * To display an error to the client call the {@link #toConsole} method which will check for the
     * existence of Firebug.</p>
     *
     * TODO: Move to Ext.js?
     *
    <code><pre>
    try {
        generateError({
            foo: 'bar'
        });
    }
    catch (e) {
        e.toConsole();
    }
    function generateError(data) {
        throw new Ext.Error('foo-error', 'Foo.js', data);
    }
    
    </pre></code>
     * @param {String} id A simple label for the error for lookup.
     * @param {String} file The file where the error occurred.
     * @param {Mixed} data context-data.
     */
    Ext.Error = function(id, file, data) {
        this.message = this.render.apply(this, arguments);
        this.error = new Error(this.message, file);
        this.error.name = this.cls;
        this.id = id;
    }
    Ext.Error.prototype = {
        /**
         * The ClassName of this Error.
         * @property cls
         * @type String
         */
        cls: 'Ext.Error',
        /**
         * The id of the error.
         * @property id
         * @type String
         */
        id : undefined,
    
        /**
         * Abstract method to render error message.  All Error extensions should override this method.
         */
        render : function(id, file, data) {
           return this.cls + ' ' + id;
        },
    
        /**
         * Attempts to output the exception info on FireBug console if exists.
         */
        toConsole : function() {
            if (typeof(console) == 'object' && typeof(console.error) == 'function') {
                console.error(this.error);
            }
            else {
                alert("Error: " + this.cls + ' ' + this.message);   // <-- ugh.  fix this before official release.
            }
        },
    
        /**
         * toString
         */
        toString : function() {
            return this.error.toString();
        }
    };
    
    /**
     * Error class for Ext.data.Api errors.
     */
    Ext.data.Api.Error = Ext.extend(Ext.Error, {
        cls: 'Ext.data.Api',
        render : function(name, file, data) {
            switch (name) {
                case 'action-url-undefined':
                    return 'No fallback url defined for action "' + data + '".  When defining a DataProxy api, please be sure to define an url for each CRUD action in Ext.data.Api.actions or define a default url in addition to your api-configuration.';
                case 'invalid':
                    // make sure data is an array so we can call join on it.
                    data = (!Ext.isArray(data)) ? [data] : data;
                    return 'received an invalid API-configuration "' + data.join(', ') + '".  Please ensure your proxy API-configuration contains only the actions defined in Ext.data.Api.actions';
                case 'invalid-url':
                    return 'Invalid url "' + data + '".  Please review your proxy configuration.';
                case 'execute':
                    return 'Attempted to execute an unknown action "' + data + '".  Valid API actions are defined in Ext.data.Api.actions"';
                default:
                    return 'Unknown Error "' + name + '"';
            }
        }
    });
    /**
    * @author Chris Scott
    * @business www.transistorsoft.com
    * @rate $150USD / hr; training $500USD / day / developer (5 dev min)
    *
    * @SenchaDevs http://senchadevs.com/developers/transistor-software
    * @twitter http://twitter.com/#!/christocracy
    * @github https://github.com/christocracy
    */