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 ThreadContextMapFactory() {
54      }
55  
56      public static ThreadContextMap createThreadContextMap() {
57          final PropertiesUtil managerProps = PropertiesUtil.getProperties();
58          final String threadContextMapName = managerProps.getStringProperty(THREAD_CONTEXT_KEY);
59          final ClassLoader cl = ProviderUtil.findClassLoader();
60          ThreadContextMap result = null;
61          if (threadContextMapName != null) {
62              try {
63                  final Class<?> clazz = cl.loadClass(threadContextMapName);
64                  if (ThreadContextMap.class.isAssignableFrom(clazz)) {
65                      result = (ThreadContextMap) clazz.newInstance();
66                  }
67              } catch (final ClassNotFoundException cnfe) {
68                  LOGGER.error("Unable to locate configured ThreadContextMap {}", threadContextMapName);
69              } catch (final Exception ex) {
70                  LOGGER.error("Unable to create configured ThreadContextMap {}", threadContextMapName, ex);
71              }
72          }
73          if (result == null && ProviderUtil.hasProviders() && LogManager.getFactory() != null) { //LOG4J2-1658
74              final String factoryClassName = LogManager.getFactory().getClass().getName();
75              for (final Provider provider : ProviderUtil.getProviders()) {
76                  if (factoryClassName.equals(provider.getClassName())) {
77                      final Class<? extends ThreadContextMap> clazz = provider.loadThreadContextMap();
78                      if (clazz != null) {
79                          try {
80                              result = clazz.newInstance();
81                              break;
82                          } catch (final Exception e) {
83                              LOGGER.error("Unable to locate or load configured ThreadContextMap {}",
84                                      provider.getThreadContextMap(), e);
85                              result = createDefaultThreadContextMap();
86                          }
87                      }
88                  }
89              }
90          }
91          if (result == null) {
92              result = createDefaultThreadContextMap();
93          }
94          return result;
95      }
96  
97      private static ThreadContextMap createDefaultThreadContextMap() {
98          if (Constants.ENABLE_THREADLOCALS) {
99              if (PropertiesUtil.getProperties().getBooleanProperty(GC_FREE_THREAD_CONTEXT_KEY)) {
100                 return new GarbageFreeSortedArrayThreadContextMap();
101             }
102             return new CopyOnWriteSortedArrayThreadContextMap();
103         }
104         return new DefaultThreadContextMap(true);
105     }
106 }