[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&uuml;lc&uuml;
  */
-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&lt;LoggingEvent> appender = new ConsoleAppender&lt;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