Chapter 12. Errors: To Be (in Style) or Not to Be

It amazes me how little coverage the topic of errors receives in programming and web development books today. I do not mean that authors gloss over the issue of errors—these books always address the mechanics of trying and catching errors. Rather, you do not see examples that illustrate what a developer should do with an error once it has been trapped. Coverage of error handling should comprise more than just how to alert the user that an error occurred. It should also comprise understanding what errors developers should allow the user to see and what errors they need to handle behind the scenes.

The basic question is where the error originated and why it occurred. Any error due to user-provided input should be sent back to the user. It’s harder to decide what to do with errors that were caused by the application.

Error Handling on the Web

When you think about JavaScript error handling on the Web, what immediately comes to mind? Many people think of a JavaScript alert box, or maybe a try-catch block within some JavaScript code. However, there is much more to error handling in a web application than just the errors that can occur within the JavaScript code. Web application developers must think about errors on the client side as well as on the server side.

Web application developers, especially those who utilize Ajax, cannot simply focus on what the client does, because the domain of the web developer stretches across client and server. Server errors that occur could be the user’s fault, and the server may need to let the user know about these errors. So, for Ajax developers, error handling needs to encompass everything.

JavaScript Errors

Most developers are used to seeing JavaScript errors that occur on the client. Whether these errors are user-defined errors that are displayed in an alert box or browser errors that are thrown in a warning window, a developer who works with JavaScript knows how these errors work.

There are seven core error types within JavaScript 1.5:

  • BaseError

  • EvalError

  • RangeError

  • ReferenceError

  • SyntaxError

  • TypeError

  • URIError

The BaseError is the error object upon which all the other error types are built. It could also be considered the generic error type. The other error types are fairly self-explanatory. An EvalError is raised whenever an error occurs while executing code in an eval( ) statement. A RangeError is raised whenever a numeric variable or parameter is outside its valid range. A ReferenceError is raised when dereferencing an invalid reference. A SyntaxError is raised whenever there is a problem with syntax while parsing code in an eval( ) statement. A TypeError is raised whenever a variable or parameter is not of a valid type. And finally, a URIError is raised whenever the function encodeURI( ) or decodeURI( ) is passed invalid parameters.

The different error types provide different properties. The generic error, or the BaseError, for example, has the name and message properties associated with it. Based on the browser, the error can come with different properties. Mozilla and Internet Explorer have their own properties available, as shown in Table 12-1.

Table 12-1. JavaScript error properties

Property

Description

Browser

constructor

This property specifies the function that creates an object’s prototype.

Standards-compliant

description

This property is the error description or message.

Internet Explorer only

fileName

This property specifies the path to the file that raised the error.

Mozilla only

lineNumber

This property specifies the line number in the file that raised the error.

Mozilla only

message

This property specifies the error message.

Standards-compliant

name

This property specifies the error name.

Standards-compliant

number

This property specifies the error number.

Internet Explorer only

prototype

This property allows the addition of properties to an error object.

Standards-compliant

stack

This property lists the stack trace.

Mozilla only

Server-Side Errors

Server-side errors occur on the server, or server side, of an Ajax application. These errors apply to anything that is outside the client scope. For example, an error that is thrown in the database of the application is a server-side error, just as one thrown by the web server is a server-side error. Because the term server-side error could encompass many separate entities, it is harder for developers to determine what to do with a thrown error. Server-side errors really fall into three categories: server scripting errors, database errors, and external errors.

Server scripting errors

Server scripting errors are errors that are thrown by the scripting language being used on the server—PHP, ASP.NET, Java, and so on. These languages usually have built-in error types just as JavaScript does. Table 12-2 lists the predefined errors that are available in PHP. Other languages will have their own predefined constants that might vary from those in this table.

Table 12-2. Error and logging constants in PHP

Constant

Value

Description

E_ERROR

1

A fatal runtime error. You cannot recover from this type of error (an example is a memory allocation error). Execution of the script is halted.

E_WARNING

2

A runtime warning (nonfatal error). Script execution is not halted.

E_PARSE

4

A compile-time parse error.

E_NOTICE

8

A runtime notice. This indicates that the script encountered something that could indicate an error, but also happens in the normal course of running a script.

E_CORE_ERROR

16

A fatal error that occurs during PHP’s initial startup. This is like an E_ERROR, except it is generated by the core of PHP.

E_CORE_WARNING

32

A warning (nonfatal error) that occurs during PHP’s initial startup. This is like an E_WARNING, except it is generated by the core of PHP.

E_COMPILE_ERROR

64

