/*
 * 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.core.config;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilderFactory;
import org.apache.logging.log4j.core.config.composite.CompositeConfiguration;
import org.apache.logging.log4j.core.config.plugins.util.PluginManager;
import org.apache.logging.log4j.core.config.plugins.util.PluginType;
import org.apache.logging.log4j.core.lookup.Interpolator;
import org.apache.logging.log4j.core.lookup.StrSubstitutor;
import org.apache.logging.log4j.core.net.UrlConnectionFactory;
import org.apache.logging.log4j.core.util.AuthorizationProvider;
import org.apache.logging.log4j.core.util.BasicAuthorizationProvider;
import org.apache.logging.log4j.core.util.FileUtils;
import org.apache.logging.log4j.core.util.Loader;
import org.apache.logging.log4j.core.util.NetUtils;
import org.apache.logging.log4j.core.util.ReflectionUtil;
import org.apache.logging.log4j.status.StatusLogger;
import org.apache.logging.log4j.util.LoaderUtil;
import org.apache.logging.log4j.util.PropertiesUtil;
import org.apache.logging.log4j.util.Strings;

Factory class for parsed Configuration objects from a configuration file. ConfigurationFactory allows the configuration implementation to be dynamically chosen in 1 of 3 ways:
  1. A system property named "log4j.configurationFactory" can be set with the name of the ConfigurationFactory to be used.
  2. setConfigurationFactory(ConfigurationFactory) can be called with the instance of the ConfigurationFactory to be used. This must be called before any other calls to Log4j.
  3. A ConfigurationFactory implementation can be added to the classpath and configured as a plugin in the ConfigurationFactory category. The Order annotation should be used to configure the factory to be the first one inspected. See XmlConfigurationFactory for an example.
If the ConfigurationFactory that was added returns null on a call to getConfiguration then any other ConfigurationFactories found as plugins will be called in their respective order. DefaultConfiguration is always called last if no configuration has been returned.
/** * Factory class for parsed {@link Configuration} objects from a configuration file. * ConfigurationFactory allows the configuration implementation to be * dynamically chosen in 1 of 3 ways: * <ol> * <li>A system property named "log4j.configurationFactory" can be set with the * name of the ConfigurationFactory to be used.</li> * <li> * {@linkplain #setConfigurationFactory(ConfigurationFactory)} can be called * with the instance of the ConfigurationFactory to be used. This must be called * before any other calls to Log4j.</li> * <li> * A ConfigurationFactory implementation can be added to the classpath and configured as a plugin in the * {@link #CATEGORY ConfigurationFactory} category. The {@link Order} annotation should be used to configure the * factory to be the first one inspected. See * {@linkplain org.apache.logging.log4j.core.config.xml.XmlConfigurationFactory} for an example.</li> * </ol> * * If the ConfigurationFactory that was added returns null on a call to * getConfiguration then any other ConfigurationFactories found as plugins will * be called in their respective order. DefaultConfiguration is always called * last if no configuration has been returned. */
public abstract class ConfigurationFactory extends ConfigurationBuilderFactory { public ConfigurationFactory() { super(); // TEMP For breakpoints }
Allows the ConfigurationFactory class to be specified as a system property.
/** * Allows the ConfigurationFactory class to be specified as a system property. */
public static final String CONFIGURATION_FACTORY_PROPERTY = "log4j.configurationFactory";
Allows the location of the configuration file to be specified as a system property.
/** * Allows the location of the configuration file to be specified as a system property. */
public static final String CONFIGURATION_FILE_PROPERTY = "log4j.configurationFile"; public static final String AUTHORIZATION_PROVIDER = "log4j2.authorizationProvider";
Plugin category used to inject a ConfigurationFactory Plugin class.
Since:2.1
/** * Plugin category used to inject a ConfigurationFactory {@link org.apache.logging.log4j.core.config.plugins.Plugin} * class. * * @since 2.1 */
public static final String CATEGORY = "ConfigurationFactory";
Allows subclasses access to the status logger without creating another instance.
/** * Allows subclasses access to the status logger without creating another instance. */
protected static final Logger LOGGER = StatusLogger.getLogger();
File name prefix for test configurations.
/** * File name prefix for test configurations. */
protected static final String TEST_PREFIX = "log4j2-test";
File name prefix for standard configurations.
/** * File name prefix for standard configurations. */
protected static final String DEFAULT_PREFIX = "log4j2";
The name of the classloader URI scheme.
/** * The name of the classloader URI scheme. */
private static final String CLASS_LOADER_SCHEME = "classloader";
The name of the classpath URI scheme, synonymous with the classloader URI scheme.
/** * The name of the classpath URI scheme, synonymous with the classloader URI scheme. */
private static final String CLASS_PATH_SCHEME = "classpath"; private static volatile List<ConfigurationFactory> factories = null; private static ConfigurationFactory configFactory = new Factory(); protected final StrSubstitutor substitutor = new StrSubstitutor(new Interpolator()); private static final Lock LOCK = new ReentrantLock(); private static final String HTTPS = "https"; private static final String HTTP = "http"; private static AuthorizationProvider authorizationProvider = null;
Returns the ConfigurationFactory.
Returns:the ConfigurationFactory.
/** * Returns the ConfigurationFactory. * @return the ConfigurationFactory. */
public static ConfigurationFactory getInstance() { // volatile works in Java 1.6+, so double-checked locking also works properly //noinspection DoubleCheckedLocking if (factories == null) { LOCK.lock(); try { if (factories == null) { final List<ConfigurationFactory> list = new ArrayList<>(); PropertiesUtil props = PropertiesUtil.getProperties(); final String factoryClass = props.getStringProperty(CONFIGURATION_FACTORY_PROPERTY); if (factoryClass != null) { addFactory(list, factoryClass); } final PluginManager manager = new PluginManager(CATEGORY); manager.collectPlugins(); final Map<String, PluginType<?>> plugins = manager.getPlugins(); final List<Class<? extends ConfigurationFactory>> ordered = new ArrayList<>(plugins.size()); for (final PluginType<?> type : plugins.values()) { try { ordered.add(type.getPluginClass().asSubclass(ConfigurationFactory.class)); } catch (final Exception ex) { LOGGER.warn("Unable to add class {}", type.getPluginClass(), ex); } } Collections.sort(ordered, OrderComparator.getInstance()); for (final Class<? extends ConfigurationFactory> clazz : ordered) { addFactory(list, clazz); } // see above comments about double-checked locking //noinspection NonThreadSafeLazyInitialization factories = Collections.unmodifiableList(list); final String authClass = props.getStringProperty(AUTHORIZATION_PROVIDER); if (authClass != null) { try { Object obj = LoaderUtil.newInstanceOf(authClass); if (obj instanceof AuthorizationProvider) { authorizationProvider = (AuthorizationProvider) obj; } else { LOGGER.warn("{} is not an AuthorizationProvider, using default", obj.getClass().getName()); } } catch (Exception ex) { LOGGER.warn("Unable to create {}, using default: {}", authClass, ex.getMessage()); } } if (authorizationProvider == null) { authorizationProvider = new BasicAuthorizationProvider(props); } } } finally { LOCK.unlock(); } } LOGGER.debug("Using configurationFactory {}", configFactory); return configFactory; } public static AuthorizationProvider getAuthorizationProvider() { return authorizationProvider; } private static void addFactory(final Collection<ConfigurationFactory> list, final String factoryClass) { try { addFactory(list, Loader.loadClass(factoryClass).asSubclass(ConfigurationFactory.class)); } catch (final Exception ex) { LOGGER.error("Unable to load class {}", factoryClass, ex); } } private static void addFactory(final Collection<ConfigurationFactory> list, final Class<? extends ConfigurationFactory> factoryClass) { try { list.add(ReflectionUtil.instantiate(factoryClass)); } catch (final Exception ex) { LOGGER.error("Unable to create instance of {}", factoryClass.getName(), ex); } }
Sets the configuration factory. This method is not intended for general use and may not be thread safe.
Params:
  • factory – the ConfigurationFactory.
/** * Sets the configuration factory. This method is not intended for general use and may not be thread safe. * @param factory the ConfigurationFactory. */
public static void setConfigurationFactory(final ConfigurationFactory factory) { configFactory = factory; }
Resets the ConfigurationFactory to the default. This method is not intended for general use and may not be thread safe.
/** * Resets the ConfigurationFactory to the default. This method is not intended for general use and may * not be thread safe. */
public static void resetConfigurationFactory() { configFactory = new Factory(); }
Removes the ConfigurationFactory. This method is not intended for general use and may not be thread safe.
Params:
  • factory – The factory to remove.
/** * Removes the ConfigurationFactory. This method is not intended for general use and may not be thread safe. * @param factory The factory to remove. */
public static void removeConfigurationFactory(final ConfigurationFactory factory) { if (configFactory == factory) { configFactory = new Factory(); } } protected abstract String[] getSupportedTypes(); protected boolean isActive() { return true; } public abstract Configuration getConfiguration(final LoggerContext loggerContext, ConfigurationSource source);
Returns the Configuration.
Params:
  • loggerContext – The logger context
  • name – The configuration name.
  • configLocation – The configuration location.
Returns:The Configuration.
/** * Returns the Configuration. * @param loggerContext The logger context * @param name The configuration name. * @param configLocation The configuration location. * @return The Configuration. */
public Configuration getConfiguration(final LoggerContext loggerContext, final String name, final URI configLocation) { if (!isActive()) { return null; } if (configLocation != null) { final ConfigurationSource source = ConfigurationSource.fromUri(configLocation); if (source != null) { return getConfiguration(loggerContext, source); } } return null; }
Returns the Configuration obtained using a given ClassLoader.
Params:
  • loggerContext – The logger context
  • name – The configuration name.
  • configLocation – A URI representing the location of the configuration.
  • loader – The default ClassLoader to use. If this is null, then the default ClassLoader will be used.
Returns:The Configuration.
/** * Returns the Configuration obtained using a given ClassLoader. * @param loggerContext The logger context * @param name The configuration name. * @param configLocation A URI representing the location of the configuration. * @param loader The default ClassLoader to use. If this is {@code null}, then the * {@linkplain LoaderUtil#getThreadContextClassLoader() default ClassLoader} will be used. * * @return The Configuration. */
public Configuration getConfiguration(final LoggerContext loggerContext, final String name, final URI configLocation, final ClassLoader loader) { if (!isActive()) { return null; } if (loader == null) { return getConfiguration(loggerContext, name, configLocation); } if (isClassLoaderUri(configLocation)) { final String path = extractClassLoaderUriPath(configLocation); final ConfigurationSource source = ConfigurationSource.fromResource(path, loader); if (source != null) { final Configuration configuration = getConfiguration(loggerContext, source); if (configuration != null) { return configuration; } } } return getConfiguration(loggerContext, name, configLocation); } static boolean isClassLoaderUri(final URI uri) { if (uri == null) { return false; } final String scheme = uri.getScheme(); return scheme == null || scheme.equals(CLASS_LOADER_SCHEME) || scheme.equals(CLASS_PATH_SCHEME); } static String extractClassLoaderUriPath(final URI uri) { return uri.getScheme() == null ? uri.getPath() : uri.getSchemeSpecificPart(); }
Loads the configuration from the location represented by the String.
Params:
  • config – The configuration location.
  • loader – The default ClassLoader to use.
Returns:The InputSource to use to read the configuration.
/** * Loads the configuration from the location represented by the String. * @param config The configuration location. * @param loader The default ClassLoader to use. * @return The InputSource to use to read the configuration. */
protected ConfigurationSource getInputFromString(final String config, final ClassLoader loader) { try { final URL url = new URL(config); URLConnection urlConnection = UrlConnectionFactory.createConnection(url); File file = FileUtils.fileFromUri(url.toURI()); if (file != null) { return new ConfigurationSource(urlConnection.getInputStream(), FileUtils.fileFromUri(url.toURI())); } else { return new ConfigurationSource(urlConnection.getInputStream(), url, urlConnection.getLastModified()); } } catch (final Exception ex) { final ConfigurationSource source = ConfigurationSource.fromResource(config, loader); if (source == null) { try { final File file = new File(config); return new ConfigurationSource(new FileInputStream(file), file); } catch (final FileNotFoundException fnfe) { // Ignore the exception LOGGER.catching(Level.DEBUG, fnfe); } } return source; } }
Default Factory.
/** * Default Factory. */
private static class Factory extends ConfigurationFactory { private static final String ALL_TYPES = "*";
Default Factory Constructor.
Params:
  • name – The configuration name.
  • configLocation – The configuration location.
Returns:The Configuration.
/** * Default Factory Constructor. * @param name The configuration name. * @param configLocation The configuration location. * @return The Configuration. */
@Override public Configuration getConfiguration(final LoggerContext loggerContext, final String name, final URI configLocation) { if (configLocation == null) { final String configLocationStr = this.substitutor.replace(PropertiesUtil.getProperties() .getStringProperty(CONFIGURATION_FILE_PROPERTY)); if (configLocationStr != null) { final String[] sources = configLocationStr.split(","); if (sources.length > 1) { final List<AbstractConfiguration> configs = new ArrayList<>(); for (final String sourceLocation : sources) { final Configuration config = getConfiguration(loggerContext, sourceLocation.trim()); if (config != null && config instanceof AbstractConfiguration) { configs.add((AbstractConfiguration) config); } else { LOGGER.error("Failed to created configuration at {}", sourceLocation); return null; } } return new CompositeConfiguration(configs); } return getConfiguration(loggerContext, configLocationStr); } for (final ConfigurationFactory factory : getFactories()) { final String[] types = factory.getSupportedTypes(); if (types != null) { for (final String type : types) { if (type.equals(ALL_TYPES)) { final Configuration config = factory.getConfiguration(loggerContext, name, configLocation); if (config != null) { return config; } } } } } } else { // configLocation != null final String configLocationStr = configLocation.toString(); for (final ConfigurationFactory factory : getFactories()) { final String[] types = factory.getSupportedTypes(); if (types != null) { for (final String type : types) { if (type.equals(ALL_TYPES) || configLocationStr.endsWith(type)) { final Configuration config = factory.getConfiguration(loggerContext, name, configLocation); if (config != null) { return config; } } } } } } Configuration config = getConfiguration(loggerContext, true, name); if (config == null) { config = getConfiguration(loggerContext, true, null); if (config == null) { config = getConfiguration(loggerContext, false, name); if (config == null) { config = getConfiguration(loggerContext, false, null); } } } if (config != null) { return config; } LOGGER.error("No Log4j 2 configuration file found. " + "Using default configuration (logging only errors to the console), " + "or user programmatically provided configurations. " + "Set system property 'log4j2.debug' " + "to show Log4j 2 internal initialization logging. " + "See https://logging.apache.org/log4j/2.x/manual/configuration.html for instructions on how to configure Log4j 2"); return new DefaultConfiguration(); } private Configuration getConfiguration(final LoggerContext loggerContext, final String configLocationStr) { ConfigurationSource source = null; try { source = ConfigurationSource.fromUri(NetUtils.toURI(configLocationStr)); } catch (final Exception ex) { // Ignore the error and try as a String. LOGGER.catching(Level.DEBUG, ex); } if (source == null) { final ClassLoader loader = LoaderUtil.getThreadContextClassLoader(); source = getInputFromString(configLocationStr, loader); } if (source != null) { for (final ConfigurationFactory factory : getFactories()) { final String[] types = factory.getSupportedTypes(); if (types != null) { for (final String type : types) { if (type.equals(ALL_TYPES) || configLocationStr.endsWith(type)) { final Configuration config = factory.getConfiguration(loggerContext, source); if (config != null) { return config; } } } } } } return null; } private Configuration getConfiguration(final LoggerContext loggerContext, final boolean isTest, final String name) { final boolean named = Strings.isNotEmpty(name); final ClassLoader loader = LoaderUtil.getThreadContextClassLoader(); for (final ConfigurationFactory factory : getFactories()) { String configName; final String prefix = isTest ? TEST_PREFIX : DEFAULT_PREFIX; final String [] types = factory.getSupportedTypes(); if (types == null) { continue; } for (final String suffix : types) { if (suffix.equals(ALL_TYPES)) { continue; } configName = named ? prefix + name + suffix : prefix + suffix; final ConfigurationSource source = ConfigurationSource.fromResource(configName, loader); if (source != null) { if (!factory.isActive()) { LOGGER.warn("Found configuration file {} for inactive ConfigurationFactory {}", configName, factory.getClass().getName()); } return factory.getConfiguration(loggerContext, source); } } } return null; } @Override public String[] getSupportedTypes() { return null; } @Override public Configuration getConfiguration(final LoggerContext loggerContext, final ConfigurationSource source) { if (source != null) { final String config = source.getLocation(); for (final ConfigurationFactory factory : getFactories()) { final String[] types = factory.getSupportedTypes(); if (types != null) { for (final String type : types) { if (type.equals(ALL_TYPES) || config != null && config.endsWith(type)) { final Configuration c = factory.getConfiguration(loggerContext, source); if (c != null) { LOGGER.debug("Loaded configuration from {}", source); return c; } LOGGER.error("Cannot determine the ConfigurationFactory to use for {}", config); return null; } } } } } LOGGER.error("Cannot process configuration, input source is null"); return null; } } static List<ConfigurationFactory> getFactories() { return factories; } }