[logback-dev] svn commit: r1583 - in logback/trunk: logback-classic/src/main/java/org/slf4j/impl logback-examples/src/main/java/chapter7 logback-site/src/site/pages logback-site/src/site/pages/manual
noreply.ceki at qos.ch
noreply.ceki at qos.ch
Wed Aug 29 23:59:52 CEST 2007
Author: ceki
Date: Wed Aug 29 23:59:52 2007
New Revision: 1583
Modified:
logback/trunk/logback-classic/src/main/java/org/slf4j/impl/LogbackMDCAdapter.java
logback/trunk/logback-examples/src/main/java/chapter7/SimpleMDC.java
logback/trunk/logback-examples/src/main/java/chapter7/UserServletFilter.java
logback/trunk/logback-site/src/site/pages/manual/mdc.html
logback/trunk/logback-site/src/site/pages/news.html
Log:
- MDC data in now inheritable by child threads. This fixes bug #64.
- updated MDC docs
Modified: logback/trunk/logback-classic/src/main/java/org/slf4j/impl/LogbackMDCAdapter.java
==============================================================================
--- logback/trunk/logback-classic/src/main/java/org/slf4j/impl/LogbackMDCAdapter.java (original)
+++ logback/trunk/logback-classic/src/main/java/org/slf4j/impl/LogbackMDCAdapter.java Wed Aug 29 23:59:52 2007
@@ -7,9 +7,9 @@
import org.slf4j.spi.MDCAdapter;
/**
- * A <em>Mapped Diagnostic Context</em>, or MDC in short, is an instrument for
- * distinguishing interleaved log output from different sources. Log output is
- * typically interleaved when a server handles multiple clients
+ * A <em>Mapped Diagnostic Context</em>, or MDC in short, is an instrument
+ * for distinguishing interleaved log output from different sources. Log output
+ * is typically interleaved when a server handles multiple clients
* near-simultaneously.
* <p>
* <b><em>The MDC is managed on a per thread basis</em></b>. A child thread
@@ -22,17 +22,17 @@
*
* @author Ceki Gülcü
*/
-public class LogbackMDCAdapter implements MDCAdapter {
-
- private final ThreadLocal<HashMap<String, String>> threadLocal = new ThreadLocal<HashMap<String, String>>();
+public class LogbackMDCAdapter implements MDCAdapter {
+
+ private final InheritableThreadLocal<HashMap<String, String>> inheritableThreadLocal = new InheritableThreadLocal<HashMap<String, String>>();
LogbackMDCAdapter() {
}
/**
* Put a context value (the <code>val</code> parameter) as identified with
- * the <code>key</code> parameter into the current thread's context map. Note
- * that contrary to log4j, the <code>val</code> parameter can be null.
+ * the <code>key</code> parameter into the current thread's context map.
+ * Note that contrary to log4j, the <code>val</code> parameter can be null.
*
* <p>
* If the current thread does not have a context map it is created as a side
@@ -40,25 +40,26 @@
*
* <p>
* Each time a value is added, a new instance of the map is created. This is
- * to be certain that the serialization process will operate on the updated map
- * and not send a reference to the old map, thus not allowing the remote logback
- * component to see the latest changes.
+ * to be certain that the serialization process will operate on the updated
+ * map and not send a reference to the old map, thus not allowing the remote
+ * logback component to see the latest changes.
*
- * @throws IllegalArgumentException in case the "key" parameter is null
+ * @throws IllegalArgumentException
+ * in case the "key" parameter is null
*/
public void put(String key, String val) throws IllegalArgumentException {
if (key == null) {
throw new IllegalArgumentException("key cannot be null");
}
-
- HashMap<String, String> oldMap = threadLocal.get();
+
+ HashMap<String, String> oldMap = inheritableThreadLocal.get();
HashMap<String, String> newMap = new HashMap<String, String>();
if (oldMap != null) {
- newMap.putAll(oldMap);
+ newMap.putAll(oldMap);
}
// the newMap replaces the old one for serialisation's sake
- threadLocal.set(newMap);
+ inheritableThreadLocal.set(newMap);
newMap.put(key, val);
}
@@ -69,7 +70,7 @@
* This method has no side effects.
*/
public String get(String key) {
- HashMap<String, String> hashMap = threadLocal.get();
+ HashMap<String, String> hashMap = inheritableThreadLocal.get();
if ((hashMap != null) && (key != null)) {
return hashMap.get(key);
@@ -83,19 +84,19 @@
*
* <p>
* Each time a value is removed, a new instance of the map is created. This is
- * to be certain that the serialization process will operate on the updated map
- * and not send a reference to the old map, thus not allowing the remote logback
- * component to see the latest changes.
+ * to be certain that the serialization process will operate on the updated
+ * map and not send a reference to the old map, thus not allowing the remote
+ * logback component to see the latest changes.
*/
public void remove(String key) {
- HashMap<String, String> oldMap = threadLocal.get();
+ HashMap<String, String> oldMap = inheritableThreadLocal.get();
HashMap<String, String> newMap = new HashMap<String, String>();
if (oldMap != null) {
- newMap.putAll(oldMap);
+ newMap.putAll(oldMap);
}
// the newMap replaces the old one for serialisation's sake
- threadLocal.set(newMap);
+ inheritableThreadLocal.set(newMap);
newMap.remove(key);
}
@@ -103,11 +104,11 @@
* Clear all entries in the MDC.
*/
public void clear() {
- HashMap<String, String> hashMap = threadLocal.get();
+ HashMap<String, String> hashMap = inheritableThreadLocal.get();
if (hashMap != null) {
hashMap.clear();
- threadLocal.remove();
+ inheritableThreadLocal.remove();
}
}
@@ -116,15 +117,15 @@
* internally.
*/
public Map<String, String> getPropertyMap() {
- return threadLocal.get();
+ return inheritableThreadLocal.get();
}
/**
- * Returns the keys in the MDC as a {@link Set}. The returned value
- * can be null.
+ * Returns the keys in the MDC as a {@link Set}. The returned value can be
+ * null.
*/
public Set<String> getKeys() {
- HashMap<String, String> hashMap = threadLocal.get();
+ HashMap<String, String> hashMap = inheritableThreadLocal.get();
if (hashMap != null) {
return hashMap.keySet();
Modified: logback/trunk/logback-examples/src/main/java/chapter7/SimpleMDC.java
==============================================================================
--- logback/trunk/logback-examples/src/main/java/chapter7/SimpleMDC.java (original)
+++ logback/trunk/logback-examples/src/main/java/chapter7/SimpleMDC.java Wed Aug 29 23:59:52 2007
@@ -10,10 +10,10 @@
package chapter7;
+import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
-import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.PatternLayout;
import ch.qos.logback.classic.spi.LoggingEvent;
import ch.qos.logback.core.ConsoleAppender;
@@ -31,11 +31,14 @@
ConsoleAppender<LoggingEvent> appender = new ConsoleAppender<LoggingEvent>();
appender.setLayout(layout);
appender.start();
- Logger root = (Logger)LoggerFactory.getLogger("root");
+ // cast root logger to c.q.logback.classic.Logger so that we can attach an
+ // appender to it
+ ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) LoggerFactory
+ .getLogger("root");
root.addAppender(appender);
-
- // get a logger
- Logger logger = (Logger)LoggerFactory.getLogger(SimpleMDC.class);
+
+ // get another logger
+ Logger logger = LoggerFactory.getLogger(SimpleMDC.class);
// We now put the last name
MDC.put("last", "Parker");
Modified: logback/trunk/logback-examples/src/main/java/chapter7/UserServletFilter.java
==============================================================================
--- logback/trunk/logback-examples/src/main/java/chapter7/UserServletFilter.java (original)
+++ logback/trunk/logback-examples/src/main/java/chapter7/UserServletFilter.java Wed Aug 29 23:59:52 2007
@@ -61,7 +61,8 @@
HttpServletRequest req = (HttpServletRequest) request;
Principal principal = req.getUserPrincipal();
-
+ // we could have also used a cookie to retreive the user name
+
if (principal != null) {
String username = principal.getName();
registerUsername(username);
Modified: logback/trunk/logback-site/src/site/pages/manual/mdc.html
==============================================================================
--- logback/trunk/logback-site/src/site/pages/manual/mdc.html (original)
+++ logback/trunk/logback-site/src/site/pages/manual/mdc.html Wed Aug 29 23:59:52 2007
@@ -82,10 +82,12 @@
lets the developer place information in a <em>diagnostic
context</em> that can be subsequently retrieved by certain
logback components. The <code>MDC</code> manages contextual
- information on a <em>per thread basis</em>. Typically, while
- starting to service a new client request, the developer will
- insert pertinent contextual information, such as the client id,
- client's IP address, request parameters etc. into the
+ information on a <em>per thread basis</em>. A child thread *
+ automatically inherits a <em>copy</em> of the mapped diagnostic
+ context of * its parent. Typically, while starting to service a
+ new client request, the developer will insert pertinent
+ contextual information, such as the client id, client's IP
+ address, request parameters etc. into the
<code>MDC</code>. Logback components, if appropriately
configured, will automatically include this information in each
log entry.
@@ -100,10 +102,10 @@
logback-examples/src/main/java/chapter7/SimpleMDC.java)</a></em>
<div class="source"><pre>package chapter7;
+import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
-import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.PatternLayout;
import ch.qos.logback.core.ConsoleAppender;
@@ -120,10 +122,14 @@
ConsoleAppender<LoggingEvent> appender = new ConsoleAppender<LoggingEvent>();
appender.setLayout(layout);
appender.start();
- Logger root = (Logger)LoggerFactory.getLogger("root");
- root.addAppender(appender);
- // get a logger
+ // cast root logger to c.q.logback.classic.Logger so that we can attach an
+ // appender to it
+ ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) LoggerFactory
+ .getLogger("root");
+ root.addAppender(appender);
+
+ // get another logger
Logger logger = (Logger)LoggerFactory.getLogger(SimpleMDC.class);
// We now put the last name
@@ -173,18 +179,21 @@
<h3>Advanced Use</h3>
<p>
- Mapped Diagnostic Contexts shine brightest within client server architectures.
- Typically, multiple clients will be served by multiple threads on the server.
- Although the methods in the <code>MDC</code> class are static,
- the diagnostic context is managed on a per thread basis, allowing each server
- thread to bear a distinct <code>MDC</code> stamp. <code>MDC</code> operations
- such as <code>put()</code> and <code>get()</code> affect the <code>MDC</code>
- of the <em>current</em> thread only. The <code>MDC</code> in other threads remain
- unaffected. Given that <code>MDC</code> information is managed on a
- per thread basis, each thread will have its own copy of the <code>MDC</code>.
- Thus, there is no need for the developer to worry about thread-safety or
- synchronization when programming with the <code>MDC</code> because
- it safely and transparently handles these issues.
+ Mapped Diagnostic Contexts shine brightest within client server
+ architectures. Typically, multiple clients will be served by
+ multiple threads on the server. Although the methods in the
+ <code>MDC</code> class are static, the diagnostic context is
+ managed on a per thread basis, allowing each server thread to
+ bear a distinct <code>MDC</code> stamp. <code>MDC</code>
+ operations such as <code>put()</code> and <code>get()</code>
+ affect only the <code>MDC</code> of the <em>current</em> thread,
+ and the children of the current thread. The <code>MDC</code> in
+ other threads remain unaffected. Given that <code>MDC</code>
+ information is managed on a per thread basis, each thread will
+ have its own copy of the <code>MDC</code>. Thus, there is no
+ need for the developer to worry about thread-safety or
+ synchronization when programming with the <code>MDC</code>
+ because it safely and transparently handles these issues.
</p>
<p>
@@ -485,36 +494,35 @@
<h3>Automating access to the <code>MDC</code></h3>
- <p>
- As we've seen, the <code>MDC</code> is very useful when dealing with multiple
- clients. In the case of a web application that manages user authentication, one
- simple solution could be to set the user's name in the <code>MDC</code> and remove
- it once the user logs out. Unfortunately, it is not always possible to achieve
- reliable results using this technique. Since <code>MDC</code> is managing
- information on a <em>per thread</em> basis, a server that recycles
- threads might lead to false information contained in the <code>MDC</code>.
- </p>
-
- <p>
- To allow the information contained in the <code>MDC</code> to be correct
- at all times when a request is processed, a solution might be to store the
- username at the beginning of the process, and remove it at the end of
- said process. A servlet
- <a href="http://java.sun.com/javaee/5/docs/api/javax/servlet/Filter.html">
- <code>Filter</code></a> is a good tool to have at
- hand in such case.
- </p>
-
- <p>
- By using a servlet filter, one can access the request, try to access
- to relevant user information and store it in the <code>MDC</code>.
- Then, after the process of creating the response, one just needs
- to remove the user information from the <code>MDC</code>.
+ <p>As we've seen, the <code>MDC</code> is very useful when dealing
+ with multiple clients. In the case of a web application that
+ manages user authentication, one simple solution could be to set
+ the user's name in the <code>MDC</code> and remove it once the
+ user logs out. Unfortunately, it is not always possible to
+ achieve reliable results using this technique. Since
+ <code>MDC</code> manages data on a <em>per thread</em> basis, a
+ server that recycles threads might lead to false information
+ contained in the <code>MDC</code>.
+ </p>
+
+ <p>To allow the information contained in the <code>MDC</code> to
+ be correct at all times when a request is processed, a possible
+ approach would be to store the username at the beginning of the
+ process, and remove it at the end of said process. A servlet <a
+ href="http://java.sun.com/javaee/5/docs/api/javax/servlet/Filter.html">
+ <code>Filter</code></a> comes in handy in this case.
+ </p>
+
+ <p>Within the servlet filter's <code>doFilter</code> method, we
+ can retreive the relevant user data through the request (or a
+ cookie threin), store it the <code>MDC</code>. Subsequent
+ processing by other filters and servlets will automatically
+ benefit from the MDC data that was stored previously. Finally,
+ when our servlet filter ragains control, we have an oppurtunity
+ to clean MDC data.
</p>
- <p>
- Here is an implementation of such a filter:
- </p>
+ <p>Here is an implementation of such a filter:</p>
<em>Example 7.5: User servlet filter (<a href="../xref/chapter7/UserServletFilter.html">
logback-examples/src/main/java/chapter7/UserServletFilter.java)</a></em>
@@ -547,7 +555,10 @@
FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
+
Principal principal = req.getUserPrincipal();
+ // Please note that we could have also used a cookie to
+ // retreive the user name
if (principal != null) {
String username = principal.getName();
@@ -587,17 +598,14 @@
If a user information is found, it is registered in the <code>MDC</code>.
</p>
- <p>
- Once the filter chain has completed, the filter removes the user information
- from the <code>MDC</code>.
- </p>
-
- <p>
- With this filter, the user information is present in the <code>MDC</code> only
- the time it takes to process the request. The thread may be reused to process a
- request for another user without risking to display false information in the
- logs.
+ <p>Once the filter chain has completed, the filter removes the user
+ information from the <code>MDC</code>.
</p>
+
+ <p>The approach we just outlined sets MDC data only for the duration
+ of the request and only for the thread processing it. Other threads
+ are unaffected. Furthermore, any given thread will contain correct
+ MDC data any point in time.</p>
<script src="../templates/footer.js"></script>
</div>
Modified: logback/trunk/logback-site/src/site/pages/news.html
==============================================================================
--- logback/trunk/logback-site/src/site/pages/news.html (original)
+++ logback/trunk/logback-site/src/site/pages/news.html Wed Aug 29 23:59:52 2007
@@ -26,6 +26,18 @@
href="http://www.qos.ch/mailman/listinfo/logback-announce">logback
announce</a> mailing list.</p>
+
+ <hr width="80%" align="center" />
+
+
+ <h3>XXth of September 2007 - Release of version 0.9.9</h3>
+
+ <p>MDC data in now inherited by child threads. This behaviour was
+ already specified in the javadocs. The issue was raised by Martin
+ Benda in <a href="http://bugzilla.qos.ch/show_bug.cgi?id=64">bug
+ 64</a> and independently by Peter Huber.
+ </p>
+
<hr width="80%" align="center" />
More information about the logback-dev
mailing list