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 ThreadContextMapFactory() { 054 } 055 056 public static ThreadContextMap createThreadContextMap() { 057 final PropertiesUtil managerProps = PropertiesUtil.getProperties(); 058 final String threadContextMapName = managerProps.getStringProperty(THREAD_CONTEXT_KEY); 059 final ClassLoader cl = ProviderUtil.findClassLoader(); 060 ThreadContextMap result = null; 061 if (threadContextMapName != null) { 062 try { 063 final Class<?> clazz = cl.loadClass(threadContextMapName); 064 if (ThreadContextMap.class.isAssignableFrom(clazz)) { 065 result = (ThreadContextMap) clazz.newInstance(); 066 } 067 } catch (final ClassNotFoundException cnfe) { 068 LOGGER.error("Unable to locate configured ThreadContextMap {}", threadContextMapName); 069 } catch (final Exception ex) { 070 LOGGER.error("Unable to create configured ThreadContextMap {}", threadContextMapName, ex); 071 } 072 } 073 if (result == null && ProviderUtil.hasProviders() && LogManager.getFactory() != null) { //LOG4J2-1658 074 final String factoryClassName = LogManager.getFactory().getClass().getName(); 075 for (final Provider provider : ProviderUtil.getProviders()) { 076 if (factoryClassName.equals(provider.getClassName())) { 077 final Class<? extends ThreadContextMap> clazz = provider.loadThreadContextMap(); 078 if (clazz != null) { 079 try { 080 result = clazz.newInstance(); 081 break; 082 } catch (final Exception e) { 083 LOGGER.error("Unable to locate or load configured ThreadContextMap {}", 084 provider.getThreadContextMap(), e); 085 result = createDefaultThreadContextMap(); 086 } 087 } 088 } 089 } 090 } 091 if (result == null) { 092 result = createDefaultThreadContextMap(); 093 } 094 return result; 095 } 096 097 private static ThreadContextMap createDefaultThreadContextMap() { 098 if (Constants.ENABLE_THREADLOCALS) { 099 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}