Introduction
This is a call to action to everyone building clients, servers and frameworks for rich internet applications (RIAs) to improve the life of RIA developers by improving the debugging of backend services RIAs depend on.
I’d like to thank the people who’ve offered valuable feedback + additional information that I’ve integrated here.
Motivation
The relationship of an RIA to its backend is quite different than that of a traditional Web application. In a traditional, dynamic HTML generation Web app, the output stream is under the control of the developer and the application server. Developers can freely mix application output with diagnostic information. By contrast, RIAs have a narrow communication channel with the server, typically using a service-oriented architecture (SOA). Because the output format is typically XML or JSON (and because there are no established standards for augmenting message payloads) there is no opportunity to include any additional information. This is particularly problematic during debugging.
Consider the following with Adobe Flex as the example frontend and ColdFusion (CF) as the example backend although the discussion applies to any RIA:
- An exception on the server would cause a service request to fail without providing any useful information to the developer. In particular, there is no way to see the exception information or the server-side stack trace, despite the fact that CF would generate ample information useful for debugging.
- Turning on debug information output on the server would break the service by adding HTML at the end of the XML output. Developers have no good way of consuming that debugging information other than test-driving the service in an HTML test harness.
- CFDump is a one of the most beloved tags in CFML. Almost every developer uses it in every application because it provides great debugging efficiency. There is no way to use CFDump in RIA development.
- There is no way to generate any auxiliary, Flash trace()-like output in service responses, something any HTML developer is used to doing. Again, when using services with RIA front-ends, developers are flying blind.
- Conversely, RIA developers have no good mechanism of creating any persistent application output. For example, while writing a message to a log takes one line with CFLog, an RIA developer would have to implement a logging service in order to achieve this capability and store some information for future analysis.
Debuggers are not the answer
Every backend environment has its own debugging capabilities. Also, there many ways to debug AJAX and Flex applications:
- Seeing the HTTP traffic using proxies such as Microsoft Fiddler or ServiceCapture
- AJAX debugging using MyEclipse
- Using GreaseMonkey to see HTTP requests on Firefox
- AJAX debugging with Firebug in Firefox
- AJAX debugging with Visual Web Developer Express on IE
- Flex debugging using the Flex Builder debugger or fdb
However, debuggers do not address the problem of efficiently debugging backend services for RIAs. Most Web developers don’t use or don’t know how to use debuggers effectively. Debuggers are good for step execution and occasional variable inspection. They are poor at displaying a lot of specific information efficiently. Also, using both a client-sde and a server-side debugger often slows down the develop-test-fix cycle. In short, using debuggers are no substitute for having flexible and powerful diagnostic output from backend services.
As an example, consider the actions a developer must take in a CF debugger to get the information equivalent to seeing the output generated by a CFQuery exception with stack trace enabled or a CFDump of a query with 200 rows. There is simply no comparison in terms of productivity.
The closest approaches I’ve found to what I’m proposing here are FirePHP and the ability of ColdFusion to return page debug data to the Flash player through FlashRemoting using ActionScript Message Format (AMF, a binary protocol) server debug response headers. (Mike Nimer pointed me to the CF-AMF capability and to a FlexDebugPanel that he built to better display the information.) Both approaches use response headers, which limits their ability to deal with large amounts of data. I also think the industry needs a broader and more standardized solution.
A personal tangent
A recent experience underscored these problems and was the personal motivation for me to write this. The latest RIA I built had a Flex 3 frontend with a CF8 backend. Despite its simplicity-one component (CFC) on the backend and only a couple of thousand lines of MXML-I constantly had to work around the lack of visibility when I was invoking services from the client. Before long, I found myself installing Fiddler to see the HTTP traffic and logging WDDX packets and CFDump output which I then crudely displayed in a browser window using a quick’n’dirty Web page. When using the client-side and server-side debuggers I was frustrated by the number of clicks it took to get to data I cared about and the amount of irrelevant information presented, e.g., tons of underscore-prefixed internal variables for unnamed controls I had to scroll through to get to variables I cared about.
When I pinged a few people building AJAX and Flex apps I heard the very same feelings about their debugging experience and the difference between building RIAs vs. traditional Web applications.
Many of the ideas proposed in this document are not new. Back in the spring of 2001, I built what may have been the first prototype of Flash communicating to servers using SOA. For the backend I used CF with a dynamic dispatch layer to custom tags (CFCs weren’t there yet). In testing the prototype I hit the same debugging problems. I implemented a feature that took diagnostic output from the server and displayed it as trace() output in the player. It made my life easier but it wasn’t a generic solution because it relied on a particular response format. The FlashRemoting team later on generalized the concept via the amf_server_debug response header, which is not an HTTP response header but a payload response header (think SOAP).
Some years ago I would have jumped to code the solution I propose here and open-source it, like I did a decade ago with WDDX. For better or worse, I don’t code that much these days. Instead, I’m focusing on helping entrepreneurs start companies. I hope this post inspires someone to tackle this problem and share their work with the RIA developer community.
Proposed solution
Improving the debugging of RIA service backends requires a multi-faceted approach that spans clients, servers and tools. In order of importance, there are four inter-related pieces:
- Establishing a diagnostic output channel from the server to the client. The goal here is to give Web developers familiar capabilities such as the ability to see exception diagnostics, to display diagnostic output and dump complex variables.
- Providing a persistent output channel from the client to the server. The goal here is to enable structured logging from the client to the server so that important diagnostic output can be preserved for future analysis.
- Implementing structured logging on the server. The goal here is to improve the power of logging by building the kind of logging system that makes developers more productive during the debugging cycle.
- Improving debuggers. There is no such thing as a true RIA debugger out there. Since this part of the solution has much broader scope, it is not covered here.
Architecture
There are multiple ways to implement the proposed solution. The approach outlined here is probably one of the simplest. It involves a single diagnostic output specification, three new services on the backend for server-side diagnostic output, client-to-server logging and log viewing, and a reusable viewer of diagnostic output. These capabilities are exposed in an appropriate manner through the frontend and backend programming models and frameworks.
Figure 1. RIA diagnostic output architecture
Diagnostic output specification
The diagnostic output specification has three parts:
- A schema for identifying the output format
- A mechanism for identifying specific output stored remotely
- A mechanism for passing diagnostic output identifiers from the server to the client
Diagnostic information is provided in “documents” (chunks, payloads, etc.) according to the schema. Each such document must be URI-addressable during its lifetime. The exact format of the URI is specific to a particular server environment. For example, in a ColdFusion deployment it can be a URL to the diagnostic output service. The URL will be parameterized such that the right diagnostic information is returned to the client. Let’s call this a DebugURI (dURI).
For performance reasons, the URI must be able to address sub-parts of any single pile of diagnostic information. For example, if the diagnostic information includes a reference to a 10,000 record query it wouldn’t make sense to either arbitrarily restrict how many rows the client can have access to or to ship all 10,000 lines with the diagnostic information request. However, to simplify implementation, the URI format need not be exposed to the client. For example, the client shouldn’t necessarily be able to request rows 9,212 – 9,532 of the query. Instead, the server can simply put a placeholder in a document saying “there is more data here and if you want it, request this URI and I’ll get it to you”. Let’s call this particular type of dURI a ContinuationURI (cURI).
The specific mechanism for passing diagnostic output identifiers from the server to the client is protocol and environment specific. An HTTP response header carrying the dURI is a simple choice that works well across languages and platforms and does not modify the response payload.
The diagnostic output schema is a format (not necessarily a formal XML Schema) for describing the information typically encountered while debugging applications, including but not limited to:
- Exception information
- Call stack information
- Data structure dumps
- Application state information
- Service requests & responses
- External service interactions, e.g., database queries and responses
- Performance-related information
- Developer-created diagnostics messages
There are trade-offs in the amount of detail in the schema. With more detail, providing support across environments will take a little more time but, on the other hand, the output viewer can be more capable, e.g., allowing dynamic expansion of data structures, etc. With minimal detail, developers will only be able to look at volumes of text in the viewer.
The schema should have built-in extensibility features.
Last but not least, the schema must specify the format of the cURI placeholder discussed above.
Backend support
Backend support includes a persistent and a transient store for diagnostic information and three new services.
Data stores
The backend must implement two data stores, one for run-time diagnostic information and one for persistent diagnostic information.
The runtime information store can leverage the traditional app server state management capabilities, in particular, session management. The information model is simple. Diagnostic information generated during the processing of a request can be maintained as an aggregate object indexed by dURIs. There needs to be a purging policy for efficient resource management.
The persistent information store can leverage the existing logging architecture of a server. However, a more efficient implementation may want to use an embedded database. See the following discussion on the log viewer.
Services
The debug output service is invoked via dURIs passed with a server response or generated by the log viewing service (see below) or with cURIs. It generates a diagnostic information document. It can use a default or user-configured policy governing the size and detail in these documents, adding cURIs where appropriate. The debug output service can pull information from both the transient and persistent stores.
The remote logging service takes a diagnostic information document and some additional meta-data describing where, when and how the diagnostic information was created and persists that information.
The log viewing service has two functions. It monitors the log for changes to know when new data is available. It also allows the log viewer to list and query (and potentially search inside) the available diagnostic information documents. Every available document is identified by a dURI that the client can pass to the debug output service to retrieve the document.
Client-side support
Client-side support comes via a pre-packaged viewer application. The viewer is an RIA that provides rich functionality for working with diagnostic output documents such as:
- Communicating with the application running on the client, allowing it to deliver diagnostic output that can be displayed richly. For example, this would be substantial improvement compared to basic trace() output in Flash or Log4Ajax.
- Listing of available information. Some information can be provided by the client. Transient information from multiple server interactions may be available. In addition, all the persisted information is available. The viewer should also be able to open a file in the diagnostic output format.
- Allowing some information to be persisted for future analysis. This becomes particularly interesting if there is support for annotations and sharing of the information with team members (via a URL to the debug output service or as an email attachment).
- Supporting flexible data structure navigation and basic search across the data structures and messages.
- Providing appropriate formatting for exceptions, call stack information, performance information, etc.
An ideal implementation of the viewer would be as an AJAX or Flex application component that can communicate with both Flash/Flex and other AJAX applications independent of the server environment. Client-side debuggers can add the viewer capability also. Perhaps the FireBug Working Group can take this on?
From a user-experience standpoint, the viewer should be able to run next-to an application as a docked component as well as a pop-up in a separate window, which would be particularly useful for developers with multiple monitors.
Specific environment support
These capabilities should be supported as appropriate by specific RIA environments. Ideally, support should be implemented at the application server level for maximum performance, efficiency, etc. As a second option, it can be provided as native or side-along extensions to common frameworks.
Specifying invocation intent
In some such environments it may be helpful to know whether a server request is made by a browser for the purposes of HTML display or as a service call. In the case of service calls, certain display-oriented capabilities such as debug output or the output of the CFDump tag in ColdFusion can be automatically redirected to contribute data to the transient diagnostic output store. In some cases the context of request can be determined directly from the HTTP-level information, e.g., a POST with Content-Type application/XML. In other cases, such as a simple HTTP GET to a RESTful service, it is difficult to do this without knowing a priori that the URL is a service. Some server components such as ColdFusion CFCs can be addressed in a number of different ways. It therefore may be helpful to consider having RIA clients optionally send a custom HTTP request header to identify the intent of the request.
Additional considerations
Managing diagnostic information in this way adds substantial overhead to application processing. It should only be enabled in debugging scenarios.
Exposing detailed server state information to remote clients poses a security risk. The backend services should require authentication and they should be able to run over a secure connection to prevent data snooping and man-in-the-middle attacks.
Manipulating diagnostic information with such ease can benefit traditional HTML application development also. Therefore, it is worth thinking about ways to integrate the view application with traditional HTML applications.
Conclusion
This document outlines a simple approach to improving the debugging backend RIA services across languages and platforms. The approach is amenable to standardization as it includes a common format for diagnostic output and a common viewer application.
These could be developed with open extensibility in mind and remotely-hosted viewer extensions allowing, for example, Ruby-on-Rails (RoR) to provide some special RoR-specific structured output that can be natively viewed via an extension (implemented as a JavaScript or Flex component) hosted on rubyforge.org.
Postscript
The discussion here points out some of the fundamental differences between RIAs and traditional HTML applications. Beyond improving the debugging of RIA services, there are many more opportunities to provide better support for RIA applications. After all, the first Web application server shipped a dozen years ago. Too much of what app servers do assumes dynamic HTML generation as opposed to Flex/AJAX clients. I’m surprised I haven’t seen more innovation in this area from large platform vendors. Smaller players such as Aptana (with Jaxer) and JackBe are pushing the boundary but their lack of distribution on the backend limits the effects of their innovation.
This has existed in SmartClient (Ajax RIA platform) since 1.0 in 2001, in increasingly refined form. It’s called the Developer Console.
It’s a SmartClient RIA that can be launched from any page that loads SmartClient, and appears as a separate browser window connected to the page that launched it.
The Developer Console has a view of client-side logging where you can enable or disable client-side log categories on the fly, similar to Log4j. You can also see all service calls, similar to Fiddler/Firebug but more purpose-specific.
If you are using the SmartClient Java Server, the Developer Console also contains a tab that allows you to view the most recent server-side logs as output via Log4j.
Your article seemed to me to imply some kind of special diagnostic support on the server specifically for the RIA – I don’t think you’d want to build diagnostic information that is *separate from* the logging and diagnostics you already do on the server. In a true RIA (or SOA) architecture the services you are calling should be generic enough to be called by other technologies, including possibly clients that aren’t web-based – if the logs aren’t good enough of debugging service calls made by RIAs, improve them until they are, then all possible client systems benefit.
More about the Developer Console:
http://www.smartclient.com/index.jsp#_Welcome_Developer.Console
Charles, I agree that what SmartClient has is cool. However, I disagree that providing log viewing services from the client is enough.
Log file formats are generic and not structured. To log complex data structures or other specialized information developers are forced to pick a format for representing that as human-readable text in the log file. Your client-side log viewer provides them with no special display and manipulation information, which makes working with complex information difficult. For example, how would one work with a 20K log file dump of an XML data structure? Last but not least, forcing extensive logging is “cost up front” operation that may not yield benefits all the time. That’s why I prefer a model where not all diagnostic information is persisted.
You raise another important point, which is that the backend shouldn’t provide any special RIA capabilities. I agree to the extent that the capabilities I’m talking about could be useful for debugging anything that doesn’t generate HTML output.
As for the type of diagnostic output that’s would be good to have and ways to display some of it, it’s easier to see than to explain. Having developed in most common platforms, from ASP.NET to RoR, I still think ColdFusion is the gold standard in diagnostic output that helps developers resolve problems quickly and intuitively without having to fire up a debugger. As for how this information should be displayed, I haven’t seen any great viewers. I believe that Mike Nimer’s FlexDebugPanel is probably the best example out there. I say that not having played with it but having known Mike for many years….
Hi Sim,
You bring up the exact reason we created Presto, our enterprise mashup platform at JackBe. We felt RIA would be the the future defacto Browser/UI approach and SOA-style (REST, RSS, WSDL, etc) would be the service approach. The two major problems, as you cited are communication and server insight. From a browser debugging/insight perspective, you got it right. Firebug et. al. help with that. But, on the server side if you just instituted logging developers wouldn’t have the whole picture. We introduced another tier that sits between the browser and the services (which may exist anywhere) called the mashup tier. Each mashup produces extensive profiling and debugging information not only about what it is doing (XML processing) but also information about the services it is consuming. Because of the enterprise nature of mashups and the fact that they sit on the server, we support sophisticated features such as nested parallel execution, timeouts, error continuation.
This same insight also applies when instituting SSO and PKI. You need to be able to see what is happening at the server level all before a single service invocation is made.
John Crupi, CTO
blogs.jackbe.com
Pingback: Debugging RIA Services « Rich Internet Applications
Hi Simeon,
I’m not sure where you perceive the difficulty here.. in actual practice, our users troubleshoot these kinds of problems easily, using the Developer Console, all the time. Probably, having not actually worked with the tool yet, you’re missing some of the capabilities, such as the ability to look at the server response as an interactive tree of XML, or get programmatic access to the response object itself.
There are client- and server-side equivalents of CFDump, that is, they will give you a “pretty-printed” view of any data structure, to a configurable level of detail.
There is no up front cost because the log categories can be enabled or disabled on the fly, and the data dumping only happens if the category is enabled.
Overall, I think that robust access to what the client system is receiving, combined with access to the server logs (which generally already exist) is a simple and appropriate approach. After all, if the purpose is to troubleshoot access to some service, how is it going to help to introduce a second service and second protocol for diagnostics? 🙂 Then you’d need to troubleshoot that 🙂
@John – troubleshooting a layer where services are composed or “mashed-up”, is, I think, not what Simeon was talking about (correct me if I’m wrong Simeon). Whether the service is contacted directly from the client RIA or by a kind of “mashup proxy”, the question is how you get to the information that explains why the service isn’t working, not why the *mashup* isn’t working.
I also don’t see what the big deal is. You can easily return escaped error data in a nice neat little field in the reponse, along with a flag set that there was a problem.
I mean, these suggestions you have are better, but the easy way is just the same as with a normal web app. You can even trace out the result if you don’t mind ignoring html tags.
Hi Simeon,
We have developed some pretty elaborate mechanisms to get important states and exceptional conditions out of our backend and to the RIA. A comprehensive system–such as you describe here–that would be flexible enough to be integrated with both our development tools as well as automated tools, would have saved us quite a bit of time.
I think the system you describe would also be extremely useful for acceptance test verification. Given the fact that the captured diagnostic output is formalized in XML (and not in a bunch of trace statements), it could be compared against expected diagnostic output.
And hey, while I’m at it how about an Eclipse plugin for Client-side support?
Thanks!
Matthew, thanks for chiming in. For the benefit of readers, Matthew is with Sprout. They’ve built one of the most sophisticated RIAs out there. His comments come from a lot of experience.
An Eclipse plug-in would do nicely for a lot of people but wouldn’t you feel sad for the millions of folks hacking on the Microsoft side?
Charles/Sean, I’m not saying that you *cannot* debug RIA services by looking at logs. I’ve done plenty of development that way.
What I am saying is that we can measurably improve RIA developer productivity by (a) establishing a common format/schema for common types of diagnostic output information combined with a common viewer for that type of information and (b) have a way to maintain some of it potentially automatically in memory on the server as opposed to having to persist everything in log files.
I don’t see how a world with no structured logging capabilities, when every app server, framework and tool provides its own way of dealing with diagnostic output leaves developers better off…
Hi Simeon,
Logs != disk. SmartClient retrieves server logs from an in-memory revolving buffer attached as a standard Log4j Appender.
Logs != unstructured. Log4j files are sufficiently structured to parse, and viewers already exist.
I think you’re still mystifying most readers as to what you’re getting at, and in my case, it’s certainly not due to lack of experience building complex RIAs (SmartClient shipped as the user interface to major BI products 5 years ago).
Perhaps if you went into more detail on some concrete scenarios that you believe are amenable to standardization.
For example, if you are connecting SmartClient to a non-Java server and you have no pre-existing services, we provide a complete protocol that includes signaling validation errors, session expired errors, stale cache errors, and general errors, with message formats available for JSON, XML or SOAP. Is that the kind of thing you have in mind?
Charles, yes, this is something along the lines of what I have in mind. I want the industry to end up in a better place than it currently is at. I want the millions of developers working on RIAs to be more productive. If SmartClient can offer good guidance, that’s wonderful.
Hi there, I am CF Developer since 2001 and started using CFAjax this year and was a really Kunfu fight, I mean thanks FireBug I finish the project. The cfdebug window it’s not a big help.
I started with Flex about year and half, I work very fine for me help me with the errors I always use CF for backend. I use RemoteObject object and I build a function on fault=”failError(event)” event, this catch everything happens and really makes my life easy. I just simple show inside a Alert box the errror.
The code looks like this.
private function failError(event:FaultEvent):void{
Alert.show(‘Error ‘ + event.toString());
}
In my little experience CF and Flex Rocks…
Simeon and all others commenting here, thanks for this detailed and thought provoking posting. I have lived in the troubleshooting world since working at Allaire back in the late 90’s, it is what I do day in and day out. My concerns about where we are going, like it or not, is into a quasi client server world where we have no control over the network we will run on. One avenue of thought for me, which was highlighted by Mike Labriola at the B-Flex conference is a scenario where Flex DataServices could have to communicate with a 1,000 distributed end-points and maintain data synchronization. My belief is that not enough of us are thinking through the full ramifications of where we are going.
Mike, yes, it’s a new world and I believe many are not thinking deeply about some of the complexities ahead.
Pingback: More on RIA Debugging « HighContrast
I thought I saw somewhere that there is no RIA debugging tools. Well…that actually is not true. RIA AppBuncher Debugger provides client-side debugging and this product line is being expanded to provide end-to-end RIA testing. A free trial of RIA AppPuncher Debugger can be downloaded for free here:
http://www.themidnightcoders.com/products/ria-apppuncher/overview.html