Sunday, October 17, 2010

.Net Debug Class (2)

My debug class contains one base class. Let's see the structure of the base class.

DianosticsHelperBase Class

My debug class is a simple base class, DianosticsHelperBase:


The above class picture shows all the public properties and protected helper methods for derived classes. Let's discuss the design of the base class.

Layout Format of Debug Message

The main purpose of the debug class is to print or log messages. The message consists two key parts: timestamp and message, with additional optional parts: context, indent, and calculated value.

The context is used as a convenience to identify message's context, for example, a class or module name could be used to mark debugged messages within the context.

Indentation is a nice feature to layout debug messages. The indentation and un-indentation are done through two method calls. DebugIndent() and DebugUnindent(). The indentation string can be set by the static property InentString. Null or empty string can be set to IndentString to disable indentation, or other strings to customize an indentation string, such as "—". The default indent string is a string of two spaces:

private static string _indentStr = "  ";

The base class provides another nice feature to calculate a value between two points. This is done by providing delegates to method calls: DebugIndent() and DebugUnindent(). If null delegates are passed to the calls (parameters), no calculation will be donem, thus disable the calculated value feature.

The layout format of debug messages is set by the static property Format of the base class. The default layout is:

//indent, timestamp, calculatedValue, context, message
private static string _format = "{0}[ {1}{2} ] {3}::{4}";

Constructor

Typically, an instance of the debug class is a member of a class where debug messages will be logged by the instance within its internal codes. A context string for the instance is set through the base class CTOR. The context of the debug instance is set in its creation time.

private string _context;
private string _calculatedValue;

public DiagnosticsHelperBase(string context) {
if (context != null) {
_context = context;
}
else {
_context = "";
}
_calculatedValue = "";
}

Helper Methods

A message is logged or output by the protected method debugMessage. In this method, a message is formatted as a string and output to a media. In the following codes, the output is passed to Output console in the Microsoft Visual Studio.

private string _context;
private string _calculatedValue;
private static bool _logMessage = System.Diagnostics.Debugger.IsAttached;

protected DiagnosticsHelperBase<T> debugMessage(string message) {
if (_logMessage)
{
System.Diagnostics.Debug.Print(
string.Format(_format, getIndent(), DateTime.Now,
_calculatedValue, _context, message));
_calculatedValue = "";
}
return this;
}

private string getIndent() {
string sVal = "";
if (_indent > 0)
{
int i = 0;
do
{
sVal = string.Format("{0}{1}", sVal, _indentStr);
i++;
} while (i < _indent);
}
return sVal;
}

The method checks _logMessage before further logging messages. This flag is set by Syste.Diagnostics.Debugger.IsAttached. If the build is in release mode, this flag will be False, as a result, no calls to log messages.

The concept of a calculated value is based on the consideration of calculating a value between two debug points, for example, a duration time value between the beginning and the end of a method call. This is done within the base class by two method calls: debugIndent() and debugUnindent(). How a value is obtained and how the calculated value is done are provided by passing delegates as parameters to those method calls. This provides a flexible way to generate calculated values such as duration, memory usage, disk free spaces and so on.

protected DiagnosticsHelperBase<T> debugIndent(GetValue<T> pushedValueDelegate) {
if (_logMessage)
{
if (pushedValueDelegate != null)
{
T value = pushedValueDelegate();
_debugDts.Push(value);
}
_calculatedValue = "";
_indent++;
}
return this;
}

protected DiagnosticsHelperBase<T> debugUnindent(GetValue<T> fromValueDelegate, GetCalculatedValue<T> calculatedValueDelegate) {
if (_logMessage)
{
if (calculatedValueDelegate != null && fromValueDelegate != null)
{
T value2 = _debugDts.Pop();
T value1 = fromValueDelegate();
_calculatedValue = calculatedValueDelegate(value1, value2);
}
if (_indent > 0)
{
_indent--;
}
}
return this;
}

All the methods in the base class are protected. They are only available for a derived class to implement specific debug usages. For example, the following duration helper class is an example for logging messages with duration information.

0 comments: