View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
16   */
17  package org.apache.logging.log4j.spi;
18  
19  import org.apache.logging.log4j.LogManager;
20  import org.apache.logging.log4j.Logger;
21  import org.apache.logging.log4j.ThreadContext;
22  import org.apache.logging.log4j.status.StatusLogger;
23  import org.apache.logging.log4j.util.Constants;
24  import org.apache.logging.log4j.util.PropertiesUtil;
25  import org.apache.logging.log4j.util.ProviderUtil;
26  
27  /**
28   * Creates the ThreadContextMap instance used by the ThreadContext.
29   * <p>
30   * If {@link Constants#ENABLE_THREADLOCALS Log4j can use ThreadLocals}, a garbage-free StringMap-based context map can
31   * be installed by setting system property {@code log4j2.garbagefree.threadContextMap} to {@code true}.
32   * </p><p>
33   * Furthermore, any custom {@code ThreadContextMap} can be installed by setting system property
34   * {@code log4j2.threadContextMap} to the fully qualified class name of the class implementing the
35   * {@code ThreadContextMap} interface. (Also implement the {@code ReadOnlyThreadContextMap} interface if your custom
36   * {@code ThreadContextMap} implementation should be accessible to applications via the
37   * {@link ThreadContext#getThreadContextMap()} method.)
38   * </p><p>
39   * Instead of system properties, the above can also be specified in a properties file named
40   * {@code log4j2.component.properties} in the classpath.
41   * </p>
42   *
43   * @see ThreadContextMap
44   * @see ReadOnlyThreadContextMap
45   * @see org.apache.logging.log4j.ThreadContext
46   * @since 2.7
47   */
48  public final class ThreadContextMapFactory {
49      private static final Logger LOGGER = StatusLogger.getLogger();
50      private static final String THREAD_CONTEXT_KEY = "log4j2.threadContextMap";
51      private static final String GC_FREE_THREAD_CONTEXT_KEY = "log4j2.garbagefree.threadContextMap";
52      
53      private static boolean GcFreeThreadContextKey;
54      private static String ThreadContextMapName;
55  
56      static {
57          initPrivate();
58      }
59      
60      /**
61       * Initializes static variables based on system properties. Normally called when this class is initialized by the VM
62       * and when Log4j is reconfigured.
63       */
64      public static void init() {
65          CopyOnWriteSortedArrayThreadContextMap.init();
66          GarbageFreeSortedArrayThreadContextMap.init();
67          DefaultThreadContextMap.init();
68          initPrivate();
69      }
70  
71      /**
72       * Initializes static variables based on system properties. Normally called when this class is initialized by the VM
73       * and when Log4j is reconfigured.
74       */
75      private static void initPrivate() {
76          final PropertiesUtil properties = PropertiesUtil.getProperties();
77          ThreadContextMapName = properties.getStringProperty(THREAD_CONTEXT_KEY);
78          GcFreeThreadContextKey = properties.getBooleanProperty(GC_FREE_THREAD_CONTEXT_KEY);
79      }
80      
81      private ThreadContextMapFactory() {
82      }
83  
84      public static ThreadContextMap createThreadContextMap() {
85          final ClassLoader cl = ProviderUtil.findClassLoader();
86          ThreadContextMap result = null;
87          if (ThreadContextMapName != null) {
88              try {
89                  final Class<?> clazz = cl.loadClass(ThreadContextMapName);
90                  if (ThreadContextMap.class.isAssignableFrom(clazz)) {
91                      result = (ThreadContextMap) clazz.newInstance();
92                  }
93              } catch (final ClassNotFoundException cnfe) {
94                  LOGGER.error("Unable to locate configured ThreadContextMap {}", ThreadContextMapName);
95              } catch (final Exception ex) {
96                  LOGGER.error("Unable to create configured ThreadContextMap {}", ThreadContextMapName, ex);
97              }
98          }
99          if (result == null && ProviderUtil.hasProviders() && LogManager.getFactory() != null) { //LOG4J2-1658
100             final String factoryClassName = LogManager.getFactory().getClass().getName();
101             for (final Provider provider : ProviderUtil.getProviders()) {
102                 if (factoryClassName.equals(provider.getClassName())) {
103                     final Class<? extends ThreadContextMap> clazz = provider.loadThreadContextMap();
104                     if (clazz != null) {
105                         try {
106                             result = clazz.newInstance();
107                             break;
108                         } catch (final Exception e) {
109                             LOGGER.error("Unable to locate or load configured ThreadContextMap {}",
110                                     provider.getThreadContextMap(), e);
111                             result = createDefaultThreadContextMap();
112                         }
113                     }
114                 }
115             }
116         }
117         if (result == null) {
118             result = createDefaultThreadContextMap();
119         }
120         return result;
121     }
122 
123     private static ThreadContextMap createDefaultThreadContextMap() {
124         if (Constants.ENABLE_THREADLOCALS) {
125             if (GcFreeThreadContextKey) {
126                 return new GarbageFreeSortedArrayThreadContextMap();
127             }
128             return new CopyOnWriteSortedArrayThreadContextMap();
129         }
130         return new DefaultThreadContextMap(true);
131     }
132 }