Should a TraceSource be guarded with "if" statements?

483 views Asked by At

Before tracing to a TraceSource, should the "Trace Level" be checked prior to issuing the trace itself?

var ts = new TraceSource("foo");
ts.Switch.Level = SourceLevels.Warning;
if (/* should there be a guard here? and if so, what? */) {
    ts.TraceEvent(TraceEventType.Warning, 0, "bar");
}

While there is SourceSwitch.ShouldTrace(TraceEventType), the documentation indicates

Application code should not call this method; it is intended to be called only by methods in the TraceSource class.

It appears that pre-TraceSource model employed the TraceSwitch (not SourceSwitch) class which had various TraceXYZ methods (for this purpose?), but such appears to be not needed/used/mentioned with the TraceSource model.

(Having the guard outside the trace method affects evaluation of expressions used in/for the call - of course side-effects or computationally expensive operations in such are "bad" and "ill-advised", but I'd still like focus on the primary question.)

4

There are 4 answers

2
Derek On BEST ANSWER

I know that in NLog you generally just do the trace at whatever level you want and it will take care of whether or not the log level should be traced or not.

To me it looks like TraceSource works the same way.

So I would say "No" you probably shouldn't check.

Test it out by setting different trace levels and tracing messages at different levels and see what gets traced.

I think in terms of performance you are generally ok if you use the methods defined on the class:

Based on an example from: http://msdn.microsoft.com/en-us/library/sdzz33s6.aspx

This is good:

ts.TraceEvent(TraceEventType.Verbose, 3, "File {0} not found.", "test");

This would be bad:

string potentialErrorMessageToDisplay = string.Format( "File {0} not found.", "test" );
ts.TraceEvent(TraceEventType.Verbose, 3, potentialErrorMessageToDisplay );

In the first case the library probably avoids the call to string.Format if the error level won't be logged anyway. In the second case, string.Format is always called.

0
WiSeeker On

Like @nexuzzz suggests, there could be situations where calculation of event parameter is expensive. Here is what I could think of.

Suggestions to developers would be: "If you don't have string argument readily available, use the lambda version of TraceInformation or TraceWarning.

public class TraceSourceLogger : ILogger
{
    private TraceSource _traceSource;

    public TraceSourceLogger(object that)
    {
        _traceSource = new TraceSource(that.GetType().Namespace);
    }

    public void TraceInformation(string message)
    {
        _traceSource.TraceInformation(message);
    }

    public void TraceWarning(string message)
    {
        _traceSource.TraceEvent(TraceEventType.Warning, 1, message);
    }

    public void TraceError(Exception ex)
    {
        _traceSource.TraceEvent(TraceEventType.Error, 2, ex.Message);
        _traceSource.TraceData(TraceEventType.Error, 2, ex);
    }

    public void TraceInformation(Func<string> messageProvider)
    {
        if (_traceSource.Switch.ShouldTrace(TraceEventType.Information))
        {
            TraceInformation(messageProvider());
        }
    }

    public void TraceWarning(Func<string> messageProvider)
    {
        if (_traceSource.Switch.ShouldTrace(TraceEventType.Warning))
        {
            TraceWarning(messageProvider());
        }
    }
}
2
MatthewMartin On

Are strings you provide to the message argument expensive? A constant or literal is pretty cheap. If that is the case, don't worry about it, use the trace switch/trace listener filters, etc to reduce the amoount of trace processed (and the perf cost of trace) (BTW, the default trace listener is very expensive, always clear the trace listeners before adding the ones you want)

System.Diagnostics doesn't have anything to make a inactive TraceSource invocation costless. Even if you use the listener filters, or set the trace switch to zero (turn it off) the TraceEvent will be invoked and the message string will be constructed.

Imagine that the trace string is expensive to calculate, for example, it iterates across all the rows in a dataset and dumps them to a string. That could take a not trivial number of milliseconds.

To get around this you can make the string building part wrapped in a function that has a conditional attribute to turn it off in release mode, or use wrapper method that takes a lambda expression or a Func that creates the string (and isn't executed when not needed)

0
nexuzzz On

As per expensive trace parameters computation I came up with the following:

internal sealed class LazyToString
{
    private readonly Func<object> valueGetter;

    public LazyToString(Func<object> valueGetter)
    {
        this.valueGetter = valueGetter;
    }

    public override string ToString()
    {
        return this.valueGetter().ToString();
    }
}

The usage would be

traceSource.TraceEvent(TraceEventType.Verbose, 0, "output: {0}", new LazyToString(() =>
{
    // code here would be executed only when needed by TraceSource
    // so it can contain some expensive computations
    return "1";
}));

Any better idea?