Introduction

Traceract is a prototype debug message tracer. The name is a combination of the words "tesseract" and "trace", as it adds "dimension" to the usual debug trace output. In particular, Traceract provides a simple command interface, directing the viewer to output trace messages to a particular window. Although it is still in its infancy, I find it quite valuable simply as is, although there are a myriad of features I would like to add to it over time. One reason to put this in the public domain is that I'd also like to get feedback as to what kind of features other people would like to see.

Features

Weifen Luo's excellent docking manager is used to enable the user to customize and control the layout of the debug windows.

Traceract currently uses a ListView in detail mode to display the debug number, time, and message. This is a bit limiting, especially with regards to line length. Traceract currently wraps lines automatically (and quite dumbly).

Commands

Traceract uses the "!" (exclamation) character, positioned as the first character in the debug string, to identify a command. If the remaining portion of the string does not match the required format, Traceract outputs the debug string as normal.

Initialization

Your application is responsible for defining the output windows to which it will be directing debug strings. This is done with the initialization command:

!!<tag>=<name>

tag is the shorthand name you will be using in the debug string text to identify that the string is output to the window referenced by the tag. name is the human readable name you are assigning to the output window. By default, all Windows appear on the document tab strip.

Other commands

The only commands that Traceract currently supports are Clear and ClearAll:

!!Clear:<tag>

This clears the debug strings in the output window specified by the tag name.

!!ClearAll

This clears all the debug strings in all output windows.

Under the hood

The code that I'm primarily going to illustrate here is the debug message string handling. I will also briefly describe the declarative UI and serialization of the dock panels. However, a full discussion of the dock panel manager that is a wrapper for Weifen Luo's DockPanel Suite will be presented in a separate article, as I'd like to illustrate using both his toolkit and the Infragistics docking manager, and how one writes a wrapper that allows one to use either docking manager in an application without changing the application code.

Debug messages

The Debug Message Monitor

At its core, Traceract intercepts the debug messages in a worker thread. The main loop:

reads a string, splits it into discrete lines, and invokes the DbgHandler event.

The Event Handler

The event handler does some brute-force decoding of the debug string to check for tags and commands. It then adds a DebugMessageEventArgs instance to a queue that is monitored by a separate worker thread. The reason for this is that the debug monitoring event thread cannot perform a Form.Invoke, nor can it post messages to the Windows message queue. I haven't really looked into why (my knowledge here is ignorant). You will note that a hashtable handlerToContentMap is used to obtain the DebugListView instance that actually handles the display of the debug messages associated with the tag specified in the debug string.

Queue processing

As mentioned above, the debug monitor thread cannot directly perform a Form.Invoke or post messages to the application. Instead, the thread adds the messages to a queue. Again, the code implementation is rather brute force--I could have used semaphores but chose a much simpler approach:

The above code will create a window on-the-fly when a new tag is encountered. Also, it will set the window's name to a previously specified human-readable caption, otherwise it uses the tag text. Lastly, it invokes the DebugMessage event. This event is hooked by all output windows, and each window gets the opportunity to display the debug message as determined by the filter (the tag). I chose this approach so that in the future, a single window can be told to "watch" for multiple tags. Again, some optimization would be helpful here--only invoking the handler associated with a specific tag.

which checks if the filter is applicable and correct and if so, displays the debug string.

The user interface

As you can probably guess, the user interface is defined declaratively, using the MyXaml parser (the 2.0 beta version). Before I continue:

MyXaml is generic declarative instantiation engine. It's syntax looks similar to Microsoft's XAML primarily because both directly map XML elements to .NET classes and XML attributes to .NET properties. However, MyXaml is not an emulation of Microsoft's XAML. The two are distinctly different. For example, MyXaml's namespace mapping is different, and MyXaml does not support compound property syntax or implicit collections.

The UI definition consists of four parts:

the form

the menu

the docking manager definition

the dock window content templates

A complete discussion of the docking manager and the wrapper that I've written to support Weifen Luo's DockPanel Suite is going to be a separate article, as the article will also illustrate how the wrapper abstracts the docking manager and can be used also for other third party docking managers, such as Infragistic's. In this article, I will only briefly describe the declarative part. As with all declarative XML, it begins with an xmlns to .NET namespace map:

The menus

