Hi All,
I have recently been looking at the NDC and MDC and wanted to run some
ideas passed you.
I am not too happy with the NDC and MDC classes for a couple of
different reasons.
The main issue is that the class names are not self documenting. The
names themselves are very similar and easy to confuse especially for
those not familiar with log4net/log4j. The acronyms are not very helpful
at explaining to users what they two different classes will do. The most
important concept that is missing from the name is that these are thread
local contexts.
Another reason is that the class names do not comply with the MS .NET
design guidelines on class names.
While I don't want to break backward compatibility I would like to
create a new recommended solution. What I have been working with so far
is a new class called ThreadContext that groups together the
functionality of the MDC and the NDC.
The MDC would forward to the ThreadContext.Properties. Some comparative
syntax:
MDC.Set("car", "volvo");
Becomes
ThreadContext.Properties["car"] = "volvo";
Whilst this syntax is more verbose I think that it is much clearer. Also
the use of the indexer accessor is similar to the dictionary
collections.
The NDC would forward to the ThreadContext.Stack. Some comparative
syntax:
using(NDC.Push("volvo"))
{
}
Becomes
using(ThreadContext.Stack.Push("volvo"))
{
}
There are some additional possibilities for the thread context stack. Is
one per thread enough? We can have named stacks.
using(ThreadContext.Stacks["MyCars"].Push("volvo"))
{
}
These could be stored in the ThreadContext.Properties. Then the same
PatternLayout pattern can be used to lookup a single named property or a
whole named stack. There may be an issue with name clashes between
Properties and Stacks.
The preferred way of using the NDC is to use the Push method with the
using keyword to automatically Pop the NDC stack frame at the end of the
scope. This ensures that the stack is consistent. The NDC has methods
that allow this usage pattern to be broken, specifically the
SetMaxDepth(depth) and Clear() methods. How are these methods typically
used? Is the NDC still used as a stack when these methods are used?
Perhaps we need another data structure. We can add a Set or List
structure that only allows messages to be Appended or Cleared. For
example:
ThreadContext.Lists["cars"].Append("volvo");
ThreadContext.Lists["cars"].Append("ford");
...
ThreadContext.Lists["cars"].Clear();
Again these lists could be stored in the ThreadContext.Properties
allowing them to use the same PatternLayout pattern as Properties and
Stacks.
If we were to add some form of List data structure then should we try to
limit the ThreadContext.Stacks to use the using keyword syntax (but
still working for languages other than C#), i.e. stack and scope based
semantics? By that I mean should we remove the Pop(), and Clear()
methods? I am already quite inclined to remove the SetMaxDepth() stuff.
We could get to a situation where there is only one method;
Push(string)->IDisposable. The IDisposable is used to cleanup the stack,
it will probably be a struct to remove a heap allocation. However
removing the Clear() method may make it more difficult to recover after
an error, if the using syntax is not followed then it is not possible to
cleanup the stack after an exception.
That is probably enough to be thinking about.
Any comments?
Nicko
------------
Nicko Cadell
log4net dev