logging-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From rpo...@apache.org
Subject svn commit: r1557551 - in /logging/log4j/log4j2/trunk: log4j-api/src/main/java/org/apache/logging/log4j/spi/ log4j-api/src/test/java/org/apache/logging/log4j/ log4j-api/src/test/java/org/apache/logging/log4j/spi/ src/changes/ src/site/xdoc/manual/
Date Sun, 12 Jan 2014 16:14:09 GMT
Author: rpopma
Date: Sun Jan 12 16:14:09 2014
New Revision: 1557551

URL: http://svn.apache.org/r1557551
Log:
LOG4J2-479: ThreadContext now uses plain ThreadLocal by default, can be configured to use
InheritableThreadLocal

Added:
    logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/ThreadContextInheritanceTest.java
  (with props)
Modified:
    logging/log4j/log4j2/trunk/log4j-api/src/main/java/org/apache/logging/log4j/spi/DefaultThreadContextMap.java
    logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/ThreadContextTest.java
    logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/spi/DefaultThreadContextMapTest.java
    logging/log4j/log4j2/trunk/src/changes/changes.xml
    logging/log4j/log4j2/trunk/src/site/xdoc/manual/thread-context.xml

Modified: logging/log4j/log4j2/trunk/log4j-api/src/main/java/org/apache/logging/log4j/spi/DefaultThreadContextMap.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-api/src/main/java/org/apache/logging/log4j/spi/DefaultThreadContextMap.java?rev=1557551&r1=1557550&r2=1557551&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/log4j-api/src/main/java/org/apache/logging/log4j/spi/DefaultThreadContextMap.java
(original)
+++ logging/log4j/log4j2/trunk/log4j-api/src/main/java/org/apache/logging/log4j/spi/DefaultThreadContextMap.java
Sun Jan 12 16:14:09 2014
@@ -20,6 +20,8 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 
+import org.apache.logging.log4j.util.PropertiesUtil;
+
 /**
  * The actual ThreadContext Map. A new ThreadContext Map is created each time it is updated
and the Map stored
  * is always immutable. This means the Map can be passed to other threads without concern
that it will be updated.
@@ -27,20 +29,37 @@ import java.util.Map;
  * the performance should be much better than if the Map was copied for each event.
  */
 public class DefaultThreadContextMap implements ThreadContextMap {
+    /** 
+     * Property name ({@value}) for selecting {@code InheritableThreadLocal} (value "true")
+     * or plain {@code ThreadLocal} (value is not "true") in the implementation.
+     */
+    public static final String INHERITABLE_MAP = "isThreadContextMapInheritable";
 
     private final boolean useMap;
-
-    private final ThreadLocal<Map<String, String>> localMap =
-        new InheritableThreadLocal<Map<String, String>>() {
-            @Override
-            protected Map<String, String> childValue(final Map<String, String>
parentValue) {
-                return parentValue == null || !useMap ? null :
-                    Collections.unmodifiableMap(new HashMap<String, String>(parentValue));
-            }
-        };
+    private final ThreadLocal<Map<String, String>> localMap;
 
     public DefaultThreadContextMap(final boolean useMap) {
         this.useMap = useMap;
+        this.localMap = createThreadLocalMap(useMap);
+    }
+    
+    // LOG4J2-479: by default, use a plain ThreadLocal, only use InheritableThreadLocal if
configured.
+    // (This method is package protected for JUnit tests.)
+    static ThreadLocal<Map<String, String>> createThreadLocalMap(final boolean
isMapEnabled) {
+        final PropertiesUtil managerProps = PropertiesUtil.getProperties();
+        final boolean inheritable = managerProps.getBooleanProperty(INHERITABLE_MAP);
+        if (inheritable) {
+            return new InheritableThreadLocal<Map<String, String>>() {
+                @Override
+                protected Map<String, String> childValue(final Map<String, String>
parentValue) {
+                    return (parentValue != null && isMapEnabled) //
+                            ? Collections.unmodifiableMap(new HashMap<String, String>(parentValue))
//
+                            : null;
+                }
+            };
+        }
+        // if not inheritable, return plain ThreadLocal with null as initial value
+        return new ThreadLocal<Map<String, String>>();
     }
 
     /**

Added: logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/ThreadContextInheritanceTest.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/ThreadContextInheritanceTest.java?rev=1557551&view=auto
==============================================================================
--- logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/ThreadContextInheritanceTest.java
(added)
+++ logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/ThreadContextInheritanceTest.java
Sun Jan 12 16:14:09 2014
@@ -0,0 +1,212 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j;
+
+import org.apache.logging.log4j.spi.DefaultThreadContextMap;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.util.Map;
+
+import static org.junit.Assert.*;
+
+/**
+ *
+ */
+public class ThreadContextInheritanceTest {
+    @BeforeClass
+    public static void setupClass() {
+        System.setProperty(DefaultThreadContextMap.INHERITABLE_MAP, "true");
+    }
+    
+    @AfterClass
+    public static void tearDownClass() {
+        System.clearProperty(DefaultThreadContextMap.INHERITABLE_MAP);
+    }
+
+    @Test
+    public void testPush() {
+        ThreadContext.push("Hello");
+        ThreadContext.push("{} is {}", ThreadContextInheritanceTest.class.getSimpleName(),
+                "running");
+        assertEquals("Incorrect parameterized stack value",
+                ThreadContext.pop(), "ThreadContextInheritanceTest is running");
+        assertEquals("Incorrect simple stack value", ThreadContext.pop(),
+                "Hello");
+    }
+
+    @Test
+    public void testInheritanceSwitchedOn() throws Exception {
+        System.setProperty(DefaultThreadContextMap.INHERITABLE_MAP, "true");
+        try {
+            ThreadContext.clear();
+            ThreadContext.put("Greeting", "Hello");
+            StringBuilder sb = new StringBuilder();
+            TestThread thread = new TestThread(sb);
+            thread.start();
+            thread.join();
+            String str = sb.toString();
+            assertTrue("Unexpected ThreadContext value. Expected Hello. Actual "
+                    + str, "Hello".equals(str));
+            sb = new StringBuilder();
+            thread = new TestThread(sb);
+            thread.start();
+            thread.join();
+            str = sb.toString();
+            assertTrue("Unexpected ThreadContext value. Expected Hello. Actual "
+                    + str, "Hello".equals(str));
+        } finally {
+            System.clearProperty(DefaultThreadContextMap.INHERITABLE_MAP);
+        }
+    }
+
+    @Test
+    public void perfTest() throws Exception {
+        ThreadContext.clear();
+        final Timer complete = new Timer("ThreadContextTest");
+        complete.start();
+        ThreadContext.put("Var1", "value 1");
+        ThreadContext.put("Var2", "value 2");
+        ThreadContext.put("Var3", "value 3");
+        ThreadContext.put("Var4", "value 4");
+        ThreadContext.put("Var5", "value 5");
+        ThreadContext.put("Var6", "value 6");
+        ThreadContext.put("Var7", "value 7");
+        ThreadContext.put("Var8", "value 8");
+        ThreadContext.put("Var9", "value 9");
+        ThreadContext.put("Var10", "value 10");
+        final int loopCount = 1000000;
+        final Timer timer = new Timer("ThreadContextCopy", loopCount);
+        timer.start();
+        for (int i = 0; i < loopCount; ++i) {
+            final Map<String, String> map = ThreadContext.getImmutableContext();
+            assertNotNull(map);
+        }
+        timer.stop();
+        complete.stop();
+        System.out.println(timer.toString());
+        System.out.println(complete.toString());
+    }
+
+    @Test
+    public void testGetContextReturnsEmptyMapIfEmpty() {
+        ThreadContext.clear();
+        assertTrue(ThreadContext.getContext().isEmpty());
+    }
+
+    @Test
+    public void testGetContextReturnsMutableCopy() {
+        ThreadContext.clear();
+        final Map<String, String> map1 = ThreadContext.getContext();
+        assertTrue(map1.isEmpty());
+        map1.put("K", "val"); // no error
+        assertEquals("val", map1.get("K"));
+
+        // adding to copy does not affect thread context map
+        assertTrue(ThreadContext.getContext().isEmpty());
+
+        ThreadContext.put("key", "val2");
+        final Map<String, String> map2 = ThreadContext.getContext();
+        assertEquals(1, map2.size());
+        assertEquals("val2", map2.get("key"));
+        map2.put("K", "val"); // no error
+        assertEquals("val", map2.get("K"));
+
+        // first copy is not affected
+        assertNotSame(map1, map2);
+        assertEquals(1, map1.size());
+    }
+
+    @Test
+    public void testGetImmutableContextReturnsEmptyMapIfEmpty() {
+        ThreadContext.clear();
+        assertTrue(ThreadContext.getImmutableContext().isEmpty());
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void testGetImmutableContextReturnsImmutableMapIfNonEmpty() {
+        ThreadContext.clear();
+        ThreadContext.put("key", "val");
+        final Map<String, String> immutable = ThreadContext.getImmutableContext();
+        immutable.put("otherkey", "otherval");
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void testGetImmutableContextReturnsImmutableMapIfEmpty() {
+        ThreadContext.clear();
+        final Map<String, String> immutable = ThreadContext.getImmutableContext();
+        immutable.put("otherkey", "otherval");
+    }
+
+    @Test
+    public void testGetImmutableStackReturnsEmptyStackIfEmpty() {
+        ThreadContext.clearStack();
+        assertTrue(ThreadContext.getImmutableStack().asList().isEmpty());
+    }
+
+    @Test
+    public void testPut() {
+        ThreadContext.clear();
+        assertNull(ThreadContext.get("testKey"));
+        ThreadContext.put("testKey", "testValue");
+        assertEquals("testValue", ThreadContext.get("testKey"));
+    }
+
+    @Test
+    public void testRemove() {
+        ThreadContext.clear();
+        assertNull(ThreadContext.get("testKey"));
+        ThreadContext.put("testKey", "testValue");
+        assertEquals("testValue", ThreadContext.get("testKey"));
+
+        ThreadContext.remove("testKey");
+        assertNull(ThreadContext.get("testKey"));
+        assertTrue(ThreadContext.isEmpty());
+    }
+
+    @Test
+    public void testContainsKey() {
+        ThreadContext.clear();
+        assertFalse(ThreadContext.containsKey("testKey"));
+        ThreadContext.put("testKey", "testValue");
+        assertTrue(ThreadContext.containsKey("testKey"));
+
+        ThreadContext.remove("testKey");
+        assertFalse(ThreadContext.containsKey("testKey"));
+    }
+
+    private class TestThread extends Thread {
+
+        private final StringBuilder sb;
+
+        public TestThread(final StringBuilder sb) {
+            this.sb = sb;
+        }
+
+        @Override
+        public void run() {
+            final String greeting = ThreadContext.get("Greeting");
+            if (greeting == null) {
+                sb.append("null");
+            } else {
+                sb.append(greeting);
+            }
+            ThreadContext.clear();
+        }
+    }
+}

Propchange: logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/ThreadContextInheritanceTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/ThreadContextTest.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/ThreadContextTest.java?rev=1557551&r1=1557550&r2=1557551&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/ThreadContextTest.java
(original)
+++ logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/ThreadContextTest.java
Sun Jan 12 16:14:09 2014
@@ -16,11 +16,11 @@
  */
 package org.apache.logging.log4j;
 
-import org.junit.Test;
+import static org.junit.Assert.*;
 
 import java.util.Map;
 
-import static org.junit.Assert.*;
+import org.junit.Test;
 
 /**
  *
@@ -39,7 +39,7 @@ public class ThreadContextTest {
     }
 
     @Test
-    public void testInheritance() throws Exception {
+    public void testInheritanceSwitchedOffByDefault() throws Exception {
         ThreadContext.clear();
         ThreadContext.put("Greeting", "Hello");
         StringBuilder sb = new StringBuilder();
@@ -47,15 +47,15 @@ public class ThreadContextTest {
         thread.start();
         thread.join();
         String str = sb.toString();
-        assertTrue("Unexpected ThreadContext value. Expected Hello. Actual "
-                + str, "Hello".equals(str));
+        assertTrue("Unexpected ThreadContext value. Expected null. Actual "
+                + str, "null".equals(str));
         sb = new StringBuilder();
         thread = new TestThread(sb);
         thread.start();
         thread.join();
         str = sb.toString();
-        assertTrue("Unexpected ThreadContext value. Expected Hello. Actual "
-                + str, "Hello".equals(str));
+        assertTrue("Unexpected ThreadContext value. Expected null. Actual "
+                + str, "null".equals(str));
     }
 
     @Test

Modified: logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/spi/DefaultThreadContextMapTest.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/spi/DefaultThreadContextMapTest.java?rev=1557551&r1=1557550&r2=1557551&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/spi/DefaultThreadContextMapTest.java
(original)
+++ logging/log4j/log4j2/trunk/log4j-api/src/test/java/org/apache/logging/log4j/spi/DefaultThreadContextMapTest.java
Sun Jan 12 16:14:09 2014
@@ -168,4 +168,22 @@ public class DefaultThreadContextMapTest
         map.put("key2", "value2");
         assertEquals("{key2=value2}", map.toString());
     }
+
+    @Test
+    public void testThreadLocalNotInheritableByDefault() {
+        System.clearProperty(DefaultThreadContextMap.INHERITABLE_MAP);
+        ThreadLocal<Map<String, String>> threadLocal = DefaultThreadContextMap.createThreadLocalMap(true);
+        assertFalse(threadLocal instanceof InheritableThreadLocal<?>);
+    }
+    
+    @Test
+    public void testThreadLocalInheritableIfConfigured() {
+        System.setProperty(DefaultThreadContextMap.INHERITABLE_MAP, "true");
+        try {
+            ThreadLocal<Map<String, String>> threadLocal = DefaultThreadContextMap.createThreadLocalMap(true);
+            assertTrue(threadLocal instanceof InheritableThreadLocal<?>);
+        } finally {
+            System.clearProperty(DefaultThreadContextMap.INHERITABLE_MAP);
+        }
+    }
 }

Modified: logging/log4j/log4j2/trunk/src/changes/changes.xml
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/src/changes/changes.xml?rev=1557551&r1=1557550&r2=1557551&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/src/changes/changes.xml (original)
+++ logging/log4j/log4j2/trunk/src/changes/changes.xml Sun Jan 12 16:14:09 2014
@@ -21,10 +21,14 @@
   </properties>
   <body>
     <release version="2.0-RC1" date="2013-MM-DD" description="Bug fixes and enhancements">
+      <action issue="LOG4J2-479" dev="rpopma" type="add" due-to="MK">
+        ThreadContext now uses plain ThreadLocal by default, unless system property
+        <tt>isThreadContextMapInheritable</tt> has value <tt>"true"</tt>.
+      </action>
       <action issue="LOG4J2-398" dev="rgoers" type="fix">
         Configure properties and setup Interpolator before processing rest of configuration.
       </action>
-      <action issue="LOG4J2-481" dev="rgoers" type="update" due-to="Matt Sicker">
+      <action issue="LOG4J2-481" dev="rgoers" type="add" due-to="Matt Sicker">
         Add Stream interface to Loggers.
       </action>
       <action issue="LOG4J2-490" dev="rgoers" type="update" due-to="Matt Sicker">

Modified: logging/log4j/log4j2/trunk/src/site/xdoc/manual/thread-context.xml
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/src/site/xdoc/manual/thread-context.xml?rev=1557551&r1=1557550&r2=1557551&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/src/site/xdoc/manual/thread-context.xml (original)
+++ logging/log4j/log4j2/trunk/src/site/xdoc/manual/thread-context.xml Sun Jan 12 16:14:09
2014
@@ -95,9 +95,12 @@ logger.debug("Message 2");
 .
 .
 ThreadContext.clear();</pre>
-          <p>The Stack and the Map are managed per thread and is based on
-            <a href="http://docs.oracle.com/javase/6/docs/api/java/lang/InheritableThreadLocal.html">InheritableThreadLocal</a>.
-            Thus, in many cases the contents of the Stack and Map will be passed to child
threads. However, as
+          <p>The Stack and the Map are managed per thread and are based on
+            <a href="http://docs.oracle.com/javase/6/docs/api/java/lang/ThreadLocal.html">ThreadLocal</a>
+            by default. The Map can be configured to use an
+            <a href="http://docs.oracle.com/javase/6/docs/api/java/lang/InheritableThreadLocal.html">InheritableThreadLocal</a>
+            by setting system property <tt>isThreadContextMapInheritable</tt>
to <tt>"true"</tt>.
+            When configured this way, the contents of the Map will be passed to child threads.
However, as
             discussed in the
             <a href="http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/Executors.html#privilegedThreadFactory()">Executors</a>
             class and in other cases where thread pooling is utilized, the ThreadContext
may not always be



Mime
View raw message