| /* |
| * 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.log4j; |
| |
| import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.InterruptedIOException; |
| import java.net.URL; |
| import java.net.URLConnection; |
| import java.nio.file.Files; |
| import java.nio.file.Paths; |
| import java.util.Enumeration; |
| import java.util.Hashtable; |
| import java.util.Map; |
| import java.util.Properties; |
| import java.util.StringTokenizer; |
| import java.util.Vector; |
| import org.apache.log4j.bridge.FilterAdapter; |
| import org.apache.log4j.config.Log4j1Configuration; |
| import org.apache.log4j.config.PropertiesConfiguration; |
| import org.apache.log4j.config.PropertySetter; |
| import org.apache.log4j.helpers.FileWatchdog; |
| import org.apache.log4j.helpers.LogLog; |
| import org.apache.log4j.helpers.OptionConverter; |
| import org.apache.log4j.or.RendererMap; |
| import org.apache.log4j.spi.Configurator; |
| import org.apache.log4j.spi.ErrorHandler; |
| import org.apache.log4j.spi.Filter; |
| import org.apache.log4j.spi.LoggerFactory; |
| import org.apache.log4j.spi.LoggerRepository; |
| import org.apache.log4j.spi.OptionHandler; |
| import org.apache.log4j.spi.RendererSupport; |
| import org.apache.log4j.spi.ThrowableRenderer; |
| import org.apache.log4j.spi.ThrowableRendererSupport; |
| import org.apache.logging.log4j.core.config.ConfigurationFactory; |
| import org.apache.logging.log4j.core.net.UrlConnectionFactory; |
| import org.apache.logging.log4j.util.PropertiesUtil; |
| import org.apache.logging.log4j.util.StackLocatorUtil; |
| |
| /** |
| * Configures Log4j from properties. |
| */ |
| public class PropertyConfigurator implements Configurator { |
| |
| static class NameValue { |
| String key, value; |
| |
| public NameValue(final String key, final String value) { |
| this.key = key; |
| this.value = value; |
| } |
| |
| @Override |
| public String toString() { |
| return key + "=" + value; |
| } |
| } |
| |
| static class PropertyWatchdog extends FileWatchdog { |
| |
| private final ClassLoader classLoader; |
| |
| PropertyWatchdog(final String fileName, final ClassLoader classLoader) { |
| super(fileName); |
| this.classLoader = classLoader; |
| } |
| |
| /** |
| * Call {@link PropertyConfigurator#configure(String)} with the <code>filename</code> to reconfigure log4j. |
| */ |
| @Override |
| public void doOnChange() { |
| new PropertyConfigurator().doConfigure(filename, LogManager.getLoggerRepository(), classLoader); |
| } |
| } |
| |
| class SortedKeyEnumeration implements Enumeration { |
| |
| private final Enumeration e; |
| |
| public SortedKeyEnumeration(final Hashtable ht) { |
| final Enumeration f = ht.keys(); |
| final Vector keys = new Vector(ht.size()); |
| for (int i, last = 0; f.hasMoreElements(); ++last) { |
| final String key = (String) f.nextElement(); |
| for (i = 0; i < last; ++i) { |
| final String s = (String) keys.get(i); |
| if (key.compareTo(s) <= 0) { |
| break; |
| } |
| } |
| keys.add(i, key); |
| } |
| e = keys.elements(); |
| } |
| |
| @Override |
| public boolean hasMoreElements() { |
| return e.hasMoreElements(); |
| } |
| |
| @Override |
| public Object nextElement() { |
| return e.nextElement(); |
| } |
| } |
| |
| private static final String CATEGORY_PREFIX = "log4j.category."; |
| private static final String LOGGER_PREFIX = "log4j.logger."; |
| private static final String FACTORY_PREFIX = "log4j.factory"; |
| private static final String ADDITIVITY_PREFIX = "log4j.additivity."; |
| private static final String APPENDER_PREFIX = "log4j.appender."; |
| private static final String RENDERER_PREFIX = "log4j.renderer."; |
| |
| private static final String THROWABLE_RENDERER_PREFIX = "log4j.throwableRenderer"; |
| private static final String LOGGER_REF = "logger-ref"; |
| private static final String ROOT_REF = "root-ref"; |
| private static final String APPENDER_REF_TAG = "appender-ref"; |
| |
| /** |
| * Key for specifying the {@link org.apache.log4j.spi.LoggerFactory LoggerFactory}. Currently set to |
| * "<code>log4j.loggerFactory</code>". |
| */ |
| public static final String LOGGER_FACTORY_KEY = "log4j.loggerFactory"; |
| |
| /** |
| * If property set to true, then hierarchy will be reset before configuration. |
| */ |
| private static final String RESET_KEY = "log4j.reset"; |
| |
| private static final String INTERNAL_ROOT_NAME = "root"; |
| |
| private static boolean isFullCompatibilityEnabled() { |
| return PropertiesUtil.getProperties().getBooleanProperty(ConfigurationFactory.LOG4J1_EXPERIMENTAL); |
| } |
| |
| private static void warnFullCompatibilityDisabled() { |
| LogLog.warn( |
| "Ignoring `PropertyConfigurator` call, since `log4j1.compatibility` is not enabled.\n" |
| + "See https://logging.staged.apache.org/log4j/2.x/migrate-from-log4j1.html#log4j1.compatibility for details."); |
| } |
| |
| /** |
| * Reads configuration options from an InputStream. |
| * |
| * @param inputStream The input stream |
| */ |
| public static void configure(final InputStream inputStream) { |
| new PropertyConfigurator() |
| .doConfigure(inputStream, LogManager.getLoggerRepository(), StackLocatorUtil.getCallerClassLoader(2)); |
| } |
| |
| /** |
| * Reads configuration options from <code>properties</code>. |
| * |
| * See {@link #doConfigure(String, LoggerRepository)} for the expected format. |
| * |
| * @param properties The properties |
| */ |
| public static void configure(final Properties properties) { |
| new PropertyConfigurator() |
| .doConfigure(properties, LogManager.getLoggerRepository(), StackLocatorUtil.getCallerClassLoader(2)); |
| } |
| |
| /** |
| * Reads configuration options from configuration file. |
| * |
| * @param fileName The configuration file. |
| */ |
| public static void configure(final String fileName) { |
| new PropertyConfigurator() |
| .doConfigure(fileName, LogManager.getLoggerRepository(), StackLocatorUtil.getCallerClassLoader(2)); |
| } |
| |
| /** |
| * Reads configuration options from url <code>configURL</code>. |
| * |
| * @param configURL The configuration URL |
| */ |
| public static void configure(final URL configURL) { |
| new PropertyConfigurator() |
| .doConfigure(configURL, LogManager.getLoggerRepository(), StackLocatorUtil.getCallerClassLoader(2)); |
| } |
| |
| /** |
| * Like {@link #configureAndWatch(String, long)} except that the default delay as defined by FileWatchdog.DEFAULT_DELAY |
| * is used. |
| * |
| * @param configFilename A file in key=value format. |
| */ |
| public static void configureAndWatch(final String configFilename) { |
| configureAndWatch(configFilename, FileWatchdog.DEFAULT_DELAY, StackLocatorUtil.getCallerClassLoader(2)); |
| } |
| |
| /** |
| * Reads the configuration file <code>configFilename</code> if it exists. Moreover, a thread will be created that will |
| * periodically check if <code>configFilename</code> has been created or modified. The period is determined by the |
| * <code>delay</code> argument. If a change or file creation is detected, then <code>configFilename</code> is read to |
| * configure log4j. |
| * |
| * @param configFilename A file in key=value format. |
| * @param delayMillis The delay in milliseconds to wait between each check. |
| */ |
| public static void configureAndWatch(final String configFilename, final long delayMillis) { |
| configureAndWatch(configFilename, delayMillis, StackLocatorUtil.getCallerClassLoader(2)); |
| } |
| |
| private static void configureAndWatch( |
| final String configFilename, final long delay, final ClassLoader classLoader) { |
| if (isFullCompatibilityEnabled()) { |
| final PropertyWatchdog watchdog = new PropertyWatchdog(configFilename, classLoader); |
| watchdog.setDelay(delay); |
| watchdog.start(); |
| } else { |
| warnFullCompatibilityDisabled(); |
| } |
| } |
| |
| /** |
| * Used internally to keep track of configured appenders. |
| */ |
| protected Hashtable registry = new Hashtable(11); |
| |
| private LoggerRepository repository; |
| |
| protected LoggerFactory loggerFactory = new DefaultCategoryFactory(); |
| |
| /** |
| * Checks the provided <code>Properties</code> object for a {@link org.apache.log4j.spi.LoggerFactory LoggerFactory} |
| * entry specified by {@link #LOGGER_FACTORY_KEY}. If such an entry exists, an attempt is made to create an instance |
| * using the default constructor. This instance is used for subsequent Category creations within this configurator. |
| * |
| * @see #parseCatsAndRenderers |
| */ |
| protected void configureLoggerFactory(final Properties properties) { |
| if (isFullCompatibilityEnabled()) { |
| final String factoryClassName = OptionConverter.findAndSubst(LOGGER_FACTORY_KEY, properties); |
| if (factoryClassName != null) { |
| LogLog.debug("Setting category factory to [" + factoryClassName + "]."); |
| loggerFactory = (LoggerFactory) |
| OptionConverter.instantiateByClassName(factoryClassName, LoggerFactory.class, loggerFactory); |
| PropertySetter.setProperties(loggerFactory, properties, FACTORY_PREFIX + "."); |
| } |
| } else { |
| warnFullCompatibilityDisabled(); |
| } |
| } |
| |
| /** |
| * Reads configuration options from an InputStream. |
| * |
| * @param inputStream The input stream |
| * @param loggerRepository The hierarchy |
| */ |
| @Override |
| public void doConfigure(final InputStream inputStream, final LoggerRepository loggerRepository) { |
| doConfigure(inputStream, loggerRepository, StackLocatorUtil.getCallerClassLoader(2)); |
| } |
| |
| private void doConfigure( |
| final InputStream inputStream, final LoggerRepository loggerRepository, final ClassLoader classLoader) { |
| doConfigure(loadProperties(inputStream), loggerRepository, classLoader); |
| } |
| |
| /** |
| * Reads configuration options from <code>properties</code>. |
| * |
| * See {@link #doConfigure(String, LoggerRepository)} for the expected format. |
| * |
| * @param properties The properties |
| * @param loggerRepository The hierarchy |
| */ |
| public void doConfigure(final Properties properties, final LoggerRepository loggerRepository) { |
| doConfigure(properties, loggerRepository, StackLocatorUtil.getCallerClassLoader(2)); |
| } |
| |
| /** |
| * Reads configuration options from <code>properties</code>. |
| * <p> |
| * See {@link #doConfigure(String, LoggerRepository)} for the expected format. |
| * |
| * @param properties The properties |
| * @param loggerRepository The hierarchy |
| */ |
| private void doConfigure( |
| final Properties properties, final LoggerRepository loggerRepository, final ClassLoader classLoader) { |
| if (isFullCompatibilityEnabled()) { |
| final PropertiesConfiguration configuration = |
| new PropertiesConfiguration(LogManager.getContext(classLoader), properties); |
| configuration.doConfigure(); |
| |
| repository = loggerRepository; |
| |
| // We don't want to hold references to appenders preventing their |
| // garbage collection. |
| registry.clear(); |
| |
| org.apache.logging.log4j.core.config.Configurator.reconfigure(configuration); |
| } else { |
| warnFullCompatibilityDisabled(); |
| } |
| } |
| |
| /** |
| * Reads configuration options from configuration file. |
| * |
| * @param fileName The configuration file |
| * @param loggerRepository The hierarchy |
| */ |
| public void doConfigure(final String fileName, final LoggerRepository loggerRepository) { |
| doConfigure(fileName, loggerRepository, StackLocatorUtil.getCallerClassLoader(2)); |
| } |
| |
| /** |
| * Reads configuration options from configuration file. |
| * |
| * @param fileName The configuration file |
| * @param loggerRepository The hierarchy |
| */ |
| @SuppressFBWarnings(value = "PATH_TRAVERSAL_IN", justification = "The filename comes from a system property.") |
| private void doConfigure( |
| final String fileName, final LoggerRepository loggerRepository, final ClassLoader classLoader) { |
| if (isFullCompatibilityEnabled()) { |
| try (final InputStream inputStream = Files.newInputStream(Paths.get(fileName))) { |
| doConfigure(inputStream, loggerRepository, classLoader); |
| } catch (final Exception e) { |
| if (e instanceof InterruptedIOException) { |
| Thread.currentThread().interrupt(); |
| } |
| LogLog.error("Could not read configuration file [" + fileName + "].", e); |
| LogLog.error("Ignoring configuration file [" + fileName + "]."); |
| } |
| } else { |
| warnFullCompatibilityDisabled(); |
| } |
| } |
| |
| /** |
| * Read configuration options from url <code>configURL</code>. |
| * |
| * @param url The configuration URL |
| * @param loggerRepository The hierarchy |
| */ |
| @Override |
| public void doConfigure(final URL url, final LoggerRepository loggerRepository) { |
| doConfigure(url, loggerRepository, StackLocatorUtil.getCallerClassLoader(2)); |
| } |
| |
| private void doConfigure(final URL url, final LoggerRepository loggerRepository, final ClassLoader classLoader) { |
| if (isFullCompatibilityEnabled()) { |
| LogLog.debug("Reading configuration from URL " + url); |
| try { |
| final URLConnection urlConnection = UrlConnectionFactory.createConnection(url); |
| try (final InputStream inputStream = urlConnection.getInputStream()) { |
| doConfigure(inputStream, loggerRepository, classLoader); |
| } |
| } catch (final IOException e) { |
| LogLog.error("Could not read configuration file from URL [" + url + "].", e); |
| LogLog.error("Ignoring configuration file [" + url + "]."); |
| } |
| } else { |
| warnFullCompatibilityDisabled(); |
| } |
| } |
| |
| private Properties loadProperties(final InputStream inputStream) { |
| final Properties loaded = new Properties(); |
| try { |
| loaded.load(inputStream); |
| } catch (final IOException | IllegalArgumentException e) { |
| if (e instanceof InterruptedIOException) { |
| Thread.currentThread().interrupt(); |
| } |
| LogLog.error("Could not read configuration file from InputStream [" + inputStream + "].", e); |
| LogLog.error("Ignoring configuration InputStream [" + inputStream + "]."); |
| return null; |
| } |
| return loaded; |
| } |
| |
| /** |
| * Parse the additivity option for a non-root category. |
| */ |
| private void parseAdditivityForLogger(final Properties properties, final Logger logger, final String loggerName) { |
| final String value = OptionConverter.findAndSubst(ADDITIVITY_PREFIX + loggerName, properties); |
| LogLog.debug("Handling " + ADDITIVITY_PREFIX + loggerName + "=[" + value + "]"); |
| // touch additivity only if necessary |
| if (value != null && !value.isEmpty()) { |
| final boolean additivity = OptionConverter.toBoolean(value, true); |
| LogLog.debug("Setting additivity for \"" + loggerName + "\" to " + additivity); |
| logger.setAdditivity(additivity); |
| } |
| } |
| |
| private Appender parseAppender(final Properties properties, final String appenderName) { |
| Appender appender = (Appender) registry.get(appenderName); |
| if ((appender != null)) { |
| LogLog.debug("Appender \"" + appenderName + "\" was already parsed."); |
| return appender; |
| } |
| // Appender was not previously initialized. |
| final String prefix = APPENDER_PREFIX + appenderName; |
| final String layoutPrefix = prefix + ".layout"; |
| |
| appender = |
| (Appender) OptionConverter.instantiateByKey(properties, prefix, org.apache.log4j.Appender.class, null); |
| if (appender == null) { |
| LogLog.error("Could not instantiate appender named \"" + appenderName + "\"."); |
| return null; |
| } |
| appender.setName(appenderName); |
| |
| if (appender instanceof OptionHandler) { |
| if (appender.requiresLayout()) { |
| final Layout layout = |
| (Layout) OptionConverter.instantiateByKey(properties, layoutPrefix, Layout.class, null); |
| if (layout != null) { |
| appender.setLayout(layout); |
| LogLog.debug("Parsing layout options for \"" + appenderName + "\"."); |
| // configureOptionHandler(layout, layoutPrefix + ".", props); |
| PropertySetter.setProperties(layout, properties, layoutPrefix + "."); |
| LogLog.debug("End of parsing for \"" + appenderName + "\"."); |
| } |
| } |
| final String errorHandlerPrefix = prefix + ".errorhandler"; |
| final String errorHandlerClass = OptionConverter.findAndSubst(errorHandlerPrefix, properties); |
| if (errorHandlerClass != null) { |
| final ErrorHandler eh = (ErrorHandler) |
| OptionConverter.instantiateByKey(properties, errorHandlerPrefix, ErrorHandler.class, null); |
| if (eh != null) { |
| appender.setErrorHandler(eh); |
| LogLog.debug("Parsing errorhandler options for \"" + appenderName + "\"."); |
| parseErrorHandler(eh, errorHandlerPrefix, properties, repository); |
| final Properties edited = new Properties(); |
| final String[] keys = new String[] { |
| errorHandlerPrefix + "." + ROOT_REF, |
| errorHandlerPrefix + "." + LOGGER_REF, |
| errorHandlerPrefix + "." + APPENDER_REF_TAG |
| }; |
| for (final Object element : properties.entrySet()) { |
| final Map.Entry entry = (Map.Entry) element; |
| int i = 0; |
| for (; i < keys.length; i++) { |
| if (keys[i].equals(entry.getKey())) { |
| break; |
| } |
| } |
| if (i == keys.length) { |
| edited.put(entry.getKey(), entry.getValue()); |
| } |
| } |
| PropertySetter.setProperties(eh, edited, errorHandlerPrefix + "."); |
| LogLog.debug("End of errorhandler parsing for \"" + appenderName + "\"."); |
| } |
| } |
| // configureOptionHandler((OptionHandler) appender, prefix + ".", props); |
| PropertySetter.setProperties(appender, properties, prefix + "."); |
| LogLog.debug("Parsed \"" + appenderName + "\" options."); |
| } |
| parseAppenderFilters(properties, appenderName, appender); |
| registry.put(appender.getName(), appender); |
| return appender; |
| } |
| |
| private void parseAppenderFilters(final Properties properties, final String appenderName, final Appender appender) { |
| // extract filters and filter options from props into a hashtable mapping |
| // the property name defining the filter class to a list of pre-parsed |
| // name-value pairs associated to that filter |
| final String filterPrefix = APPENDER_PREFIX + appenderName + ".filter."; |
| final int fIdx = filterPrefix.length(); |
| final Hashtable filters = new Hashtable(); |
| final Enumeration e = properties.keys(); |
| String name = ""; |
| while (e.hasMoreElements()) { |
| final String key = (String) e.nextElement(); |
| if (key.startsWith(filterPrefix)) { |
| final int dotIdx = key.indexOf('.', fIdx); |
| String filterKey = key; |
| if (dotIdx != -1) { |
| filterKey = key.substring(0, dotIdx); |
| name = key.substring(dotIdx + 1); |
| } |
| Vector filterOpts = (Vector) filters.get(filterKey); |
| if (filterOpts == null) { |
| filterOpts = new Vector(); |
| filters.put(filterKey, filterOpts); |
| } |
| if (dotIdx != -1) { |
| final String value = OptionConverter.findAndSubst(key, properties); |
| filterOpts.add(new NameValue(name, value)); |
| } |
| } |
| } |
| |
| // sort filters by IDs, insantiate filters, set filter options, |
| // add filters to the appender |
| final Enumeration g = new SortedKeyEnumeration(filters); |
| Filter head = null; |
| while (g.hasMoreElements()) { |
| final String key = (String) g.nextElement(); |
| final String clazz = properties.getProperty(key); |
| if (clazz != null) { |
| LogLog.debug("Filter key: [" + key + "] class: [" + properties.getProperty(key) + "] props: " |
| + filters.get(key)); |
| final Filter filter = (Filter) OptionConverter.instantiateByClassName(clazz, Filter.class, null); |
| if (filter != null) { |
| final PropertySetter propSetter = new PropertySetter(filter); |
| final Vector v = (Vector) filters.get(key); |
| final Enumeration filterProps = v.elements(); |
| while (filterProps.hasMoreElements()) { |
| final NameValue kv = (NameValue) filterProps.nextElement(); |
| propSetter.setProperty(kv.key, kv.value); |
| } |
| propSetter.activate(); |
| LogLog.debug("Adding filter of type [" + filter.getClass() + "] to appender named [" |
| + appender.getName() + "]."); |
| head = FilterAdapter.addFilter(head, filter); |
| } |
| } else { |
| LogLog.warn("Missing class definition for filter: [" + key + "]"); |
| } |
| } |
| appender.addFilter(head); |
| } |
| |
| /** |
| * This method must work for the root category as well. |
| */ |
| private void parseCategory( |
| final Properties properties, |
| final Logger logger, |
| final String optionKey, |
| final String loggerName, |
| final String value) { |
| |
| LogLog.debug("Parsing for [" + loggerName + "] with value=[" + value + "]."); |
| // We must skip over ',' but not white space |
| final StringTokenizer st = new StringTokenizer(value, ","); |
| |
| // If value is not in the form ", appender.." or "", then we should set |
| // the level of the loggeregory. |
| |
| if (!(value.startsWith(",") || value.isEmpty())) { |
| |
| // just to be on the safe side... |
| if (!st.hasMoreTokens()) { |
| return; |
| } |
| |
| final String levelStr = st.nextToken(); |
| LogLog.debug("Level token is [" + levelStr + "]."); |
| |
| // If the level value is inherited, set category level value to |
| // null. We also check that the user has not specified inherited for the |
| // root category. |
| if (INHERITED.equalsIgnoreCase(levelStr) || NULL.equalsIgnoreCase(levelStr)) { |
| if (loggerName.equals(INTERNAL_ROOT_NAME)) { |
| LogLog.warn("The root logger cannot be set to null."); |
| } else { |
| logger.setLevel(null); |
| } |
| } else { |
| logger.setLevel(OptionConverter.toLevel(levelStr, Log4j1Configuration.DEFAULT_LEVEL)); |
| } |
| LogLog.debug("Category " + loggerName + " set to " + logger.getLevel()); |
| } |
| |
| // Begin by removing all existing appenders. |
| logger.removeAllAppenders(); |
| |
| Appender appender; |
| String appenderName; |
| while (st.hasMoreTokens()) { |
| appenderName = st.nextToken().trim(); |
| if (appenderName == null || appenderName.equals(",")) { |
| continue; |
| } |
| LogLog.debug("Parsing appender named \"" + appenderName + "\"."); |
| appender = parseAppender(properties, appenderName); |
| if (appender != null) { |
| logger.addAppender(appender); |
| } |
| } |
| } |
| |
| /** |
| * Parse non-root elements, such non-root categories and renderers. |
| */ |
| protected void parseCatsAndRenderers(final Properties properties, final LoggerRepository loggerRepository) { |
| if (!isFullCompatibilityEnabled()) { |
| warnFullCompatibilityDisabled(); |
| return; |
| } |
| final Enumeration enumeration = properties.propertyNames(); |
| while (enumeration.hasMoreElements()) { |
| final String key = (String) enumeration.nextElement(); |
| if (key.startsWith(CATEGORY_PREFIX) || key.startsWith(LOGGER_PREFIX)) { |
| String loggerName = null; |
| if (key.startsWith(CATEGORY_PREFIX)) { |
| loggerName = key.substring(CATEGORY_PREFIX.length()); |
| } else if (key.startsWith(LOGGER_PREFIX)) { |
| loggerName = key.substring(LOGGER_PREFIX.length()); |
| } |
| final String value = OptionConverter.findAndSubst(key, properties); |
| final Logger logger = loggerRepository.getLogger(loggerName, loggerFactory); |
| synchronized (logger) { |
| parseCategory(properties, logger, key, loggerName, value); |
| parseAdditivityForLogger(properties, logger, loggerName); |
| } |
| } else if (key.startsWith(RENDERER_PREFIX)) { |
| final String renderedClass = key.substring(RENDERER_PREFIX.length()); |
| final String renderingClass = OptionConverter.findAndSubst(key, properties); |
| if (loggerRepository instanceof RendererSupport) { |
| RendererMap.addRenderer((RendererSupport) loggerRepository, renderedClass, renderingClass); |
| } |
| } else if (key.equals(THROWABLE_RENDERER_PREFIX)) { |
| if (loggerRepository instanceof ThrowableRendererSupport) { |
| final ThrowableRenderer tr = (ThrowableRenderer) OptionConverter.instantiateByKey( |
| properties, THROWABLE_RENDERER_PREFIX, org.apache.log4j.spi.ThrowableRenderer.class, null); |
| if (tr == null) { |
| LogLog.error("Could not instantiate throwableRenderer."); |
| } else { |
| final PropertySetter setter = new PropertySetter(tr); |
| setter.setProperties(properties, THROWABLE_RENDERER_PREFIX + "."); |
| ((ThrowableRendererSupport) loggerRepository).setThrowableRenderer(tr); |
| } |
| } |
| } |
| } |
| } |
| |
| private void parseErrorHandler( |
| final ErrorHandler errorHandler, |
| final String errorHandlerPrefix, |
| final Properties props, |
| final LoggerRepository loggerRepository) { |
| if (errorHandler != null && loggerRepository != null) { |
| final boolean rootRef = OptionConverter.toBoolean( |
| OptionConverter.findAndSubst(errorHandlerPrefix + ROOT_REF, props), false); |
| if (rootRef) { |
| errorHandler.setLogger(loggerRepository.getRootLogger()); |
| } |
| final String loggerName = OptionConverter.findAndSubst(errorHandlerPrefix + LOGGER_REF, props); |
| if (loggerName != null) { |
| final Logger logger = loggerFactory == null |
| ? loggerRepository.getLogger(loggerName) |
| : loggerRepository.getLogger(loggerName, loggerFactory); |
| errorHandler.setLogger(logger); |
| } |
| final String appenderName = OptionConverter.findAndSubst(errorHandlerPrefix + APPENDER_REF_TAG, props); |
| if (appenderName != null) { |
| final Appender backup = parseAppender(props, appenderName); |
| if (backup != null) { |
| errorHandler.setBackupAppender(backup); |
| } |
| } |
| } |
| } |
| } |