Introduce DataBase,Asp.net,JavaScript,Xml,Html,Css,Sql,Php,ASP.NET Controls,AJAX,Tools,HTML,CSS,JavaScript,Open Source Project,WPF,.Net Framework,Linq
Top Recommended Hosting

Leverage .NET Runtime Diagnostics

by the3factory 5/5/2008 8:18:00 AM

Problems happen. They don't always happen in the lab, on machines you can easily debug. The problems you can't fix always seem to occur on one user's machine in the field, with no debugging environment and no way to figure out the cause. Experienced developers have learned to build in the capability to capture as much information as possible from systems running in the field. The .NET Framework includes a set of classes that you can use to generate diagnostics. These are configurable at runtime or compile time. If you leverage them, you can more quickly find problems that occur only in the field. Using code already in the framework, you can send diagnostic messages to a file, to the system logger, or to a debugging terminal. In addition, you can specify the level of debugging output that your program produces. You should use these features early in your development and make sure that you can produce the output you need to fix unanticipated problems in the field. Don't write your own diagnostic library until you understand what's already provided.

The System.Diagnostics.Debug, System.Diagnostics.Trace, and System.Diagnostics.EventLog classes provide all the tools you need to create diagnostic information from a running program. The first two classes have almost identical capabilities. The difference is that the trace class methods are controlled by the TRACE preprocessor symbol, and the Debug class methods are controlled by the DEBUG preprocessor symbol. When you create a project with VS .NET, the trACE symbol is defined for both release and debug builds, while the DEBUG symbol is defined only for debug builds. You create all your release build diagnostics using the TRace class. The EventLog class provides entry points so that your application can write to the system event log. The EventLog class does not support runtime configuration, but you can wrap it to conform to the same interface illustrated shortly.

You can also control the diagnostic output at runtime. The .NET Framework uses an application-configuration file to control a variety of runtime settings. This file is an XML document, located in the same directory as the main executable. The file shares the same name as the executable, with .config appended. For example, MyApplication.exe would be controlled by the MyApplication.exe.config XML document. All the configuration information is contained in a configuration node:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
</configuration>

The .NET Framework uses predefined keys to control the behavior of framework classes. In addition, you can define your own configuration keys and values.

You combine the TRace.WriteLineIf() method and traceSwitches to control the granularity of the output that your application generates. You turn off output by default so that you get the most performance possible out of your application. When you find problems, you can ratchet up the output to diagnose and correct any problems you find in the field. WriteLineIf() generates output only when an expression evaluates to true:

bool _printDiagnostics = true;
Trace.WriteLineIf( _printDiagnostics,
"Printing Diagnostics Today", "MySubSystem" );

You create traceSwitches to control the level of output. A traceSwitch is a variable set using the application-configuration file to one of five states: Off, Error, Warning, Info, and Verbose. These states are part of an enumeration and have values from 0 to 4. You can create a switch for each subsystem to control its messages. To create the switch, declare a variable of the traceSwitch class and construct it:

static private TraceSwitch librarySwitch = new
TraceSwitch( "MyAssembly",
"The switch for this assembly" );

The first parameter is the display name for the switch; the second parameter is the description. You set the value of the switch at runtime in the application configuration file. The following snippet sets the librarySwitch to Info:

<system.diagnostics>
<switches>
<add name="MyAssembly" value="3" />
</switches>
</system.diagnostics>

If you edit the config file's value of the switch, you modify the output generated by all statements controlled by that switch.

One more task: You need to configure where your trace output goes. By default, one listener is connected to the TRace class: a DefaultTraceListener object. The DefaultTraceListener sends messages to the debugger, and its Fail method (called when asserts fail) prints a diagnostic messages and terminates the program. In a production environment, you won't see any of the messages. You can configure a different listener in a production environment; you add listeners in the application configuration file. The following snippet adds a TextWriterTraceListener to your application:

<system.diagnostics>
<trace autoflush="true" indentsize="0">
<listeners>
<add name="MyListener"
type="System.Diagnostics.TextWriterTraceListener"
initializeData="MyListener.log"/>
</listeners>
</trace>
</system.diagnostics>

This TextWriterTraceListener prints all diagnostic information to the MyListener.log file. The name attribute specifies the name for the listener. The type specifies the type of object to create as a listener; it must be derived from System.Diagnostics.TraceListener. On those rare occasions when the standard listener classes in the .NET Framework are not enough for you, create your own listener class. The initializeData value is a string that gets passed to the object's constructor. TextWriterTraceListeners use this value for the filename.

You can extend these basics a bit to make it easier to create diagnostics for each assembly you distribute in your application. For each assembly you create, add a class to track the diagnostics generated by that assembly:

internal class MyAssemblyDiagnostics
{
static private TraceSwitch myAssemblySwitch =
new TraceSwitch( "MyAssembly",
"The switch for this assembly" );
internal static void Msg( TraceLevel l, object o )
{
Trace.WriteLineIf( myAssemblySwitch.Level >= l,
o, "MyAssembly" );
}
internal static void Msg( TraceLevel l, string s )
{
Trace.WriteLineIf( myAssemblySwitch.Level >= l,
s, "MyAssembly" );
}
// Add additional output methods to suit.
}

The MyAssemblyDiagnostices class creates diagnostic messages for the assembly, depending on a switch for that assembly. To generate a message, call either of the overloaded Msg routines:

public void Method1( )
{
MyAssemblyDiagnostics.Msg( TraceLevel.Info,
"Entering Method1." );
bool rVal = DoMoreWork( );
if( rVal == false )
{
MyAssemblyDiagnostics.Msg( TraceLevel.Warning,
"DoMoreWork Failed in Method1" );
}
MyAssemblyDiagnostics.Msg( TraceLevel.Info,
"Exiting Method1." );
}

You can also combine the assembly-specific switch with a global switch to control the entire application's output:

internal static void Msg( TraceLevel l, object o )
{
Trace.WriteLineIf ( librarySwitch.Level >= l ||
globalSwitch.Level >= l,
o, "MyLibrary" );
}
internal static void Msg( TraceLevel l, string s )
{
Trace.WriteLineIf( librarySwitch.Level >= l ||
globalSwitch.Level >= l,
s, "MyLibrary" );
}

This enables you to control application-wide diagnostics and more finely control an individual library's output. You can set the application-level diagnostics to the Error level to find errors anywhere in the application. When you have isolated the problem, you can raise the level of that one library's output to a higher level and find the exact source of the problem.

Diagnostic libraries are necessary to diagnose and maintain programs that have been distributed to the field. Don't write your own diagnostic library: The .NET FCL already has the core features you need. Use it to the fullest and then extend it for your own purposes, and you will capture all problems, even in production environments.

Tags:

C#

Related posts

Sign up for PayPal and start accepting credit card payments instantly.


Powered by BlogEngine.NET 1.2.0.0