[slf4j-user] Why is there no NDC?

Joern Huxhorn jhuxhorn at googlemail.com
Fri Jan 23 15:42:46 CET 2009


Ceki Gulcu wrote:
> Hello Joern,
>
> Did you know that NDC can be implemented on top of MDC? I just added
> such an implementation into slf4j-ext. You can view it at:
>
> http://svn.slf4j.org/viewvc?view=rev&revision=1268
>
I'd implement NDC by using MDC the same way but it's obviously much less
efficient than using a dedicated Stack (or ArrayList ) like in the log4j
NDC.
Moreover, it kind of "pollutes" the MDC by mixing MDC and NDC together.
> I hear that you when you sat that you would like to see NDC introduced
> in SLF4J but I still don't really see the added value of NDC when MDC
> is avaliable.
>
> The blog about NDC by "mike" does not really explain why NDC is
> better. It explains that the NDC API is different and that his team
> would like to stay with an API they already use, namely NDC.
Well, I don't use NDC but I'd really like to :)
It's not about which one is better but about which one is more suitable
for a certain use case.
They supplement each other. The NDC is something like a contextual stack
trace while the MDC is a snapshot of some of the application data.

In our application, for example, we use the MDC to store the username,
thee URL, the request-parameters and some other state information.

We write a proprietary CMS system where content can include other
content and each of these have certain dynamic parts which need to be
resolved. Therefore one use case for NDC would be to keep the nesting of
the contents as well as informations about the various dynamic resolvers
that could, again, load other contents..

An actual NDC would look something like this:
Loading content A
Loading content B
Resolving dynamic values for X
Loading content C

which would be of great use for us. Really. ;)

For example, if the loading of C failed we'd have the additional
information that it could be related to the resolving of X that was done
before.
Using the NDC like this would also mean that we would push and pop quite
often. Therefore the implementation of size(), which is called both in
push and pop, would have a significant performance impact und would
certainly perform *much* worse than just adding/removing to/from a
thread-local List.
>
> As for your examples with closures, other than showing that closures
> are cool, they do not explain why NDC is required when MDC is
> available. Can you think of an explanation without closures
> demonstrating the advantages of NDC?
I just used the closures example because I guessed - in my original mail
- that NDC was probably removed/not implemented because it could create
problems if the popping isn't done properly, e.g. in case of an
Exception or just out of mistake.
But that wasn't the case so I just wanted to clarify what I meant with
my pseudo code because you asked.
>
> > While I *can* live without an NDC I would definitely use it if it was
> > available in SLF4J.
>
> I am interested. How you would use NDC more efficiently than MDC, if
> NDC was available in SLF4J?
As I wrote above, a dedicated NDC is much more efficient than an NDC
implemented on top of the MDC - simply because of the data structures
used. A Map just isn't very suitable to use as a List.

I also suggested an NDC with lazy message formatting similar to logback
logging messages. This would make a big difference if there was a place
to globally disable NDC evaluation altogether or if NDC wasn't disabled
but isn't used in any appender.

