Working in a development team can be tricky. Of course, everyone has their individual style of coding and their own conception of what structures and design patterns should be used to create desired functionality. But also there are more practical issues, like who is working in which files and how code will be merged. One of these more practical issues is how your application talks back to you.
Making Your App Talk
Wether it’s a trace() statement in AS3, an NSLog() command in Objective C, or an alert() in JavaScript; every language has it’s mouthpiece. A device by which the developer can tell the application to spit out human-readable information from within the running program is essential to the development and debugging process.
A Tiny Tower of Babel
In Ham’s AS3 core library, we had always used the intrinsic trace() statement for this purpose, sporadically spread throughout the the core and all published applications. After functioning this way for years, we realized that the output console was filling up quickly with trace() statements from 4 or 5 different developers. Aside from the, admittedly minor, extra computational power these trace() statements absorbed, it became very difficult to find your own messages among the clutter.
Stranger Danger
We also realized the danger in allowing these traces to be published within a live application: you never know who has the debug version of Flash Player installed. Anyone running the debug Flash Player can easily see the content of all trace() statements. While this may be a relatively minor concern for some, it is in some ways a breach of security.
Consider the case where a developer needs to confirm that privileged information from an application is being stored or computer correctly. They may use a trace() to reveal a password, credit card number, or cheat code intended for their eyes only. If they forget to remove the trace() or rely on the public not having the debug player, this privileged information could be exposed unintentionally.
The Solution
While we don’t deal with all that many credit card numbers at Ham, we still need to take security seriously so we realized the need to create a system to prevent unwanted trace() statements from making it into the wild. Furthermore, a system by which one developer could prevent the output of all messages other than their own would be enormously helpful to the development process.
We ended up calling our solution the OutputManager; a singleton class to manage all output to the debugging console. Essentially, this class is intended to be the ONLY location in the entire core library where a trace() statement could be found. This way, there was a single choke point at which we could control the flow of data to the console. We ended up shortening the class name to “Out,” because who wants to type 13+ characters (“OutputManager” + method names) when they could just type 5 (“trace”)?! This is an important thing to remember when writing classes that are intended to be used by multiple developers; if it’s inconvenient in any way, it won’t be universally adopted.
So, we created a singleton class with several static methods in order to log both regular messages (logM) and errors (logE). Making the methods static and forcing instantiation on the first line of the method also made the methods more convenient to use. Instead of having to create and store an instance of the OutputManager, a dev could simple type Out.logM() and trace away.
These “log” methods in turn call a private method in the OutputManager that performs the actual trace. This is the point at which we wrapped the trace() statement in a conditional that checks if the app is allowed to perform traces. By simple setting this Boolean early in the run sequence of the app, we can block all output messages from reaching the console.
The Finishing Touches
After using the OutputManager system for some time, we discovered a couple pieces of functionality that could make the class even more useful. First, we realized that having an automatic way to announce which class and method from which the message originated would be very helpful for debugging. Also, we found that one of our original problems was still present; too many traces in the output console from other developers. This meant it was still difficult to find your own messages in the crowded output window.
To solve the issue of automatically notating the location of the original message, we built off the suggestion of this article. By throwing an Error in the OutputManager when a message was sent to be traced, we could parse through the text of the Error’s stack trace and capture the class and method names, along with the line number. The one issue we ran into when implementing this, is that throwing an Error in this way will cause the non-debug version of the Flash Player to freeze up, as it cannot handle the Error properly. To overcome this, we employed the Capabilities class’ isDebugger property. By checking if this property is set to true before throwing the Error, we could avert the issue altogether.
We were able to solve the problem of a crowded output console by adding something we call the “Override Flag.” This flag is a String set on the OutputManager at some point (presumably early) in the run sequence of the app using the setOverrideFlag() method. After the flag has been set, any messages sent to the log() methods in the OutputManager that do not include the correct override flag String (as an optional parameter) will not be traced. This way, a single developer can choose, with minimal effort, to see ONLY their own trace messages in the output console.
Take a Look
Download a zipped copy of the Out class here.