001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache license, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the license for the specific language governing permissions and 015 * limitations under the license. 016 */ 017package org.apache.logging.log4j.spi; 018 019import org.apache.logging.log4j.LogManager; 020import org.apache.logging.log4j.Logger; 021import org.apache.logging.log4j.ThreadContext; 022import org.apache.logging.log4j.status.StatusLogger; 023import org.apache.logging.log4j.util.Constants; 024import org.apache.logging.log4j.util.PropertiesUtil; 025import org.apache.logging.log4j.util.ProviderUtil; 026 027/** 028 * Creates the ThreadContextMap instance used by the ThreadContext. 029 * <p> 030 * If {@link Constants#ENABLE_THREADLOCALS Log4j can use ThreadLocals}, a garbage-free StringMap-based context map can 031 * be installed by setting system property {@code log4j2.garbagefree.threadContextMap} to {@code true}. 032 * </p><p> 033 * Furthermore, any custom {@code ThreadContextMap} can be installed by setting system property 034 * {@code log4j2.threadContextMap} to the fully qualified class name of the class implementing the 035 * {@code ThreadContextMap} interface. (Also implement the {@code ReadOnlyThreadContextMap} interface if your custom 036 * {@code ThreadContextMap} implementation should be accessible to applications via the 037 * {@link ThreadContext#getThreadContextMap()} method.) 038 * </p><p> 039 * Instead of system properties, the above can also be specified in a properties file named 040 * {@code log4j2.component.properties} in the classpath. 041 * </p> 042 * 043 * @see ThreadContextMap 044 * @see ReadOnlyThreadContextMap 045 * @see org.apache.logging.log4j.ThreadContext 046 * @since 2.7 047 */ 048public final class ThreadContextMapFactory { 049 private static final Logger LOGGER = StatusLogger.getLogger(); 050 private static final String THREAD_CONTEXT_KEY = "log4j2.threadContextMap"; 051 private static final String GC_FREE_THREAD_CONTEXT_KEY = "log4j2.garbagefree.threadContextMap"; 052 053 private static boolean GcFreeThreadContextKey; 054 private static String ThreadContextMapName; 055 056 static { 057 initPrivate(); 058 } 059 060 /** 061 * Initializes static variables based on system properties. Normally called when this class is initialized by the VM 062 * and when Log4j is reconfigured. 063 */ 064 public static void init() { 065 CopyOnWriteSortedArrayThreadContextMap.init(); 066 GarbageFreeSortedArrayThreadContextMap.init(); 067 DefaultThreadContextMap.init(); 068 initPrivate(); 069 } 070 071 /** 072 * Initializes static variables based on system properties. Normally called when this class is initialized by the VM 073 * and when Log4j is reconfigured. 074 */ 075 private static void initPrivate() { 076 final PropertiesUtil properties = PropertiesUtil.getProperties(); 077 ThreadContextMapName = properties.getStringProperty(THREAD_CONTEXT_KEY); 078 GcFreeThreadContextKey = properties.getBooleanProperty(GC_FREE_THREAD_CONTEXT_KEY); 079 } 080 081 private ThreadContextMapFactory() { 082 } 083 084 public static ThreadContextMap createThreadContextMap() { 085 final ClassLoader cl = ProviderUtil.findClassLoader(); 086 ThreadContextMap result = null; 087 if (ThreadContextMapName != null) { 088 try { 089 final Class<?> clazz = cl.loadClass(ThreadContextMapName); 090 if (ThreadContextMap.class.isAssignableFrom(clazz)) { 091 result = (ThreadContextMap) clazz.newInstance(); 092 } 093 } catch (final ClassNotFoundException cnfe) { 094 LOGGER.error("Unable to locate configured ThreadContextMap {}", ThreadContextMapName); 095 } catch (final Exception ex) { 096 LOGGER.error("Unable to create configured ThreadContextMap {}", ThreadContextMapName, ex); 097 } 098 } 099 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}