|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
A Guide to Controlling Trace Output
As a summary to the vital concepts you have been reading about, Table 8 collects reference links that you may need. Don't remember the difference between a TraceSwitch and a TraceSource? Check this table. Need to know how to define something in the configuration file? Check this table!
Table 8: Key .NET Diagnostic Concepts: This table provides a brief description, plus links on the left to passages in the .NET Framework Developers Guide, and references on the right to the formal documentation.
Interested in designing your own TraceListeners? Take a look at the source code for the AlignedTextWriterTraceListener referenced in this project, or the TextBoxTraceListener discussed in the next section. A couple other tutorials specifically on customization are also available: CLR Inside Out: Customized Tracing and Custom Trace Listener. Build a Trace Output Comparison Sandbox
All the radio buttons and check boxes are populated dynamically. The first two group boxes will probably not change much over time, but they may when a new release of the framework comes out. The third group box—the trace listeners—is populated based on the contents of the app.config file and/or any TraceListeners that you create programmatically. Each TraceListener gets a check box in the trace listener group, but its configuration will also be listed in the output area. This configuration reflects the initial configuration from the app.config file, plus any changes you have made programmatically before the form actually opens. Given the preliminaries you learned in the earlier parts of this article, the user interface of this tool should be reasonably evident. If you want to refresh yourself:
Working with the application is simple: select a source level, any number of trace options, and any number of trace listeners, then press Go. The application then exercises each of the common trace event types (see Table 2) for all TraceListeners you have selected. The output of each trace listener goes to a different destination, as shown in Figure 5. Note that output from the Console TraceListener will not be available unless you are running inside Visual Studio, because the application is a graphical rather than a console application. Table 9 shows the output from each TraceListener responding to the same trace events with the same output settings. You can reproduce equivalent results by selecting All for the source level; DateTime, ProcessId, and ThreadId for trace options (my preferred combination); and all the trace listeners listed. The more expansive formats have linked figures showing more complete samples. Note that there is no requirement for all TraceListeners to use either the same source level or the same output options; the TraceSourceGuiApp just happens to work that way for easier "apples to apples" comparisons. Table 9. TraceListener output samples: Thumbnail samples of all TraceListeners are shown here; refer to the individual figure references indicated for larger samples of the more expansive formats.
Most TraceListeners have a variety of constructors, but the configuration file does not support all of them—it supports only those that take string arguments. As the configuration sample shows, you pass the string arguments to the constructors in the initializeData attribute. Typically, this will be a file name. That means that you can't initialize a constructor that uses a Stream, a TextBox, or any other object, in the configuration file. That does not necessarily prevent you from instantiating all your listeners in the configuration file. You could define a listener here using some dummy string arguments, and then update it during your program initialization to configure it completely. Another new feature introduced here is the <filter> child attached to most of the listeners. It is not strictly necessary, because it's manipulated in the code, but including it here illustrates where it would be used. The combination of the source level of the switch and the source level of the filter determines the trace listener's ultimate output. The earlier discussion of the TraceSourceDemoApp application mentioned that you should generally use shared listeners rather than private listeners if you want seamless integration for your separate modules. I stated that shared listeners use the same instance of a listener. That is only partly true. (I suspect it is a bug and should have been completely true.) Using a shared listener lets all consumers write to a single file. That's true. Using a shared listener lets you set the TraceOutputOptions once and everyone gets access to them. That's true. But then there's the common indentation issue. When different TraceSources are in the same file, such as in this TraceSourceGuiApp, they have common indentation. When they are in different modules, such as in the TraceSourceDemoApp, they do not have common indentation. As I was designing my custom AlignedTextWriterTraceListener I discovered the indentation was corrupted, so I had to build in a fix to make the indentation global. Table 10 summarizes the differences between shared and local (or private) TraceListeners. Table 10: Shared vs. Local Listener Summary: The table summarizes the differences between shared and local listeners.
The configuration file connects to your program by the names of the <source> elements as you have seen. In the TraceSourceGuiApp constructor you see where these TraceSources are initialized with names corresponding to the configuration names. This application is again using StructuredTraceSources instead of TraceSources to take advantage of the extra program flow tracing methods they offer. The constructor also builds a list of its TraceSources so that when a user chooses the settings, they may be applied to both TraceSources. The final statement sets up the TraceListener that is used to channel output into the output pane of the application. This TraceListener is defined in InitializeTextBoxListening, specifying the TextBox it will send output to and a name to reference it. The second line programmatically turns the listener off via its Filter property, the programmatic corollary to how you saw it done in the configuration file. The final line adds this listener to both TraceSources—just as in the configuration file, all the other listeners were added to both TraceSources.
Pressing the Go button invokes the event handler shown below is invoked. This method first sets the source level for both TraceSources based on the user's selected settings. Next, it sets the TraceOutputOptions of each listener to the set of options the user chose. That method walks through the list of listeners, setting each one either on or off depending on the checked choices. The ExerciseTraces method simply invokes a series of TraceEvents. Finally, the method flushes the output so all listeners will write to their respective media for you to view.
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|