Joern.
>
>
> Cheers,
>
> [1] http://www.flester.com/blog/2008/02/03/slf4j-log4j-and-ndc
>
> Joern Huxhorn wrote:
>> Hi Ceki,
>>
>> I guess that a piece of actual code is the easiest way to explain
>> what I mean.
>>
>> The following is valid Groovy code, using MDC as an example:
>> ====================================
>> import org.slf4j.MDC;
>>
>> def withValue =
>> {
>>         String key, String value, Closure operation ->
>>
>>         MDC.put(key, value)
>>         try
>>         {
>>                 operation()
>>         }
>>         finally
>>         {
>>                 MDC.remove(key)
>>         }
>> }
>>
>> withValue("foo","bar")
>> {
>>         // MDC contains the key "foo" with the value "bar"
>>         assert MDC.get("foo") == "bar"
>> }
>>
>> assert !MDC.get("foo") // MDC does not contain the key "foo" anymore
>> ====================================
>>
>> This means that the MDC will contain the given key, value for the
>> duration of the operation in the following scope, ensuring to
>> *always* remove the value again, even in case of an exception.
>> Another use-case would be to get the old value before putting the new
>> one and restoring it instead of just removing.
>> Those closures, while syntactically a little awkward, are extremely
>> powerful.
>>
>> So what I meant in my previous mail would be something like this (and
>> this time I didn't check it for correctness, obviously:
>> ====================================
>> import org.slf4j.NDC;
>>
>> def withNdc =
>> {
>>         String messagePattern, Object[] args, Closure operation ->
>>
>>            NDC.push(messagePattern, params)
>>            try
>>         {
>>                 operation()
>>         }
>>         finally
>>         {
>>                    NDC.pop()
>>         }
>> }
>>
>> withNdc("Parameter is {}", ["bar"])
>> {
>>         // NDC has the message "Parameter is bar." pushed. In case of
>> Logback, I would postpone the actual formatting until the formatted
>> message is really needed/used by an appender (like in LoggingEvent)...
>> }
>> // NDC has been popped.
>> ====================================
>>
>> which would mean automatic NDC cleanup. Beside Groovy which is
>> gaining quite some momentum at the moment, Java will - probably -
>> also get them. See http://www.javac.info/
>>
>> To me, NDC and MDC are completely different things. While the MDC
>> gives informations about certain states of the application like the
>> user that is executing the code, the NDC is more like a manual
>> StackTrace, adding stacked state informations about the context. The
>> feature of nesting would be hard to implement using MDC, e.g. in case
>> of recursion.
>>
>> While I *can* live without an NDC I would definitely use it if it was
>> available in SLF4J.
>
>> I missed out on both MDC and NDC because I previously used
>> commons.logging but now, with SLF4J, I'm using MDC all the time. It
>> was just harder or, let's say, impossible from the maintainability
>> perspective, to log the same infos that I now obtain using the MDC.
>>
>> Beside that, it seems that the absence of NDC in SLF4J/Logback is a
>> pain-point for some people that would like to switch over from log4j
>> to slf4j/logback. I found
>> http://www.flester.com/blog/2008/02/03/slf4j-log4j-and-ndc when I was
>> searching for a reason why there's no NDC. Yes, I've seen that you
>> know the link ;)
>>
>> In contrast to log4j, I would suggest that peek should be left out to
>> prevent misuse of the NDC in application logic. While it would still
>> be possible using the cloneStack method it's way more obvious that
>> this would be a bad idea, imo.
>> Additionally, I'd suggest to implement message formatting like in the
>> log statements because I guess that NDC could be disabled globally by
>> configuration to save memory and have the ability to achieve better
>> performance on demand.
>>
>> I think it would be relatively easy to implement an SLF4J NDC in a
>> similar way like MDC.
>> What do you think about that?
>>
>> Regards, Joern.
>>
>> On 22.01.2009, at 12:04, Ceki Gulcu wrote:
>>
>>>
>>> Hi Joern,
>>>
>>> You are not beating a dead horse. There is no NDC in SLF4J because
>>> it is thought that the MDC offers a better more general solution
>>> than the NDC.
>>>
>>> However, I don't understand what you mean when you write:
>>> slf4j.NDC("Parameter is {}.", obj)
>>>
>>> What does the NDC offer that the MDC does not? I mean from a users
>>> point of view.
>>>
>>> Cheers,
>>>
>>> Joern Huxhorn wrote:
>>>> Hi.
>>>> I hope I'm not beating a dead horse but I just somewhat
>>>> rediscovered the log4j NDC class (I was using commons.logging
>>>> before switching to slf4j) and I was wondering why there is no NDC
>>>> class defined in slf4j.
>>>> While the original NDC class could be improved it seems like a
>>>> quite nice addition to the MDC. I guess it would get especially
>>>> interesting with closures where you could write something like
>>>> (pseudo-code)
>>>> slf4j.NDC("Parameter is {}.", obj)
>>>> {
>>>>    [the nested code]
>>>> }
>>>> which would remove the necessity to pop the stack manually.
>>>> But even without closures an NDC implementation utilizing
>>>> parameters seems really worthwhile to me.
>>>> Just wondering,
>>>> Joern.
>>>
>>>
>>> -- 
>>> Ceki Gülcü
>>> Logback: The reliable, generic, fast and flexible logging framework
>>> for Java.
>>> http://logback.qos.ch
>>> _______________________________________________
>>> user mailing list
>>> user at slf4j.org
>>> http://www.slf4j.org/mailman/listinfo/user
>>
>> _______________________________________________
>> user mailing list
>> user at slf4j.org
>> http://www.slf4j.org/mailman/listinfo/user
>>
>




More information about the slf4j-user mailing list