A fatal compile-time error. This is like an E_ERROR, except it is generated by the Zend Scripting Engine.

E_COMPILE_WARNING

128

A compile-time warning (nonfatal error). This is like an E_WARNING, except it is generated by the Zend Scripting Engine.

E_USER_ERROR

256

A user-generated error message. This is like an E_ERROR, except it is generated in PHP code by using the PHP function trigger_error( ).

E_USER_WARNING

512

A user-generated warning message. This is like an E_WARNING, except it is generated in PHP code using the PHP function trigger_error( ).

E_USER_NOTICE

1024

A user-generated notice message. This is like an E_NOTICE, except it is generated in PHP code by using the PHP function trigger_error( ).

E_STRICT

2048

A runtime notice. This is enabled to have PHP suggest changes to your code that will ensure best interoperability and forward compatibility.

E_RECOVERABLE_ERROR

4096

A catchable fatal error. This indicates that a (probably) dangerous error occurred, but did not leave the engine in an unstable state. If a user-defined handler does not catch the error, the application aborts, as if it were an E_ERROR.

E_ALL

8191

Denotes all errors and warnings, as supported, except those of level E_STRICT in PHP version 6 and earlier.

PHP lets you define how to handle errors at runtime via the set_error_handler( ) function. When you use this function, the standard PHP error handler is completely bypassed in lieu of the developer’s function. This means the developer will have complete control over all errors in the server scripting portion of the application. Example 12-1 is a simple example of such a function.

Example 12-1. A custom error handler for PHP

<?php
/**
 * Example 12-1, A custom error handler for PHP
 *
 * This example shows how to create a custom error handler to receive any
 * application script error that may appear within the PHP script. All PHP error
 * handlers need to receive four parameters:
 *     the error number, the error message, the file the error occurred in, and the
 *     line the error occurred on.
 */

/* Set the error reporting levels for this script */
error_reporting(E_ERROR | E_WARNING | E_NOTICE);

/**
 * This function, customErrorHandler, is a custom error handler replacement for the
 * default error handler used by PHP.
 *
 * @param integer $p_error_num the error number
 * @param string $p_error_str the error message string
 * @param string $p_error_file the file the error occurred on
 * @param integer $p_err_line the line the error occurred on
 */
function customErrorHandler($p_error_num, $p_error_str, $p_error_file, $p_err_line) {
    switch ($p_error_num) {
        case E_ERROR:
            print("<b>Custom ERROR</b> [$p_error_num] $p_error_str<br />
");
            print(" Fatal error on line $p_err_line in file $p_error_file");
            print(', PHP '.PHP_VERSION.' ('.PHP_OS.')<br />
'),
            exit(1);
            break;
        case E_WARNING:
            print("<b>Custom WARNING</b> [$p_error_num] $p_error_str<br />
");
            break;
        case E_NOTICE:
            print("<b>Custom NOTICE</b> [$p_error_num] $p_error_str<br />
");
            break;
        default:
            print("Unknown error type: [$p_error_num] $p_error_str<br />
");
            break;
    }
}

/* Set the error handling to the user-defined custom error handler */
$php_error_handler = set_error_handler('customErrorHandler'),
?>

Database errors

Databases throw their own set of errors, which is rather large and can change frequently. This makes it impossible for me to list all of these errors with any accuracy. The database does not handle these errors anyway, instead putting the responsibility on whatever client is using the database. The following shows how the error is displayed in the MySQL client program:

ajax> SELECT * FROM no_such_table;
ERROR 1146 (42S02):  Table 'ajax.no_such_table' doesn't exist

In an Ajax application, it is the server script’s responsibility to handle any error thrown by the database. Most server scripting languages will not only recognize an error when it is thrown by the SQL Server, but also will usually have mechanisms to retrieve the error for logging and display purposes. PHP, for example, has two functions: one displays the error number and one displays the error message. In Example 12-2, we can see both of these being used.

Example 12-2. Server script handling a database error

<?php
/**
 * Example 12-2, Server script handling a database error
 *
 * This example shows how to trap a database error in PHP and handle the information
 * that is given to the server script (PHP) from the database.
 */

/**
 * This function, execute_query, takes the passed /$p_sql/ parameter and queries the
 * database.  It then checks for errors before sending results.
 *
 * @param string $p_sql the SQL to execute on the server
 * @return mySQL result | boolean
 */
function execute_query($p_sql) {
    $result = mysql_query($p_sql);

    /* Is there an error with the query? */
    if (mysql_errno( )) {
        print('MySQL error ['.mysql_errno( ).'] '.mysql_error( )
            ."<br />
Attempted to execute query:<br />
$p_sql
<br />";
        $result = false;
    }
    return ($result);
}
?>

External errors

The other type of error that can crop up on the server side is one that is out of the web application’s control. This error comes from external applications from which the Ajax application expects to receive data, but from which any error must be handled by the application without an indication as to what caused the error.

The most typical type of application from which an Ajax application will require external data is an RSS feed, a news feed, a mapping feed, or something similar. Because the Ajax application cannot control what these services return, it is imperative that the server scripting trap any data it is not expecting and give a more useful error to the client.

A final external error is that caused by the Internet. What if an Ajax request is sent out, but the server is unreachable or the page is temporarily not found? This type of error can also cause problems with the application when the Ajax request must occur. In these cases, it is up to the client to handle the issue and act accordingly, even if it means stopping the application.

Should I React to That Error?

The big question is which errors you should react to. This is difficult to answer, as every application developer has her own idea of which errors are important. Obviously, you must react to errors that will halt application execution. What remains are the warnings and notices that could still break the application. Because I cannot answer the question of which errors you should react to, I will instead concentrate on how to react to any errors.

Trapping an Error

Trapping an error is something that every programming book covers in one way or another. This book will discuss trapping errors on both the client and server sides. But what do you do once you trap the error? Some errors you can ignore, meaning they do not need to stop program execution. Other errors need to stop the application because something has gone horribly wrong.

try...catch...finally

Like most other programming languages, JavaScript can use the try...catch...finally block of code. Under normal circumstances, when the JavaScript parser encounters an error, the script stops and no further code execution occurs on the page. The try...catch...finally block is useful for trapping these errors so that execution may continue on the rest of the page. A standard try...catch...finally block of code looks like this:

try {
    // statements within the try block
}
[catch (exception if condition) {
    // statements within the catch block
}]
[catch (exception) {
    // statements within the catch block
}]
[finally {
    // statements within the finally block
}]

The try block goes around any lines of code that you believe could create an error. When an error occurs, execution jumps to the catch block of code. The finally block of code is called regardless of whether there is an error. For example:

try {
    my_function_that_may_fail( );
} catch (ex) {
    document.write('Caught an exception in the my_function_that_may_fail( ) ' +
        'function.'),
} finally {
    document.write('I am always outputted.'),
}

I already mentioned that there are different types of JavaScript errors depending on which piece of code fails. To test for these errors, you would use a conditional catch clause:

try {
    /* This function may throw any type of error */
    my_function_that_may_fail( );
} catch (e if e instanceof EvalError) {
    alert('Caught an EvalError with the my_function_that_may_fail( ) function.'),
} catch (e if e instanceof RangeError) {
    alert('Caught a RangeError with the my_function_that_may_fail( ) function.'),
} catch (e if e instanceof ReferenceError) {
    alert('Caught a ReferenceError with the my_function_that_may_fail( ) function.'),
} catch (e if e instanceof SyntaxError) {
    alert('Caught a SyntaxError with the my_function_that_may_fail( ) function.'),
} catch (e if e instanceof TypeError) {
    alert('Caught a TypeError with the my_function_that_may_fail( ) function.'),
} catch (e if e instanceof URIError) {
    alert('Caught a URIError with the my_function_that_may_fail( ) function.'),
} catch (e) {
    alert('Caught an unknown exception in the the my_function_that_may_fail( ) ' +
        'function.'),
}

Warning

Conditional catch clause functionality is not part of the ECMAScript specification; therefore, you should use it with care. Not all browsers have implemented this functionality in their error handling.

Throwing an error

You use the throw statement to throw an exception to the nearest try...catch block of code. Like other languages, the throw statement can specify the value of the exception to be thrown. The syntax is the same in most languages, and it looks like this:

throw expression;

The throw statement is most useful when the object being thrown is a user-defined object. In this way, the object’s properties can be referenced within the catch block. For example:

/**
 * This object, UserException, is an example of a simple user object that can be
 * used when throwing a new exception.
 *
 * @param {Integer} p_number The number of the exception.
 * @param {String} p_message The message for the exception.
 */
function UserException(p_number, p_message) {
    this.name = 'UserException';
    this.number = p_number;
    this.message = p_message;
}

/**
 * This variable, months, is an array containing the twelve month abbreviations.
 * @global
 */
var months = ['Jan', 'Feb', 'Mar',
              'Apr', 'May', 'Jun',
              'Jul', 'Aug', 'Sep',
              'Oct', 'Nov', 'Dec'];
/**
 * This function, getMonth, returns the month abbreviation for the passed
 * /p_monthNumber/ variable.
 *
 * @param {Integer} p_monthNumber The number of the month to get.
 * @return Returns the month abbreviation.
 * @type String
 */
function getMonth(p_monthNumber) {
    p_monthNumber--;
    /* Is this a valid month? */
    if (months[p_monthNumber])
        return months[p_monthNumber];
    else
        throw (new UserException(782, 'Invalid Month Number'));
}

var monthName = '';

try {
    monthName = getMonth(aNewMonth);
} catch (e) {
    monthName = 'Unknown';
    alert(e.name + '[' + e.number + ']: 
' + e.message);
}

Ajax gone wrong

Catching problems with any Ajax request is important for Ajax application stability. Problems can range from an unexpected value returned from the server to the server not answering the request. Whatever the case, you need to trap these errors so that the client can decide what to do with them.

Using a framework such as Prototype for our Ajax requests gives us some built-in abilities to trap problems and deal with them before they cause application instability. Refer back to Table 4-3 in Chapter 4, and the callback options available to the Ajax.Request( ) method. We are particularly interested in the following callbacks:

  • onException

  • onFailure

  • on404 (onXXX)

These callbacks allow a developer to trap these errors and decide which ones need more serious attention. The following shows an Ajax.Request( ) using these callbacks:

Ajax.Request('myAjaxPage.php' {
    method: 'POST',
    parameters: myParameters,
    onSuccess: function(xhrResponse) {
        alert('Completed transaction.'),
    },
    onFailure: function(xhrResponse) {
        alert('The transaction failed to complete.'),
    },
    onException: function(xhrResponse) {
        alert('An exception occurred within the client of the application.'),
    },
    on404: function(xhrResponse) {
        alert('The application is experiencing technical difficulties.'),
    },
    on503: function(xhrResponse) {
        alert('The application is experiencing heavy traffic and has timed out.'),
    }
});

Ignorable Errors

As a developer, it is always tough to decide when it is safe to ignore an error and when to take more action. Ignorable errors will cause no harm to the application if left alone, but you should address them in some way (silently) so that the system can be alerted. Examples of ignorable errors are data that was returned as missing from the server and failure by the client to update a log or some other less significant information to the server. Whatever the case, you know these errors need to notify the server, and possibly even the user, but they will not harm the application if left alone. It is up to you to decide what to do with them.

Hold It Right There!

On the other hand, some errors require immediate attention and cessation of the application when they occur. These errors occur when a critical application function fails to complete its task.

Say, for instance, that the client makes an Ajax call to the server, which in turn sends a query to the database, which fails to return any data because a table does not exist. The client expects to get this data and cannot continue to function without it. In these cases, the client must notify the user and stop the application, while allowing no further user action.

Handling an Error with Care

By now, you should have a good idea which errors you would ignore and which you would handle immediately. Now you must determine who should find out about such errors. Many errors the user should not know about, simply so that he does not panic unnecessarily. Whatever the case, it is important to enable users to report an error in a professional and helpful manner.

By simply ensuring that errors are trapped and presented correctly, you can keep your application more reliable in the long run. If users understand what has occurred and can report clear and concise descriptions of errors they encounter, it is much easier for developers to troubleshoot them. In the same manner, errors reported to developers through email or logging should explain exactly what took place and where, without forcing the developer to hunt for the necessary information.

Notifying the User

Any errors that are relayed to the user should be clear and should provide information for contacting the development team for reporting purposes. Errors such as those in Figure 12-1 are not necessarily helpful, as they do not convey much information to the user that could help solve the problem.

A typical “blue screen of death” in Windows

Figure 12-1. A typical “blue screen of death” in Windows

Figure 12-2 shows an error that is more helpful, in that it provides a bit more information, but it can also be a little overwhelming.

The Windows XP “blue screen of death”

Figure 12-2. The Windows XP “blue screen of death”

Then there are errors such as that shown in Figure 12-3; not only does this error tell the end user absolutely nothing useful, but it is also the type of error report that overwhelms the typical end user (or, for that matter, even the typical power users of this operating system).

The Windows NT 3.1 “blue screen of death”

Figure 12-3. The Windows NT 3.1 “blue screen of death”

An error sent to the end user should state the type of error that occurred: Syntax, Eval, Type, and so on. It should also include an error number and message. Of course, any filename and line number would also be helpful. Most important, it should tell the user whom to contact with this error information, and if possible, it should automate that notification task.

Errors that occur on the client side are easier to handle than errors that occur on the server side, simply because the client should automatically have everything it needs to report the error. Server errors must be communicated down to the client in some fashion, and then parsed before they can be sent to the same mechanism that the client-side errors use. The other issue with server-side errors is that you have to hope that the error makes it back to the client. In some circumstances, the server error might not be communicated to the client, and then the client must either report a vague error or guess what the error was.

Emailing the Developer

Any error that is sent via email to the developer should be as clear as possible for her to understand. This greatly decreases the amount of time she will spend debugging and fixing the error. She should expect to get the same information that is displayed to the user, but usually in a more verbose manner. In addition, any available stack dump can go a long way toward communicating to the developer what may have occurred.

Emailing the developer carries a certain amount of risk of failure if the error occurs on the client side, simply because there is no guarantee that the client will be able to successfully communicate with the server to get the error delivered. Because of this, when a developer writes code to send an error message back to the server for email, a contingency must be in place to ignore any additional failure on the client’s part should communication with the server fail.

Logging to a Database

Logging an error to the database carries with it the same issues that can arise when emailing the developer about an error. When logging an error to the database, however, usually less information is placed in the database for every given error. This is not to say that logging an error to the database is not as important as email in terms of error communication. Rather, logging an error is most useful for tracking trends of errors that occur in an application.

Integrating the User Error

Something that is not so important for logging to a database or emailing the developer, but is important for displaying an error to the user, is how the display looks. Errors that are given to the user through a standard alert box tell the user that there is an error, but they fail to have the seamless appeal they could have if they were presented on a custom page or pop up. A developer who takes the time to integrate errors into an application gives the user the feeling that the application is made by professionals.

That is the whole point of this chapter: to give the user a sense of comfort and reliability. Users do not have this when an application fails and the error screen that comes up is over their heads, or is simply useless to them (the Windows blue screen of death again). A user would at least like to know what happened, and wants to feel that the error will be taken care of by capable individuals. Errors that incorporate the look of the application as a whole go a long way toward achieving this.

Following Site Design

It is never helpful for a user to get an error on a web site such as that shown in Figure 12-4. Granted, the 404 error may be a bad example to use here, as most Internet users understand what this error means. But what about an HTTP 413 error? Would the typical user understand what the server is saying with the words “Request Entity Too Large”?

The 404 error most web surfers are used to seeing

Figure 12-4. The 404 error most web surfers are used to seeing

Most corporate entities attempt to tie the error into the site so that the error seems to be a part of the site or application. Figure 12-5 shows what this can look like.

The 404 error shown on O’Reilly’s web site

Figure 12-5. The 404 error shown on O’Reilly’s web site

End users may also ignore most of the text on a web application that throws an error, especially when the error comprises only a bit of text in an alert box or something similar. The remedy is to have a customized error handler in the application.

This error handler must be able to receive an error number, description, and optional object containing extras such as a stack dump, file occurrence, line occurrence, and so on. The first thing it needs, however, is an error level. This level will let the client know what it should attempt to do with the error. To that end, the first thing we must do is define our error levels. In Table 12-3, I am defining a number and a constant name, and describing what should be done for such errors.

Table 12-3. Custom error levels

Value

Constant name

Description

1

ERROR_NOTICE

This error is a noncritical error and the application should not halt because of it. The results of the error should only be sent to the user.

2

ERROR_WARNING

This error could cause some instability in the application by continuing without a restart. The results of the error should be sent to the user and logged in the database.

3

ERROR_CRITICAL

This error occurs when something happened that should not have happened. Notice should be sent to the user that the application needs a restart. The results of the error should be sent to the user and the database, and should be emailed to the developer.

4

ERROR_SILENT_WARN

This error could cause some instability in the application by continuing without a restart, but the user should not know about the error. Results of the error should be sent to the database.

5

ERROR_SILENT_CRIT

This error occurs when something went horribly wrong on the client and it must restart suddenly. If possible, this error should be sent to the database and emailed to the developer.

6

ERROR_SERVER_WARN

This error occurs when something happened on the server that the client does not need to know about, as it should not adversely affect performance. The results of this error should be sent to the database.

7

ERROR_SERVER_CRIT

This error occurs when something has gone horribly wrong on the server. It must send a restart command to the client but should not inform the user. The results of this error should be sent to the database and emailed to the developer.

Once we have our custom error levels defined, we can concentrate on an Error object that can handle all the functionality we desire. Example 12-3 shows just such an object.

Example 12-3. customError.js: A custom Error object for the client

/**
 * @fileoverview, Example 12-3.  customError.js: A custom Error object for the
 * client.
 *
 * This file, customError.js, contains custom error codes, and the /myError/ object
 * to use to throw errors in the Ajax application.
 */

var ERROR_UNDEFINED =   0;
var ERROR_NOTICE =      1;
var ERROR_WARNING =     2;
var ERROR_CRITICAL =    3;
var ERROR_SILENT_WARN = 4;
var ERROR_SILENT_CRIT = 5;
var ERROR_SERVER_WARN = 6;
var ERROR_SERVER_CRIT = 7;

/**
 * This object, myError, is used to send errors where they are required.  It has
 * the following public methods:
 *     - throw(p_level, p_number, p_message, p_param)
 *     - restart(p_method)
 *
 * @type object
 * @constructor
 */
var myError = {
    /**
     * This member, level, holds the custom level of the error.
     * @type Integer
     */
    level: ERROR_UNDEFINED,
    /**
     * This member, number, holds the number of the error.
     * @type Integer
     */
    number: -1,
    /**
     * This member, message, holds the message of the error.
     * @type String
     */
    message: '',
    /**
     * This member, parameters, holds the optional parameters object for the error.
     * @type Object | null
     */
    parameters: null,
    /**
     * This method, sendToTuser, formats the error and sends it to the user using
     * the /fillPopUp( )/ method for display.
     *
     * @member myError
     * @see #parseError
     */
    sendToUser: function( ) {
        /*
         * This variable, format, will hold the formatted error to display to the
         * user
         */
        var format = '';

        // Decide how the error should be formatted, and do so here...

        /* This is from Example 10-2 in Chapter 10 */
        fillPopUp('Error', format);
    },
    /**
     * This method, sendToServer, formats the error and, depending on the
     * /p_method/ passed in, will send the error to the database for logging,
     * email the developers, or both.
     *
     * @member myError
     * @param {Integer} p_method The method to send to the server.
     * @see #parseError
     * @see Ajax#Request
     */
    sendToServer: function(p_method) {
        /*
         * This variable, param, will hold the formatted error to send to the
         * server
         */
        var param = '';

        param += '<error>';
        param += '<number>' + this.number + '</number>';
        param += '<message>' + this.message + '</message>';
        if (this.parameters.file)
            param += '<file>' + this.parameters.file + '</file>';
        if (this.parameters.line)
            param += '<line>' + this.parameters.line + '</line>';
        if (this.parameters.trace)
            param += '<trace>' + this.parameters.trace + '</trace>';
        param += '</error>';

        /* What method should be used? */
        switch (p_method) {
            case 1:
                Ajax.Request('logError.php', {
                    method: 'post',
                    parameters: param
                });
                break;
            case 2:
                Ajax.Request('emailError.php', {
                    method: 'post',
                    parameters: param
                });
                break;
            case 3:
                Ajax.Request('logError.php', {
                    method: 'post',
                    parameters: param
                });
                Ajax.Request('emailError.php', {
                    method: 'post',
                    parameters: param
                });
                break;
        }
    },
    /**
     * This method, throw, takes the error parameters passed in by the user, sets
     * them to the object's members, and calls the error parser.
     *
     * @member myError
     * @param {Integer} p_level The custom level of the error.
     * @param {Integer} p_number The number of the error, usually given by the
     *     system.
     * @param {String} p_message The message of the error, usually given by the
     *     system.
     * @param {Object} p_param Optional object containing additional parameters to
     *     send.
     * @see #parseError
     */
    throw: function(p_level, p_number, p_message, p_param) {
        this.level = p_level;
        this.number = p_number & 0xFFFF;
        this.message = p_message;
        this.parameters = p_param;

        this.parseError( );
    },
    /**
     * This member, parseError, looks at the custom level of the error and
     * determines where the error should be sent.
     *
     * @member myError
     * @see #throw
     */
    parseError: function( ) {
        /* What is the level of the error? */
        switch (this.level) {
            case ERROR_NOTICE:
                this.sendToUser( );
                break;
            case ERROR_WARNING:
            case ERROR_SILENT_WARN:
                this.sendToServer(1);
                if (this.level != ERROR_SILENT_WARN)
                    this.sendToUser( );
                break;
            case ERROR_CRITICAL:
            case ERROR_SILENT_CRIT:
                this.sendToServer(3);
                if (this.level != ERROR_SILENT_WARN)
                    this.sendToUser( );
                this.restart(1);
            break;
        }
    },
    /**
     * This member, restart, restarts the application either when the custom pop-up
     * window has closed or regardless of what is happening in the application.
     *
     * @member myError
     * @param {Integer} p_method Lets the object know if the error should be sudden.
     */
    restart: function(p_method) {
        /* Do we care if anything is going on and is the pop up visible? */
        if (p_method && Element.visible('popupContainer'))
            /* check again in a quarter of a second */
            setTimeout('myError.restart(1)', 250);
        /* Can we just restart? */
        else if (!p_method || !Element.visible('popupContainer'))
            window.location.href = window.location.href;
    }
}

This object uses the custom pop-up windows from Example 10-2 in Chapter 10 to display information to the user. It also allows the developer to “kill” the application with the die( ) method, which sends the user to Google, or restart the application with the restart( ) method by reloading the page. Utilizing this object is as simple as the following:

try {
    // ... some code that might cause an error here
} catch (ex) {
    myError.throw(ERROR_WARNING, ex.number, ex.description, {
        file: window.location.href
    });
}

We also need the server scripts to handle the errors. Example 12-4 shows what the logError.php file would look like. It must take an XML posting and put that data into an error table in the database.

Example 12-4. logError.php: The script to handle the error to be logged from the client

<?php
/**
 * Example 12-4, logError.php: The script to handle the error to be logged from
 * the client.
 */

/**
 * The Zend Framework Db.php library is required for this example.
 */
require_once('Zend/Db.php'),
/**
 * The generic db.php library, containing database connection information such as
 * username, password, server, etc., is required for this example.
 */
require_once('db.inc'),

/* Get the passed XML */
$raw_xml = file_get_contents("php://input");
$data = simplexml_load_string($raw_xml);

/* Set up the parameters to connect to the database */
$params = array ('host' => $host,
                 'username' => $username,
                 'password' => $password,
                 'dbname' => $db);

try {
    /* Connect to the database */
    $conn = Zend_Db::factory('PDO_MYSQL', $params);
    /* The row of data to be inserted in column => value format */
    $row = array (
        'error_dte'  => date('Y-m-d'),
        'number' => $conn->quote($data->number),
        'message' => $conn->quote($data->message),
        'file' => $conn->quote((($data->file) ? $data->file : '')),
        'line' => $conn->quote((($data->line) ? $data->line : '')),
        'trace' => $conn->quote((($data->trace) ? $data->trace : ''))
    );
    /* the table into which the row should be inserted */
    $table = 'application_errors';
    /* Insert the row */
    $conn->insert($table, $row);
} catch (Exception $e) { }
?>

This takes us much farther along the path to not only getting users’ attention when an error occurs in an application, but also getting them more actively involved in reporting such errors. Microsoft and others use such error handling techniques with their applications so that when an error occurs, the user has a chance to explain what happened to cause the error. Figure 12-6 shows one of these error messages.

The quality feedback agent found in Firefox browsers

Figure 12-6. The quality feedback agent found in Firefox browsers

Implementing the same sort of error handling will enable your application to get the best error feedback. This can only help to advance your application to a more stable level.

User Instructions for the Error

To enable users to provide feedback when an error occurs, we must add to our custom Error object. The easiest way is to simply add another parameter that can be passed in the optional object to the Error object. This new parameter will need to tell the Error object what message should be displayed to the user to garner feedback. It is better to have a few built-in messages to display than to have every error throwing a new message at the user. Remember that consistency goes a long way toward keeping the user happier with the application. Example 12-5 shows our new Error object with this code.

Example 12-5. customError.js: A modified custom Error object with user input

/**
 * @fileoverview, Example 12-5.  customError.js: A modified custom Error object
 * with user input.
 *
 * This file, customError.js, contains custom error codes, and the /myError/ object
 * to use to throw errors in the Ajax application.
 */

var ERROR_UNDEFINED =   0;
var ERROR_NOTICE =      1;
var ERROR_WARNING =     2;
var ERROR_CRITICAL =    3;
var ERROR_SILENT_WARN = 4;
var ERROR_SILENT_CRIT = 5;
var ERROR_SERVER_WARN = 6;
var ERROR_SERVER_CRIT = 7;

/**
 * This object, myError, is used to send errors where they are required.  It has
 * the following public methods:
 *     - throw(p_level, p_number, p_message, p_param)
 *     - restart(p_method)
 *
 * @type object
 * @constructor
 */
var myError = {
    /**
     * This member, level, holds the custom level of the error.
     * @type Integer
     */
    level: ERROR_UNDEFINED,
    /**
     * This member, number, holds the number of the error.
     * @type Integer
     */
    number: -1,
    /**
     * This member, message, holds the message of the error.
     * @type String
     */
    message: '',
    /**
     * This member, parameters, holds the optional parameters object for the error.
     * @type Object | null
     */
    parameters: null,
    /**
     * This method, sendToUser, formats the error and sends it to the user using
     * the /fillPopUp( )/ method for display.
     *
     * @member myError
     * @see #parseError
     */
    /* The following are constants to myError for user input on the pop up */
    FORM_EMAIL-OCCURRED: 1,
    FORM_EMAIL-INPUT, 2,
    sendToUser: function( ) {
        /*
         * This variable, format, will hold the formatted error to display to
         * the user
         */
        var format = '';

        // Decide how the error should be formatted, and do so here...

        if (this.parameters.form == this.FORM_EMAIL-OCCURRED)
            /* Create a form that has inputs for an email and what occurred */
        else if (this.parameters.form == this.FORM_EMAIL-INPUT)
            /*
             * Create a form that has inputs for an email and the input from
             * the user
             */
        /* ... */

        /* This is from Example 10-2 in Chapter 10 */

        fillPopUp('Error', format);
    },
    /**
     * This method, sendToServer, formats the error and, depending on the
     * /p_method/ passed in, will send the error to the database for logging,
     * email the developers, or both.
     *
     * @member myError
     * @param {Integer} p_method The method to send to the server.
     * @see #parseError
     * @see Ajax#Request
     */
    sendToServer: function(p_method) {
        /*
         * This variable, param, will hold the formatted error to send to the
         * server
         */
        var param = '';

        param += '<error>';
        param += '<number>' + this.number + '</number>';
        param += '<message>' + this.message + '</message>';
        if (this.parameters.file)
            param += '<file>' + this.parameters.file + '</file>';
        if (this.parameters.line)
            param += '<line>' + this.parameters.line + '</line>';
        if (this.parameters.trace)
            param += '<trace>' + this.parameters.trace + '</trace>';
        param += '</error>';
        /* What method should be used? */
        switch (p_method) {
            case 1:
                Ajax.Request('logError.php', {
                    method: 'post',
                    parameters: param
                });
                break;
            case 2:
                Ajax.Request('emailError.php', {
                    method: 'post',
                    parameters: param
                });
                break;
            case 3:
                Ajax.Request('logError.php', {
                    method: 'post',
                    parameters: param
                });
                Ajax.Request('emailError.php', {
                    method: 'post',
                    parameters: param
                });
                break;
        }
    },
    /**
     * This method, throw, takes the error parameters passed in by the user, sets
     * them to the object's members, and calls the error parser.
     *
     * @member myError
     * @param {Integer} p_level The custom level of the error.
     * @param {Integer} p_number The number of the error, usually given by the
     *     system.
     * @param {String} p_message The message of the error, usually given by the
     *     system.
     * @param {Object} p_param Optional object containing additional parameters
     *     to send.
     * @see #parseError
     */
    throw: function(p_level, p_number, p_message, p_param) {
        this.level = p_level;
        this.number = p_number & 0xFFFF;
        this.message = p_message;
        this.parameters = p_param;

        this.parseError( );
    },
    /**
     * This member, parseError, looks at the custom level of the error and
     * determines where the error should be sent.
     *
     * @member myError
     * @see #throw
     */
    parseError: function( ) {
        /* What is the level of the error? */
        switch (this.level) {
            case ERROR_NOTICE:
                this.sendToUser( );
                break;
            case ERROR_WARNING:
            case ERROR_SILENT_WARN:
                this.sendToServer(1);
                if (this.level != ERROR_SILENT_WARN)
                    this.sendToUser( );
                break;
            case ERROR_CRITICAL:
            case ERROR_SILENT_CRIT:
                this.sendToServer(3);
                if (this.level != ERROR_SILENT_WARN)
                    this.sendToUser( );
                this.restart(1);
            break;
        }
    },
    /**
     * This member, restart, restarts the application either when the custom
     * pop-up window has closed or regardless of what is happening in the
     * application.
     *
     * @member myError
     * @param {Integer} p_method Lets the object know if the error should be sudden.
     */
    restart: function(p_method) {
        /* Do we care if anything is going on and is the pop up visible? */
        if (p_method && Element.visible('popupContainer'))
            /* check again in a quarter of a second */
            setTimeout('myError.restart(1)', 250);
        /* Can we just restart? */
        else if (!p_method || !Element.visible('popupContainer'))
            window.location.href = window.location.href;
    }
}

You will notice that this input is always sent to a database for storage. This is the safest method to ensure that all errors are trapped and tracked in a consistent manner and that nothing is lost. This also will allow for better and easier error analysis for the developer.

Error handling is never a glamorous part of writing an application. However, it is one of the most important parts of that process—not so that you can impress the user, but so that your application has a stronger and more solid foundation to build upon. By thinking about what to do with an error, you will begin to subconsciously think about better error handling and trapping. This makes for better Ajax applications in the long run.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset