Logging, Error Handling, and Debugging
D-1. Logging
Logging is an integral part of any application. It helps developers and support personnel monitor the execution path under different scenarios and diagnose the problems. A well implemented logging mechanism in the application reduces the time to identify the root cause of the problem. Developers should be careful about the amount of logging they implement in the application. On the one hand logging is very helpful but on the other hand, too much logging impacts performance and requires a huge amount of disk storage.
Because a security mechanism is built into most browsers which prevents JavaScript from writing to a file on the client’s machine/device, it is not possible to log messages in a file on the client side. JavaScript code can write log messages by using the following mechanism:
Basic Logging
The newer versions of all major browsers provide access to their web console. In JavaScript, you can use Console APIs to log messages in the browser’s web console. Refer to the Recipe B-2 in Appendix B for the details about how to use the web console in various browsers. The following are the console APIs that can be used in JavaScript:
Listing D-1 shows the use of console APIs.
Listing D-1. Console APIs
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<script>
console.log("Test Message");
console.warn("Test Warning Message");
console.error("Test Error Message");
// Create object
var obj = new Object();
obj.id = 1;
obj.name = "John Smith";
var departments = new Object();
departments.department1 = "Infrastructure";
departments.department2 = "Development";
obj.departments = departments;
console.dir(obj);
</script>
</head>
<body></body>
</html>
To open the console window in the Firefox browser, click Open Menu in the top-right corner of the browser, click Developer, and then click Web Console. Figure D-1 displays the log messages in the Firefox browser console. Make sure that the Logging tab is active (that it’s clicked to display logging messages). Message display styles are slightly different in other browsers.
Figure D-1. Output of console APIs in the Firefox browser
To open the console window in the Chrome browser, click the Customize and Control button in the top-right corner of the browser, click Tools, and then click Developer Tools. Figure D-2 displays the log messages in the Chrome browser console. Make sure that the Console tab is active (it is clicked to display logging messages).
Figure D-2. Output of the console APIs in the Chrome browser
To open the console window in Internet Explorer browser, click the Tools button in the top-right corner of the browser and then click F12 Developer Tools. Figure D-3 displays the log messages in the Internet Explorer browser console. If you have opened the console after loading the HTML page and the messages are not displayed in the console, refresh the page to see the messages in the console.
Figure D-3. Output of console APIs in the Internet Explorer browser
To open the console window in the Safari browser, click Display a Menu for the Current Page button in the top-right corner of the browser, click Develop, and then click Show Error Console. If the Develop menu item is not available, make sure that the Show Develop Menu in Menu Bar checkbox is checked under Safari Settings Preferences Advanced tab. Figure D-4 displays the log messages in the Safari browser console.
Figure D-4. Output of console APIs in the Safari browser
Logging Framework
There are many open source JavaScript logging frameworks available. I like the Log4JavaScript framework as it is based on the industry standard and widely used Log4J logging framework. I’ll cover the basics of the log4javascript library in this appendix. One of the main advantages of using this framework is that you can easily change the logging severity level without making code changes in many places. For example, you can set the logging severity as ALL during the development and when the code is deployed in production, just change the logging severity to ERROR without making any other changes in the code. If you want to investigate an issue in production, you can change the severity to DEBUG. Without such logging frameworks, developers have to comment and uncomment logging messages all the time during various stages of the development lifecycle, which is time-consuming and prone to errors.
To download the log4javascript library, go to http://log4javascript.org and click the Download button. At the time of this writing, the latest version is 1.4.9. After you have downloaded the file log4javascript-1.4.9.zip, extract all the files from it. Copy log4javascript-1.4.9jslog4script.js to the scripts/ directory under your development root directory.
Log4JavaScript has the following five main concepts, all of which are used to log messages:
Loggers
The logger is the named reference pointer to which appenders are added, severity levels are set, and methods are executed to log the messages. A web page can have multiple named loggers. Multiple loggers are useful if you want to send different messages to different logging targets (different appenders).
Appenders
The appender specifies where the log messages will be sent/logged. Multiple appenders can be added to the same logger. The following is a list of appenders:
var appender = new log4javascript.AlertAppender();
var appender = new log4javascript.AjaxAppender(url);
where url is the server-side program URL (for example, the web service URL).
var appender = new log4javascript.PopUpAppender();
var appender = new log4javascript.InPageAppender();
var appender = new log4javascript.BrowserConsoleAppender();
Severity Level
The severity level of the logger specifies the severity level of the messages that will be logged. There are six types of message severities—TRACE, DEBUG, INFO, WARN, ERROR, and FATAL. The following is the ascending order of the severity levels. You can set the level on the logger. When the logger is used to log the message, only those messages that have the same or a higher level will be logged. For example, if the logger’s level is set to ERROR, messages with a severity of ERROR or FATAL will be logged. This level is preferable in the production environment. If the logger’s level is set to ALL, messages with the severity TRACE, DEBUG, INFO, WARN, ERROR, and FATAL will be logged. This level is preferable in the development environment. If the level is not explicitly set on the logger, the default value of DEBUG is used, which means the messages with the severity DEBUG, INFO, WARN, ERROR, and FATAL will be logged.
Pattern Layout
The pattern layout specifies the pattern of the logged messages. It is set at the appender level. For example, "%c %d - %m%n" specifies the following:
For the complete list of patterns and formatting, refer to this site: http://log4javascript.org/docs/manual.html#layouts.
Log Messages
You can log messages with various severity settings by using following statements:
log.trace("message");
log.debug("message");
log.info("message");
log.warn("message");
log.error("message");
log.fatal("message");
where log is the logger that has at least one appender added to it and message is the message to be logged.
You should set up the loggers with the appenders, level, and pattern layout at the global level so that changes in the severity level can be set or an appender can be changed in one place only. Once a logger is set at the global level, it can be used anywhere in the page within the JavaScript code.
Listing D-2 demonstrates a basic example of using the log4javascript library.
Listing D-2. Basic logging
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<script src="scripts/log4javascript.js"></script>
<script>
// Set Logger
var log = log4javascript.getLogger("myLogger");
// Get Appender
var appender = new log4javascript.InPageAppender();
// Set message pattern
var layout = new log4javascript.PatternLayout("%c %d - %m");
appender.setLayout(layout);
// Set Appender
log.addAppender(appender);
// Set Level
log.setLevel(log4javascript.Level.ALL);
// Log Message
log.info("This is the test message.");
</script>
</head>
<body></body>
</html>
Figure D-5 displays the log message at the bottom of the web page when the page is viewed in a browser.
Figure D-5. Output due to the InPageAppender
You can filter and search all the logged messages. You can also set options to log, wrap, bring the newest message to the top, and scroll to the latest message.
The following code segments show different logger types and appenders:
var log = new log4javascript.getDefaultLogger();
log.info("This is informational message");
var log = log4javascript.getLogger("myLogger");
var appender = new log4javascript.PopUpAppender();
log.addAppender(appender);
log.info("This is informational message");
var log = log4javascript.getLogger("client");
var appender = new log4javascript.InPageAppender();
log.addAppender(appender);
log.error("This is the client error message");
var devLog = log4javascript.getLogger("development");
var devAppender = new log4javascript.PopUpAppender();
devLog.addAppender(devAppender);
devLog.debug("This is the debug message");
Messages logged by the log logger will go to the inline window and messages from the devLog logger will go to the pop-up window. In real-world applications, all error and fatal-level logs should go to the log with the appender AjaxAppender so that all error and fatal messages can be logged to a central location on the server side and can be used by developers and production support personnel to diagnose the problem.
var log = log4javascript.getLogger("main");
var appender1 = new log4javascript.InPageAppender();
log.addAppender(appender1);
var appender2 = new log4javascript.PopUpAppender();
log.addAppender(appender2);
log.error("This is the client error message");
The messages will be logged in two places—inline, that is, on the same web page, and in the pop-up window.
D-2. Error Handling
If an error occurs during runtime and it’s not handled in the JavaScript code, no error message will be displayed to the user. If you open the browser’s web console you can see those messages as unhandled exceptions. For example, if you view a page with the following code in a browser, no error message will be displayed and the JavaScript code execution will terminate at the point of error.
displayMessage("My Message");
Due to the missing function—displayMessage()—an exception will be thrown by the code. If you open the browser console window, you will see the following error message:
"ReferenceError: displayMessage is not defined"
These four statements handle JavaScript exceptions:
Try and Catch
Listing D-3 demonstrates an example where an error is caught and handled.
Listing D-3. Using try and catch for exception handling
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<script>
try {
// some code
displayMessage("My Message");
}
catch (errorMsg) {
alert("Error: " + errorMsg);
}
</script>
</head>
<body></body>
</html>
When this page is viewed in a browser and the JavaScript code is executed, an exception (error) is thrown at the line displayMessage("My Message"); and the JavaScript code is not executed further within the try block. Execution control will go to the catch block. The parameter errorMsg in the catch statement contains the text of the error message. In the catch block, you can handle this error condition any way you want. Some of the possible actions you can take are to:
Figure D-6 displays the error message as a pop-up message.
Figure D-6. Displaying an error message
Finally
Listing D-4 shows the use of the finally statement. The code segment within the finally block is executed at the end of the try and catch blocks, regardless of whether code execution is completed in the try block or in the catch block. Generally, clean-up tasks like closing the connection or re-initializing variables are coded in the finally block.
Listing D-4. Using a finally statement
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<script>
var status = "";
try {
testFunc();
status = "Successful";
} catch (err) {
status = "Failed";
} finally {
alert("DOM ready function is completed with the status: " + status);
// Cleanup or reinitialization code
}
</script>
</head>
<body></body>
</html>
Figure D-7 displays the pop-up message with the status as Failed because testFunc() doesn’t exist. An exception has occurred and the status is set to Failed in the catch block.
Figure D-7. Display error message from the finally block
If the testFunc() function exists in the JavaScript code, the following message will be displayed:
“DOM ready function is completed with the status: Successful”
Throw
Listing D-5 shows the use of the throw statement. By using the throw statement, you can throw an exception with the custom message. The throw statement can be used anywhere in the JavaScript code (not necessarily in the catch block only).
Listing D-5. Using a throw statement
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<script>
function testFunc() {
try {
// some code
displayMessage("My Message");
}
catch (errorMsg) {
throw "Programming error occurred.";
}
}
try {
testFunc();
} catch (err) {
alert(err);
}
</script>
</head>
<body></body>
</html>
Figure D-8 displays the Programming Error Occurred pop-up message. Because displayMessage() doesn’t exist, the code in the catch block is executed, and it has thrown the custom error message—"Programming error occurred"—to the caller function.
Figure D-8. Displaying a custom error message
If the same variable is referenced in try and also in catch or finally, it should be declared outside the try, catch, and finally blocks because the scope of the variable declared within the try, catch, and finally block is limited to that block only.
D-3. Debugging
Debugging is the process whereby developers can execute a single line of code at a time and then view the value of the variables (primitive and objects) at any step in the code-execution process (provided the variable is still in the scope). Debugging is helpful in finding bugs and fixing them quickly. This section covers how to debug a simple program using the Firefox browser. Listing D-6 contains a simple program in which an employee object is set and then used to display all the departments for the employee.
Listing D-6. Setting and getting the employee’s object (the filename is debug.htm)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<script>
function displayDepartments(employeeObject) {
for (var i=0; i<employeeObject.departments.length; i++) {
// Display department names of the employee
alert(employeeObject.departments[i]);
}
}
// Create employee object
var empObj = new Object();
// Set employee object
empObj.id = 1;
empObj.name = "John Smith";
// Set department names in an array
var departments = new Array();
departments.push("Infrastructure");
departments.push("Development");
// Set departments for the employee
empObj.departments = departments;
// Call displayDepartments() function
displayDepartments(empObj);
</script>
</head>
<body></body>
</html>
The following is the list of steps that need to be performed to debug the code:
Figure D-9. Opening the Firefox debugger window
Figure D-10. The debugger window with the source code
In the source code window, you can set or remove the breakpoints by clicking the gray area left to the line numbers. The breakpoint is the place in the code where code execution will stop so that the developers can step over it or through it. Let’s set up the breakpoints at the line numbers 10 and 27. When you refresh the page, you will see Figure D-11 with the line where the code execution has stopped highlighted.
Figure D-11. Debugger window with the breakpoints in the source code
Code execution buttons are located at the top left-corner of the debugger window. In Figure D-12, code execution buttons are displayed. The first button (Resume) continues the code execution up to the next breakpoint or up to the end of the code. The second button (Step Over) executes the current statement. The third button (Step In) steps inside the function (if the breakpoint line contains a function). The fourth button (Step Out) steps out of the current function.
Figure D-12. Code execution buttons
Click the Step Over button. At this point all properties of the employee objects are set. If you want to see the elements of the empObj, click empObj and then click Watch. Figure D-13 displays empObj in the Watch expression window.
Figure D-13. Watch expression for empObj
From this watch expression, you can change the value of the variable or properties of the object at runtime. This is helpful if you find a bug. Instead of having to terminate the debugger session, make the code changes, and then restart the debugger, you can change the value(s) at the debugging time and continue the code execution. If you get the expected result, you can make the code changes after the debugging is completed. For example, in this example I changed the value of second department from “Development” to “Support” by clicking the value Development, entering a new value called Support, and then pressing Enter. Figure D-14 displays the updated value of the second department name.
Figure D-14. Watch expression for empObj with updated department
Now press the Resume button to jump to the next breakpoint. By clicking the Resume or Step Over buttons, you can see the Infrastructure pop-up message. Click OK from this pop-up message and then click Resume or Step Over again. This time you will see the Support pop-up message. Click OK and then the Resume button to execute until the end of the code.
The newer versions of other browsers have similar debugging capabilities.
3.137.178.9