To expand on the link which Sikorski put in the comments:
NDC
In NDC, the N stands for nested, meaning you control a single value with a Stack. You "push" a string, then you can "push" another whenever processing dictates; and when processing is complete you can pop it off to see the former value. This sort of contextual logging would be useful in some deeply-nested processing.
Setting the context string
NDC.push("processingLevel2");
log.info("success");
This will get output in your log where you have the %x
(lowercase) pattern:
log4j.appender.CA.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss.SSSS} %p %C %x = %m%n
MDC
The M stands for mapped, and this gives you a different type of control. Instead of using a single stack to control a single contextual string, you use name/value pairs. This is useful for tracking multiple contextual bits, such as putting username and IP into the log.
Setting the mapped values:
MDC.put("userIP", req.getRemoteAddr());
MDC.put("userName", foo.getName());
log.info("success");
Example MDC log4j pattern
Uppercase X with the example "userIP"
and "userName"
strings. These must match in your code and in the log4j config:
log4j.appender.CA.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss.SSSS} %p %X{userIP} %C %X{userName} = %m%n
I would combine these into one )context string*.
Similarities and Differences
In both cases, whenever you do the log.info("success");
, the output will have the additional context you have provided. This is much cleaner than concatenating strings like log.info(req.getRemoteAddr()) + " success");
every time you log.
Note that there are serious differences between the two with respect to threading and resources. In particular, NDC will keep handles to your threads which can affect the freeing of resources if you are not diligent about popping and clearing the NDC stack.
From the link: NDC use can lead to memory leaks if you do not periodically call the NDC.remove()
method.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…