12,066,952 members (58,790 online)
This article was written as a part of a presentation for Norwegian .NET User Group.
Some time ago in my article about custom .NET trace listeners I presented various methods to customize generation of .NET application trace output. Originally I planned to update that article to include more advanced customization, but I changed my mind due to several reasons:
Traceclass from being the best choice for production level tracing; because of these limitations developers should customize standard
Traceonly up to a certain level – above that level they should consider other options;
So instead of writing new fancy trace listeners, I decided to investigate the following questions:
This will of course be a matter of choice, although Microsoft has shown their
TRACE symbol is not defined for release build of
newly generated managed C++ projects, but release builds of C# projects have
defined. Looks confusing but I think it is reasonable: it is useful to have a
trace backdoor for final versions of your applications, and as long as there
are no active listeners (specified in the configuration file), there will be no
trace output. On the other hand, C++ language traditions does not have default
trace capabilities, so C++ developers accept that they should remember to
TRACE symbol for release builds of their
But there are different opinions on this matter. Richard Grimes in his book Developing Applications with Visual Studio.NET criticized the idea of enabling trace in release builds. He listed four arguments:
I must say I fully agree only with the first of these statements: if your application produces detailed output of its activities, it will always affect performance. On the other hand, this must be a conscious choice: low level routines that can be used within loops should probably limit its output to debug builds, but if you need an activity report from an application in production, you will just have to take performance cost.
I don't think trace messages add up so much to your binary size that you should consider removing them just for this reason. Modern technology trend does not care much about disk space consumption. Look at .NET with its XCOPY deployment! And how much of your application CD will be taken by trace messages? Just a small fraction.
The argument about custom trace listeners that can affect your application execution requires your attention: yes, adding trace functionality adds a new dimension to your application and sets certain requirements to trace consumers. But this is a general concern that you have to take when shipping your program to a customer: hardware, operating system service pack, device drivers – there is a lot of parameters that you can’t control but must consider. Again, I don’t think you should disable application trace just for this reason.
And finally, the argument that I most strongly oppose: leaving trace messages in the release build indicates that you have not fully tested your code. Such statement brings us back to the world of stand-alone applications running on stand-alone machines, where if a program fails, it must be that particular program’s responsibility. Modern computer programs aggregate so much of external functionality that in case of failure it’s not often possible to point a finger to a single module – system administrators must collect enough information to trace back a sequence of steps that led to an error.
So my personal choice is to enhance release versions of applications with trace
capabilities, either by defining
TRACE symbol during compilation,
or by using custom reporting facilities. Take some precautions to limit the
size of your trace output (one approach was demonstrated in my previous
article), but give your users an option to generate program activity report.
Traceclass a right choice?
Now that I have convinced you that you should have trace or some alternative
enabled in release builds (or at least I convinced myself one more time), let’s
see if existing .NET
Trace class can be used as a basis for trace
in a production environment. As with any .NET class, you can override default
trace functionality (by implementing custom listeners, not by subclassing
class that is sealed). However, there are two major design limitations of
Trace that may force you looking for alternatives. I
described them in my previous article, but let me summarize it here:
TraceListenerhave no way of defining message severity level – you can use conditional methods, but conditions that are tested using either
TraceSwitchare not forwarded to a listener, i.e. listeners are not able to categorize incoming messages by its
TraceSwitchlevel. As a result, trace output will not contain information about error level.
I consider these omissions to be a design flaw: leaving message severity levels
out of .NET trace implementation puts a serious limit on usability of Trace
class in production environment. I can think about only one reason why
was designed in such a way: internally
Trace class is almost a
Debug class – they share the same implementation and
differ only in preprocessor symbols and default listeners. I wish
had more on its own and was more extensible.
But before you decide that you will implement your own trace facilities (or use
the ones I’ll mention in the next section), remember that once you replace
Trace class, you no longer use unified trace output.
If your application consists of several components written by different
developers – or even vendors – you will risk ending up with several trace
outputs, generated by different trace engines.
So what criteria should be used to select trace implementation for release builds? The choice is of course yours, but I would consider the following:
Stream-based class; so in this case you should also try to stay with standard .NET
So what is a replacement for built-in .NET
Trace class in case you
need more sensitive control over trace output? It seems that Microsoft is aware
of current .NET trace limitations and is going to help us reasonably soon: next
version of Visual Studio.NET (code-named "Everett") will include Enterprise
Instrumentation Framework (EIF) that will fill the gaps and provide a lot of
new features. EIF will have support for configurable event filtering and
severity levels, so developers should be able to write the following code:
// Note that the syntax may change when EIF is released TraceMessageEvent.Raise("My trace message"); ErrorMessageEvent.Raise("My error", 1, "CODE"); // message, severity, error code
And of course SQL databases can be configured as trace event repository which makes EIF a good choice for audit purposes.
While Enterprise Instrumentation Framework meets your tomorrow demands, if you are more concerned about where you want to go today, you must look somewhere else. One nice alternative is a solution TrimaTrace developed by a Norwegian company Trimanet.It is much more powerful than standard .NET trace functionality, it can be used remotely via TCP and has very good performance (more than 30,000 messages per second locally on a Pentium III machine with 1 GHz CPU). TrimaTrace has unbeatable affordability, because it’s free.
After I uploaded the first edition of this article, I received a reference to another alternative - .NET implementation of the popular log4j Java API providing flexible and arbitrarily granular control over log management and configuration. This implementation is also free and is hosted on SourceForge by NeoWorks.
Every modern design decision is complex: all simple questions are already answered, and we are left with necessity to compare and compromise. Topics like debugging options and formatting trace output seem to be unimportant in the face of real problems we have to solve every day. However, don’t oversimplify them: they are parts of the foundation of your development environment, and it’s worth arranging them properly.