The menu object graph includes wiring up the event handlers. I'm using the MxMenu assembly here so that in the future I can add icons to the menus (the original implementation was written by Chris Becket). As with all MyXaml object graphs, this follows a "class-property-class" parent-child-grandchild format.

The DockingManager class manages two things--dock sites and dock windows. A DockWindow is docked to a particular site and the content for the window comes from a separate template file, referenced by the properties ContentFile and ContentName. With this information we can construct the initial form layout, which is done imperatively during initialization:

Content content=dockingManager.CreateDockWindow("fullOutput");

When a new debug message window is required, the imperative code copies the "newHandler" template and instantiates a new dock window:

Thus, the "newHandler" XML definition determines where the new debug message windows are initially positioned--in this case, in the document tab strip.

Dock window content templates

The templates, which are defined in a separate file, determine the content of the DockWindow instances. Therefore, if you want a different content, you not only define the content template but also an initial DockWindow instance in which that content is displayed. The two templates defined by default are:

The MyXaml 2.0 parser is "platform neutral", meaning that it doesn't include the System.Windows.Forms namespace, nor does it know about special things that ought to be done during form initialization. This is handled by the MyXaml.WinForms extender which hooks events in the parser. In particular, it implements handlers for the InstantiateBegin and InstantiateEnd methods, which in turn call SuspendLayout and ResumeLayout for any instances of Control type.

In the declarative portion, events were wired up to the "app" instance. The above code illustrates how the parser is told about the "app" instance.

Also, the parser can initialize fields with the instance instantiated during the declarative parsing. In the main application, there is one such field:

[MyXamlAutoInitialize] DockManager dockingManager=null;

The MyXamlAutoInitialize attribute tells the parser that only fields decorated with this attribute expect (and require) to be initialized.

Serialization

Traceract can save an existing layout and load it (although, this still seems a bit buggy). The layout is serialized to XML as well. For example, if you change the layout to something like this (shrunk so it fits in a screenshot):

The serialization of the form layout (stored in layout.xml) looks something like this:

You will note that a GUID is used to ensure that a unique name is created for new content windows.

Serialization/deserialization of the window content is complicated. Each docking manager I've worked with is very finicky about deserialization order and how it controls the tiling of the windows. As I've mentioned before, I'll be discussing this in greater depth in an article on docking managers in general.

Upcoming features

The following is a list of features I'm planning to work on as time permits and need requires. Feel free to let me know of other features you'd like.

improve the code!

replace the ListView with something more decent,

colorization,

better line wrap handling,

automatic positioning of all windows to adjacent debug messages when selecting a debug message,

save/restore application screen position and size,

automatic reloading of last configuration,

MRU list of configurations,

file selection for save/load of configurations,

automatic configuration selection through debug message string,

remote debugging support,

copy line/range to clipboard,

print,

additional configuration options, such as always output to main debug window, line wrap, etc.,

layout serialization bug fixes.

References

Terms and conditions

Traceract is intended to be used for your personal use. You can modify it, borrow from the code, etc., as much as you wish, as long as this is for your own personal use. Traceract or its derivatives cannot be distributed as a stand-alone commercial application or as part of a commercial application without my express permission. Traceract uses the GPL'd MyXaml assemblies. Including the source code and assemblies in the download here does not convey any rights to use the MyXaml assemblies in a commercial application.

Updates and source code

You can obtain the latest code releases, updated source code, and if you wish, contribute to the project by obtaining an account for my CVS server. Anyone interested in this should contact Marc Clifton and I will set you up with an account. I will also be updating the article download, but probably not as frequently.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

This is just the basic structure , and one trace function, but it demonstrates the ease of use it provides while still providing alot of info. The stack trace is probably a bit heavy, but u could use it exclusively in error/extreme conditions.

The second idea is to have 'live' object monitoring like WMI is meant to do, but suck so badly at. A few attributes on fields that allows you to take a snapshot of an object state, not mattering whether the fields are private, etc. Perhaps could be part of the previously discused interface.

Excellent ideas! Thanks again leppie! I'll have to see about implementing them, as I've worked before with trace levels, which I've found very useful. And object monitoring would be really neat! I especially like the idea of using attributes. There's some really interesting directions this could take.

Do you have to add a trance listener in the .config file? When I just use your Debug.WriteLine(...) lines nothing appears in Traceract (with Debug build selected). Doesn't work from an .aspx page either. Perhaps I missed something in your article.