On 23.01.2009, at 17:53, Ceki Gulcu wrote:
Joern Huxhorn wrote:
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.
It is indeed less efficient, and increasingly so as the stack size
grows. In the tests I have performed, for a stack size of 3, I got
the following results:
3.9 microseconds on average for direct calls to log4j's NDC with 3
pushes followed 3 pops
9.4 microseconds on average for NDC implemented via lMDC for 3
pushes followed 3 pops
For a stack size of 4,
direct log4j NDC: 5.6 microseconds on average for 4 push followed by
4 pop
indirectly via MDC: 17 microseconds on average for 4 push followed
by 4 pops
Under logback, the performance is very similar, 17 microseconds for
4 pushes followed by 4 pops.
So, the cost is clearly higher but not appalling so.
I haven't benchmarked this myself but the relatively small difference
could be caused by the log4j implementation which isn't optimal
either. It's using a Stack as the backend which extends Vector. Both
synchronize their methods which is absolutely unnecessary in our use
case because the instance is thread-local anyway. An ArrayList would
be suited much better.
Moreover, it kind of "pollutes" the MDC by mixing MDC and NDC
together.
Indeed. For example, MDC.clear() would also affect 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.
OK.
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. ;)
Thank you for clarifying this point.
Let me respond by observing that if you print the whole NDC stack on
each log, they will take a lot of space (on each line), eventually
causing eye sore.
I wouldn't output the NDC in the console or in log files but I would
show them in Lilith.
The table would only show the latest message while the details view
and the popup of the table cell would show the entire NDC.
If you think of it, NDC is just a thread local stack accessible by a
conversion pattern of the underlying logging system. In logback, it
is quite easy to register your own conversion pattern and conversion
words. Search for "Creating a custom conversion specifier" in http://logback.qos.ch/manual/layouts.html
Given that you are using logback, you could create your own NDC
implementation and associated conversion specifier. Once you have
done that, you could print (log) NDC data at convenient times, for
example every time the NDC data changes. (Your NDC code would use
its own logger.)
Come to think of it, you don't even need your own conversion
specifier. Your NDC implementation would simply convert the NDC
stack into a string and log that as a message at appropriate times,
for example when NDC changes.
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.
Although, it is true that there is a significant impact on
performance, depends on what you mean by *much* worse. Anyway, I
tend to agree with you.
[snip]
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.
I think that writing your own NDC implementation would be the way to
go in the use case you describe.
I'll probably do that because I'll implement support to keep the NDC
stack in my Lilith LoggingEvent anyway just to be able to import log4j
xml logs without loss of information.
If i do that it would mean that events logged using my multiplex-
appender would support my NDC while using the logback appender would
result in no NDC entries.
That's a bit unfortunate because the logback appender is better suited
for shortly running application like commandline tools or unit tests
while the multiplex-appender is better suited for long-running
applications like web-applications or server software. The reason for
that is that my appender is somewhat asynchronous using a buffer of n
events. This reduces blocking of the application in case of event
bursts now and then but does also mean that some events will get lost
if the application is shut down.
I'd feel much better if this could be implemented in SLF4J/Logback,
though.
Partly because I don't want to deviate too much from Logback (that was
never my intention but is already the case because I implemented http://bugzilla.slf4j.org/show_bug.cgi?id=70
and http://bugzilla.slf4j.org/show_bug.cgi?id=112 in my appender)
and partly because I think that you seriously underestimate the power
of this feature.
Beside my closure example that was only applicable (for the time
being) to people that use SLF4J in conjunction with Groovy, another
use case for the NDC would be dynamic weaving using an AOP around
advice [1].
An example would look like this:
===============================
@Aspect
public class AroundExample
{
@Around("com.xyz.myapp.SystemArchitecture.businessService()")
public Object pushOnNdc(ProceedingJoinPoint pjp) throws
Throwable
{
NDC.push("We are entering businessService!");
try
{
return pjp.proceed();
}
finally
{
NDC.pop();
}
}
}
===============================
The way described in [2] is even better but [1[ is better suited for a
simple example.
That way, the NDC use is configured at runtime. The classes that are
woven don't have to know anything about it.
You'd also have the ability to evaluate arguments given to the called
method, using them in the messagePattern in case of an implementation
like the one I suggested.
Again, like in the closure example, the cleanup would be automatic in
any case, whether an exception is thrown or not. I just point this out
- again - because it is something that is easily overlooked when
coding manually.
All in all, I think that the NDC is a feature that would be of general
use for the average developer.
Have a nice weekend,
Joern.
[1]
http://static.springframework.org/spring/docs/2.0.x/reference/aop.html#aop-ataspectj-around-advice
[2]
http://static.springframework.org/spring/docs/2.0.x/reference/aop.html#aop-schema-advice-around
Joern.
--
Ceki Gülcü
Logback: The reliable, generic, fast and flexible logging framework
for Java.
http://logback.qos.ch
_______________________________________________
user mailing list
user@slf4j.org
http://www.slf4j.org/mailman/listinfo/user
_______________________________________________
user mailing list
user@slf4j.org
http://www.slf4j.org/mailman/listinfo/user