Log4Ajax Blog

Version 2



    Client-Side Logging
    Client-to-Server-Side Logging
    Network Optimizations
    Client Optimizations

    As you have undoubtedly heard, the latest and greatest four-letter acronym in the web-based software development world is AJAX. Despite the tidal wave of excitement and hype, there is another four-letter word that can be associated with AJAX:pain. The dirty little secret is that there are many reasons developing AJAX applications can hurt. Some of these reasons are the browsers' fault, due to the fact that each browser version offers differing levels of DOM and JavaScript compliance. Memory leaks in all existing browsers compound the problem. A portion of the responsibility also falls onto the "J" of AJAX: the loosely typed JavaScript has a syntax that is familiar to Java developers but is filled with gotchas you will inevitably run into. Arguably, the advent of AJAX is pushing JavaScript beyond its intended use and definitely beyond the skill set of the average web UI designer.

    An additional reason writing AJAX applications can be difficult is the lack of tools that are considered standard for server-side development, including a logging system. "Logging equips the developer with detailed context," according to the website for the popular Java logging framework Log4j. Because your AJAX application executes on the client machine and you have no detailed context of its state, how do you know when an error occurs? How do you know what events and application defects lead to the error? Thorough testing before a release increases your confidence in the product, but you can't prove a negative; just because bugs are not reported does not mean your end users are not experiencing them. Through logging, you have enough detailed context that you can be confident your application is not experiencing errors and is running smoothly.

    When starting an AJAX project and writing a lot of JavaScript, the alert function is your new best friend and, for the short term, is sufficient as a debugging tool. But as the complexity of your AJAX applications increase, using alerts becomes tedious, irritating, and time-consuming. Worse, when working on event-driven code, alerts have the potential to disrupt the normal flow of program execution. At some point, the cons of using alerts in debugging begin to outweigh the pros, and the need for an improved logging mechanism becomes clear.

    Joshua Gitlin's article "Errors and AJAX" details an error-handling system for transmitting JavaScript errors to the server and will ease some of the pain you'll experience while working with AJAX. While a step in the right direction, his solution is concerned only with improving error handling. By using Mr. Gitlin's concept as a foundation and expanding the scope of the problem, a client- and server-side logging solution can be developed. By using this solution, all of the advantages and benefits of traditional logging systems, including improved error handling, become available to AJAX applications. Because the system I describe relies on the use of AJAX and Log4j, I have given it the name Log4Ajax.

    Before diving into the details of Log4Ajax, this article assumes the reader has some level of familiarity with AJAX and DHTML. If not, the Web contains a wealth of information that would be redundant to duplicate here. Links to several good sources, including Joshua Gitlin's "Errors and AJAX," are included in the Resources section.

    Client-Side Logging

    While creating software, developers typically use an integrated development environment (IDE), within which a console displays log information. Seeing this log information as the code is executed provides immediate feedback that the code does or does not work as intended. To provide this IDE-ish console capability, Log4Ajax creates a div inside of the browser dedicated to display log information (from here on referred to as theconsole div). By appending debug text to theinnerHTML of the div, the look, feel, and utility of an IDE logging console is available within your AJAX application.

    Let's dive into the sample code. The content ofExample1.html is HTML and JavaScript typical of most web pages:

    //Example1.html <html> <head> <title>Log4Ajax Example</title> <link rel="stylesheet" href="style.css" type="text/css" /> <script type="text/javascript" src="js/request.js"></script> <script type="text/javascript" src="js/log4ajax.js"></script> </head> <body> <center> <h2><code>Log4Aajax Example</code></h2> <br /> <table> <tr> <td> <input type="button" id="button1" value="Button 1" onclick="LOG.debug('Clicked button: ' + this.id);" onmouseover="LOG.debug('Mouseover button: ' + this.id);" /> </td> <td> <input type="button" id="button2" value="Button 2" onclick="LOG.debug('Clicked button: ' + this.id);" onmouseover="LOG.info('Mouseover button: ' + this.id);" /> </td> </tr> </table> <!-- Log Console --> <table align="center"> <tr> <td> <div class="consoleDiv" id="logConsole"></div> </td> </tr> <tr align="center"> <td> <input type="button" id="clearLogConsole" value="Clear Debug Console" onclick="getElementById('logConsole').innerHTML='';" /> </td> </tr> </table> </center> <script type="text/javascript"> LOG.debug("Initilization complete"); </script> </body> </html> 

    Nothing in Example1.html probably jumped out as particularly exciting. The section of interest, however, is just after the Log Console comment. By creating an HTMLdiv with a well-known id, we're allowing the content of this div to be manipulated through JavaScript. The position and size of the console divwithin the browser is arbitrary, but the css scroll style attribute is set to a value of AUTO to enable scrolling. The id of this div is then put to use within log4ajax.js:

    // log4ajax.js /** * Provide logging capabilities to AJAX applications. */ function LOG() { throw "Do not instantiate LOG"; } LOG.consoleDivId = "logConsole"; LOG.transmitToServer = true; LOG.debug = function(msg) { LOG._log(msg, "debug"); } LOG.info = function(msg) { LOG._log(msg, "info"); } LOG.warn = function(msg) { LOG._log(msg, "warn"); } LOG.error = function(msg) { LOG._log(msg, "error"); } LOG.fatal = function(msg) { LOG._log(msg, "fatal"); } LOG._log = function(msg, logLevel) { LOG._logToConsole(msg, logLevel); // .... } LOG._logToConsole = function(msg, logLevel) { var consoleDiv = document.getElementById(LOG.consoleDivId); if (consoleDiv) { consoleDiv.innerHTML = "<span class='log_" + logLevel + "'>" + logLevel + "</span>: " + msg + "<br />" + consoleDiv.innerHTML; } } 

    log4ajax.js defines a JavaScript object namedLOG. This object provides the five standard log functions: debug, info,warn, error, and fatal. These functions are very straightforward and simply pass a string representing the log level and the string argument msgto an internal function (indicated by the underscore in the function name) named _log. The _logfunction in turn, passes both the msg andlogLevel arguments to another function named_logToConsole, which uses the well-knownid contained within the variableLOG.consoleDivId to acquire a reference to the consolediv. Through the use of this reference, the function argument msg is appended to the existing content of the console div. To pretty up the output, the log level is wrapped by a span tag to change the color.

    Now that we have a console to display log information and the plumbing code in place, what does this buy us? As we have done inExample1.html, pepper your code withLOG.debug() (or any of the various levels) statements, refresh your browser, and watch the information accumulate in the console div as your JavaScript executes. The familiar log console you are accustomed to having inside of your IDE is now right inside of your browser.

    You can already see examples of the console divconcept in the wild. While looking over the Google Maps API, the following line of code is sprinkled throughout their project:

    ... document.getElementById("message").innerHTML = latLngStr; ... 

    Google is using the exact same technique and is creating adiv in the browser (not shown), getting a reference to that div through the use ofgetElementById and a well-known id, and modifying the innerHTML to display the contents of the string latLgnStr.

    Although these examples are extremely trivial, you can begin to see why this useful. You are now able to execute your JavaScript and see the individual steps of its execution without the use of alerts or a debugger. As your JavaScript and AJAX applications become increasingly complex, the utility of this consolediv only increases, saving you time and making you more productive.

    Client-to-Server-Side Logging

    Beyond using logging as an aid during development, it is also used to provide a history of what events occurred as actors used the system in your deployed environment. At any time, this history can be analyzed to assess the health of the application. While a standard capability in "traditional" software development, AJAX applications run in the client browser and lack the ability to persist client-side history. As the name implies, Log4Ajax solves this problem by using AJAX transmissions of the client-side context to the server, where they are persisted. Again, let's dive into some sample code. This time, we add additional functionality to the previously discussed log4ajax.js:

    // log4ajax.js revised ... LOG._log = function(msg, logLevel) { LOG._logToConsole(msg, logLevel); if (LOG.transmitToServer) { LOG._logToServer(msg, logLevel); } } LOG._logToServer = function(msg, logLevel) { var data = logLevel.substring(0, 1) + msg; // Use request.js to make an AJAX transmission to the server Http.get({ url: "log", method: "POST", body: data, callback: LOG._requestCallback }); } LOG._requestCallback = function() { // Process any return value data here } 

    The additional code passes the variable msg and the log level to the function _logToServer. Within_logToServer, we concatenate these function parameters into a string and transmit this string to the server. To try to help isolate AJAX inconsistencies between different browsers, I chose to use Http, a freely available AJAX library written by Adam Vandenberg, for our AJAX transmissions. Once our transmission is received by the server, it is parsed and the relevant information is captured using any logging framework available on your server. Although this article is Java-centric, there is nothing that ties the Log4Ajax or AJAX concepts to Java. The server-side process that accepts the AJAX transmission can be implemented with any number of technologies; for examples in this article, I have chosen to implement this process with a Java servlet filter. Here is the sample code:

    // Log4AjaxFilter.java public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { String s = ""; try { s = this.readInput((BufferedReader) req.getReader()); if (s.length() > 0) { HttpSession session = ((HttpServletRequest) req).getSession(); String userId = (String) session.getAttribute("userId"); if (userId == null) { userId = "anonymousUser"; } String logLevel = s.substring(0, 1); String message = s.substring(1); this.log(logLevel, userId + "\t" + message); } } catch (Exception e) { LOG.error(e); } finally { PrintWriter pw = resp.getWriter(); pw.println(s.length()); } } /** * Log the message at the specified level using the underlying log * mechanism. * * @param logLevel * The category to log the message at, ie: debug, info, etc. * @param msg * The message to log. */ private void log(String logLevel, String msg) { if ("d".equalsIgnoreCase(logLevel)) { LOG.debug(msg); } else if ("f".equalsIgnoreCase(logLevel)) { LOG.fatal(msg); } else if ("i".equalsIgnoreCase(logLevel)) { LOG.info(msg); } else if ("e".equalsIgnoreCase(logLevel)) { LOG.error(msg); } else if ("w".equalsIgnoreCase(logLevel)) { LOG.warn(msg); } } 

    Using the filter, the client-side context is captured on the server. Since this implementation is using Log4j, the actual output format is configurable. Depending on your choice of configuration, the output will be similar to Figure 1:

    Figure 1. Sample output log

    Transmitting client-side information to the server to be captured by a standard logging mechanism is at the heart of Log4Ajax. The code is simplistic and trivial, but the capability it offers is powerful. You are now able to capture debug and event information from AJAX applications, just as you would expect to be able to do from a thick client. There are probably dozens of areas in your AJAX applications that could benefit from this concept.

    Network Optimizations

    The provided code samples demonstrate the basic Log4Ajax concept and are not extremely sophisticated. As with all software, there are several opportunities for improvement beyond the basic working system that are centered on wisely taking advantage of the available processor and network resources.

    Overhead is associated with establishing and tearing down network connections. Making an AJAX transmission for each log statement is highly inefficient and could possibly saturate your network. To minimize this problem, reduce the number of transmissions by buffering log statements. Each time a log method is invoked, increment a counter and write the statement to both the console div and a statement cache. Once the counter has reached or exceeded some integer threshold, read the entire statement cache, bundle it into a single transmission, transmit it, and clear the statement cache. For maximum flexibility, the design of the receiving process, likely a servlet or filter, should support both the bundled and individual log request formats. As a word of caution, error and fatal log statements should not be cached and should be sent without delay, or you'll risk encountering a fatal error before these statements are transmitted.

    Depending on the value of the integer transmission threshold, buffering statements opens the possibility that a large period of time might pass before enough statements accumulate to warrant a transmission. By using a timer, provided by JavaScript'ssetInterval() method, the statement cache can be periodically transmitted and flushed to reduce the amount of logging data lost, should your application suffer an unrecoverable error.

    A client-side performance improvement over the demonstrated Log4Ajax example is to take advantage of the asynchronous capabilities of the browser's AJAX support. Since the flow of logging information is unidirectional, there is no need to synchronously transmit log statements and have the browser wait for an empty or nearly empty response from the server. Asynchronously fire and forget.

    Client Optimizations

    The use of Log4Ajax, as that of any logging system, has performance implications that you need to consider. Fortunately, there are a few enhancements that can be made to minimize the impact on your application.

    Constantly appending text to the console div will have a negative impact on browser performance. I have no hard numbers for you, but as your application runs, you'll inevitably notice that the logging code takes longer to execute than does the "meat" of your application. How much time will pass before you experience this will vary from system to system, but it's on the order of minutes and not hours. Consider adding the ability to periodically empty the display div and the overall performance impact of this problem will be negligible.

    A standard feature of all logging systems is the ability to reduce or filter the amount of information logged. While in early development, most projects log at the debug level and transition to less-verbose levels as the project progresses toward production. This log-level threshold concept could be implemented into Log4Ajax on both the writing of messages to the consolediv and the transmission of these messages to the server. These two settings could then be adjusted independently, allowing only error- and fatal-level information to be displayed in the console div while having all debug, info,warn, error, and fatalinformation transmitted to the server, for example.

    No matter how many optimizations you implement, the hard fact is that any use of AJAX has the potential to increase congestion on your network and can impact the client browser performance. Keep this in mind as you design your application and your targeted network infrastructure.


    None of the concepts used in Log4Ajax are earth-shattering or overly complex. It is the combination of simple solutions that provides a flexible yet powerful client- and server-side logging system. With the implementation of the described advanced features, the value added by Log4Ajax becomes quite high. It won't rid the world of disease, but I believe it will help you deliver a higher quality AJAX application in a shorter amount of time.