All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.log4j.PropertyConfigurator Maven / Gradle / Ivy

There is a newer version: 6.1.2
Show newest version
/*
 * 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.
 */

// Contibutors: "Luke Blanshard" 
//              "Mark DONSZELMANN" 
//               Anders Kristensen 

package org.apache.log4j;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.net.URLConnection;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.Vector;
import java.util.Iterator;
import java.util.Map;

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.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.log4j.spi.ErrorHandler;

/**
 * Allows the configuration of log4j from an external file. See
 * {@link #doConfigure(String, LoggerRepository)} for the expected
 * format.
 *
 * 

* It is sometimes useful to see how log4j is reading configuration files. You can enable log4j internal logging by * defining the log4j.debug variable. * *

* As of log4j version 0.8.5, at class initialization time class, the file * log4j.properties will be searched from the search path used to load * classes. If the file can be found, then it will be fed to the {@link PropertyConfigurator#configure(java.net.URL)} * method. * *

* The PropertyConfigurator does not handle the advanced configuration features supported by the * {@link org.apache.log4j.xml.DOMConfigurator DOMConfigurator} such as support custom * {@link org.apache.log4j.spi.ErrorHandler ErrorHandlers}, nested appenders such as the * {@link org.apache.log4j.AsyncAppender AsyncAppender}, etc. * *

* All option values admit variable substitution. The syntax of variable substitution is similar to that of * Unix shells. The string between an opening "${" and closing "}" is interpreted as a * key. The value of the substituted variable can be defined as a system property or in the configuration file itself. * The value of the key is first searched in the system properties, and if not found there, it is then searched in the * configuration file being parsed. The corresponding value replaces the ${variableName} sequence. For example, if * java.home system property is set to /home/xyz, then every occurrence of the sequence * ${java.home} will be interpreted as * /home/xyz. * * @author Ceki Gülcü * @author Anders Kristensen * @since 0.8.1 */ public class PropertyConfigurator implements Configurator { /** * Used internally to keep track of configured appenders. */ protected Hashtable registry = new Hashtable(11); private LoggerRepository repository; protected LoggerFactory loggerFactory = new DefaultCategoryFactory(); static final String CATEGORY_PREFIX = "log4j.category."; static final String LOGGER_PREFIX = "log4j.logger."; static final String FACTORY_PREFIX = "log4j.factory"; static final String ADDITIVITY_PREFIX = "log4j.additivity."; static final String ROOT_CATEGORY_PREFIX = "log4j.rootCategory"; static final String ROOT_LOGGER_PREFIX = "log4j.rootLogger"; static final String APPENDER_PREFIX = "log4j.appender."; static final String RENDERER_PREFIX = "log4j.renderer."; static final String THRESHOLD_PREFIX = "log4j.threshold"; 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 * "log4j.loggerFactory". */ 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"; static final private String INTERNAL_ROOT_NAME = "root"; /** * Read configuration from a file. The existing configuration is not cleared nor reset. If you require a * different behavior, then call {@link LogManager#resetConfiguration resetConfiguration} method before calling * doConfigure. * *

* The configuration file consists of statements in the format * key=value. The syntax of different configuration elements are * discussed below. * *

Repository-wide threshold

* *

* The repository-wide threshold filters logging requests by level regardless of logger. The syntax is: * *

     * log4j.threshold=[level]
     * 
* *

* The level value can consist of the string values OFF, FATAL, ERROR, WARN, INFO, DEBUG, ALL or a custom * level value. A custom level value can be specified in the form level#classname. By default the * repository-wide threshold is set to the lowest possible value, namely the level * ALL. *

* * *

Appender configuration

* *

* Appender configuration syntax is: * *

     * # For appender named appenderName, set its class.
     * # Note: The appender name can contain dots.
     * log4j.appender.appenderName=fully.qualified.name.of.appender.class
     *
     * # Set appender specific options.
     * log4j.appender.appenderName.option1=value1
     * ...
     * log4j.appender.appenderName.optionN=valueN
     * 
*

* For each named appender you can configure its {@link Layout}. The syntax for configuring an appender's layout * is: * *

     * log4j.appender.appenderName.layout=fully.qualified.name.of.layout.class
     * log4j.appender.appenderName.layout.option1=value1
     * ....
     * log4j.appender.appenderName.layout.optionN=valueN
     * 
*

* The syntax for adding {@link Filter}s to an appender is: * *

     * log4j.appender.appenderName.filter.ID=fully.qualified.name.of.filter.class
     * log4j.appender.appenderName.filter.ID.option1=value1
     * ...
     * log4j.appender.appenderName.filter.ID.optionN=valueN
     * 
*

* The first line defines the class name of the filter identified by ID; subsequent lines with the same ID specify * filter option - value paris. Multiple filters are added to the appender in the lexicographic order of IDs. *

* The syntax for adding an {@link ErrorHandler} to an appender is: * *

     * log4j.appender.appenderName.errorhandler=fully.qualified.name.of.filter.class
     * log4j.appender.appenderName.errorhandler.root-ref={true|false}
     * log4j.appender.appenderName.errorhandler.logger-ref=loggerName
     * log4j.appender.appenderName.errorhandler.appender-ref=appenderName
     * log4j.appender.appenderName.errorhandler.option1=value1
     * ...
     * log4j.appender.appenderName.errorhandler.optionN=valueN
     * 
* *

Configuring loggers

* *

* The syntax for configuring the root logger is: * *

     * log4j.rootLogger=[level], appenderName, appenderName, ...
     * 
* *

* This syntax means that an optional level can be supplied followed by appender names separated by * commas. * *

* The level value can consist of the string values OFF, FATAL, ERROR, WARN, INFO, DEBUG, ALL or a custom * level value. A custom level value can be specified in the form level#classname. * *

* If a level value is specified, then the root level is set to the corresponding level. If no level value is * specified, then the root level remains untouched. * *

* The root logger can be assigned multiple appenders. * *

* Each appenderName (separated by commas) will be added to the root logger. The named appender is defined * using the appender syntax defined above. * *

* For non-root categories the syntax is almost the same: * *

     * log4j.logger.logger_name=[level|INHERITED|NULL], appenderName, appenderName, ...
     * 
* *

* The meaning of the optional level value is discussed above in relation to the root logger. In addition however, * the value INHERITED can be specified meaning that the named logger should inherit its level from the logger * hierarchy. * *

* If no level value is supplied, then the level of the named logger remains untouched. * *

* By default categories inherit their level from the hierarchy. However, if you set the level of a logger and later * decide that that logger should inherit its level, then you should specify INHERITED as the value for the level * value. NULL is a synonym for INHERITED. * *

* Similar to the root logger syntax, each appenderName (separated by commas) will be attached to the named * logger. * *

* See the appender additivity rule in the user manual for the * meaning of the additivity flag. * *

ObjectRenderers

*

* You can customize the way message objects of a given type are converted to String before being logged. This is * done by specifying an {@link org.apache.log4j.or.ObjectRenderer ObjectRenderer} for the object type would like to * customize. * *

* The syntax is: * *

     * log4j.renderer.fully.qualified.name.of.rendered.class = fully.qualified.name.of.rendering.class
     * 
*

* As in, * *

     * log4j.renderer.my.Fruit = my.FruitRenderer
     * 
* *

ThrowableRenderer

*

* You can customize the way an instance of Throwable is converted to String before being logged. This is done by * specifying an {@link org.apache.log4j.spi.ThrowableRenderer ThrowableRenderer}. * *

* The syntax is: * *

     * log4j.throwableRenderer=fully.qualified.name.of.rendering.class
     * log4j.throwableRenderer.paramName=paramValue
     * 
*

* As in, * *

     * log4j.throwableRenderer = org.apache.log4j.EnhancedThrowableRenderer
     * 
* *

Logger Factories

*

* The usage of custom logger factories is discouraged and no longer documented. * *

Resetting Hierarchy

*

* The hierarchy will be reset before configuration when log4j.reset=true is present in the properties file. * *

Example

* *

* An example configuration is given below. Other configuration file examples are given in the examples * folder. * *

     *
     * # Set options for appender named "A1".
     * # Appender "A1" will be a SyslogAppender
     * log4j.appender.A1=org.apache.log4j.net.SyslogAppender
     *
     * # The syslog daemon resides on www.abc.net
     * log4j.appender.A1.SyslogHost=www.abc.net
     *
     * # A1's layout is a PatternLayout, using the conversion pattern
     * # %r %-5p %c{2} %M.%L %x - %m\n. Thus, the log output will
     * # include # the relative time since the start of the application in
     * # milliseconds, followed by the level of the log request,
     * # followed by the two rightmost components of the logger name,
     * # followed by the callers method name, followed by the line number,
     * # the nested disgnostic context and finally the message itself.
     * # Refer to the documentation of {@link PatternLayout} for further information
     * # on the syntax of the ConversionPattern key.
     * log4j.appender.A1.layout=org.apache.log4j.PatternLayout
     * log4j.appender.A1.layout.ConversionPattern=%-4r %-5p %c{2} %M.%L %x - %m\n
     *
     * # Set options for appender named "A2"
     * # A2 should be a RollingFileAppender, with maximum file size of 10 MB
     * # using at most one backup file. A2's layout is TTCC, using the
     * # ISO8061 date format with context printing enabled.
     * log4j.appender.A2=org.apache.log4j.RollingFileAppender
     * log4j.appender.A2.MaxFileSize=10MB
     * log4j.appender.A2.MaxBackupIndex=1
     * log4j.appender.A2.layout=org.apache.log4j.TTCCLayout
     * log4j.appender.A2.layout.ContextPrinting=enabled
     * log4j.appender.A2.layout.DateFormat=ISO8601
     *
     * # Root logger set to DEBUG using the A2 appender defined above.
     * log4j.rootLogger=DEBUG, A2
     *
     * # Logger definitions:
     * # The SECURITY logger inherits is level from root. However, it's output
     * # will go to A1 appender defined above. It's additivity is non-cumulative.
     * log4j.logger.SECURITY=INHERIT, A1
     * log4j.additivity.SECURITY=false
     *
     * # Only warnings or above will be logged for the logger "SECURITY.access".
     * # Output will go to A1.
     * log4j.logger.SECURITY.access=WARN
     *
     *
     * # The logger "class.of.the.day" inherits its level from the
     * # logger hierarchy.  Output will go to the appender's of the root
     * # logger, A2 in this case.
     * log4j.logger.class.of.the.day=INHERIT
     * 
* *

* Refer to the setOption method in each Appender and Layout for class specific options. * *

* Use the # or ! characters at the beginning of a line for comments. * *

* * @param configFileName The name of the configuration file where the configuration information is stored. */ public void doConfigure(String configFileName, LoggerRepository hierarchy) { Properties props = new Properties(); FileInputStream istream = null; try { istream = new FileInputStream(configFileName); props.load(istream); istream.close(); } catch (Exception e) { if (e instanceof InterruptedIOException || e instanceof InterruptedException) { Thread.currentThread().interrupt(); } LogLog.error("Could not read configuration file [" + configFileName + "].", e); LogLog.error("Ignoring configuration file [" + configFileName + "]."); return; } finally { if (istream != null) { try { istream.close(); } catch (InterruptedIOException ignore) { Thread.currentThread().interrupt(); } catch (Throwable ignore) { } } } // If we reach here, then the config file is alright. doConfigure(props, hierarchy); } /** * */ static public void configure(String configFilename) { new PropertyConfigurator().doConfigure(configFilename, LogManager.getLoggerRepository()); } /** * Read configuration options from url configURL. * * @since 0.8.2 */ public static void configure(java.net.URL configURL) { new PropertyConfigurator().doConfigure(configURL, LogManager.getLoggerRepository()); } /** * Reads configuration options from an InputStream. * * @since 1.2.17 */ public static void configure(InputStream inputStream) { new PropertyConfigurator().doConfigure(inputStream, LogManager.getLoggerRepository()); } /** * Read configuration options from properties. *

* See {@link #doConfigure(String, LoggerRepository)} for the expected format. */ static public void configure(Properties properties) { new PropertyConfigurator().doConfigure(properties, LogManager.getLoggerRepository()); } /** * Like {@link #configureAndWatch(String, long)} except that the default delay as defined by * {@link FileWatchdog#DEFAULT_DELAY} is used. * * @param configFilename A file in key=value format. */ static public void configureAndWatch(String configFilename) { configureAndWatch(configFilename, FileWatchdog.DEFAULT_DELAY); } /** * Read the configuration file configFilename if it exists. Moreover, a thread will be created that * will periodically check if * configFilename has been created or modified. The period is * determined by the delay argument. If a change or file creation is detected, then * configFilename is read to configure log4j. * * @param configFilename A file in key=value format. * @param delay The delay in milliseconds to wait between each check. */ static public void configureAndWatch(String configFilename, long delay) { PropertyWatchdog pdog = new PropertyWatchdog(configFilename); pdog.setDelay(delay); pdog.start(); } /** * Read configuration options from properties. *

* See {@link #doConfigure(String, LoggerRepository)} for the expected format. */ public void doConfigure(Properties properties, LoggerRepository hierarchy) { repository = hierarchy; String value = properties.getProperty(LogLog.DEBUG_KEY); if (value == null) { value = properties.getProperty("log4j.configDebug"); if (value != null) LogLog.warn("[log4j.configDebug] is deprecated. Use [log4j.debug] instead."); } if (value != null) { LogLog.setInternalDebugging(OptionConverter.toBoolean(value, true)); } // // if log4j.reset=true then // reset hierarchy String reset = properties.getProperty(RESET_KEY); if (reset != null && OptionConverter.toBoolean(reset, false)) { hierarchy.resetConfiguration(); } String thresholdStr = OptionConverter.findAndSubst(THRESHOLD_PREFIX, properties); if (thresholdStr != null) { hierarchy.setThreshold(OptionConverter.toLevel(thresholdStr, (Level) Level.ALL)); LogLog.debug("Hierarchy threshold set to [" + hierarchy.getThreshold() + "]."); } configureRootCategory(properties, hierarchy); configureLoggerFactory(properties); parseCatsAndRenderers(properties, hierarchy); LogLog.debug("Finished configuring."); // We don't want to hold references to appenders preventing their // garbage collection. registry.clear(); } /** * Read configuration options from url configURL. * * @since 1.2.17 */ public void doConfigure(InputStream inputStream, LoggerRepository hierarchy) { Properties props = new Properties(); try { props.load(inputStream); } catch (IOException 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; } this.doConfigure(props, hierarchy); } /** * Read configuration options from url configURL. */ public void doConfigure(java.net.URL configURL, LoggerRepository hierarchy) { Properties props = new Properties(); LogLog.debug("Reading configuration from URL " + configURL); InputStream istream = null; URLConnection uConn = null; try { uConn = configURL.openConnection(); uConn.setUseCaches(false); istream = uConn.getInputStream(); props.load(istream); } catch (Exception e) { if (e instanceof InterruptedIOException || e instanceof InterruptedException) { Thread.currentThread().interrupt(); } LogLog.error("Could not read configuration file from URL [" + configURL + "].", e); LogLog.error("Ignoring configuration file [" + configURL + "]."); return; } finally { if (istream != null) { try { istream.close(); } catch (InterruptedIOException ignore) { Thread.currentThread().interrupt(); } catch (IOException ignore) { } catch (RuntimeException ignore) { } } } doConfigure(props, hierarchy); } // -------------------------------------------------------------------------- // Internal stuff // -------------------------------------------------------------------------- /** * Check the provided Properties 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(Properties props) { String factoryClassName = OptionConverter.findAndSubst(LOGGER_FACTORY_KEY, props); if (factoryClassName != null) { LogLog.debug("Setting category factory to [" + factoryClassName + "]."); loggerFactory = (LoggerFactory) OptionConverter.instantiateByClassName(factoryClassName, LoggerFactory.class, loggerFactory); PropertySetter.setProperties(loggerFactory, props, FACTORY_PREFIX + "."); } } /* * void configureOptionHandler(OptionHandler oh, String prefix, Properties * props) { String[] options = oh.getOptionStrings(); if(options == null) * return; * * String value; for(int i = 0; i < options.length; i++) { value = * OptionConverter.findAndSubst(prefix + options[i], props); LogLog.debug( * "Option " + options[i] + "=[" + (value == null? "N/A" : value)+"]."); // Some * option handlers assume that null value are not passed to them. // So don't * remove this check if(value != null) { oh.setOption(options[i], value); } } * oh.activateOptions(); } */ void configureRootCategory(Properties props, LoggerRepository hierarchy) { String effectiveFrefix = ROOT_LOGGER_PREFIX; String value = OptionConverter.findAndSubst(ROOT_LOGGER_PREFIX, props); if (value == null) { value = OptionConverter.findAndSubst(ROOT_CATEGORY_PREFIX, props); effectiveFrefix = ROOT_CATEGORY_PREFIX; } if (value == null) LogLog.debug("Could not find root logger information. Is this OK?"); else { Logger root = hierarchy.getRootLogger(); synchronized (root) { parseCategory(props, root, effectiveFrefix, INTERNAL_ROOT_NAME, value); } } } /** * Parse non-root elements, such non-root categories and renderers. */ protected void parseCatsAndRenderers(Properties props, LoggerRepository hierarchy) { Enumeration enumeration = props.propertyNames(); while (enumeration.hasMoreElements()) { 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()); } String value = OptionConverter.findAndSubst(key, props); Logger logger = hierarchy.getLogger(loggerName, loggerFactory); synchronized (logger) { parseCategory(props, logger, key, loggerName, value); parseAdditivityForLogger(props, logger, loggerName); } } else if (key.startsWith(RENDERER_PREFIX)) { String renderedClass = key.substring(RENDERER_PREFIX.length()); String renderingClass = OptionConverter.findAndSubst(key, props); if (hierarchy instanceof RendererSupport) { RendererMap.addRenderer((RendererSupport) hierarchy, renderedClass, renderingClass); } } else if (key.equals(THROWABLE_RENDERER_PREFIX)) { if (hierarchy instanceof ThrowableRendererSupport) { ThrowableRenderer tr = (ThrowableRenderer) OptionConverter.instantiateByKey(props, THROWABLE_RENDERER_PREFIX, org.apache.log4j.spi.ThrowableRenderer.class, null); if (tr == null) { LogLog.error("Could not instantiate throwableRenderer."); } else { PropertySetter setter = new PropertySetter(tr); setter.setProperties(props, THROWABLE_RENDERER_PREFIX + "."); ((ThrowableRendererSupport) hierarchy).setThrowableRenderer(tr); } } } } } /** * Parse the additivity option for a non-root category. */ void parseAdditivityForLogger(Properties props, Logger cat, String loggerName) { String value = OptionConverter.findAndSubst(ADDITIVITY_PREFIX + loggerName, props); LogLog.debug("Handling " + ADDITIVITY_PREFIX + loggerName + "=[" + value + "]"); // touch additivity only if necessary if ((value != null) && (!value.equals(""))) { boolean additivity = OptionConverter.toBoolean(value, true); LogLog.debug("Setting additivity for \"" + loggerName + "\" to " + additivity); cat.setAdditivity(additivity); } } /** * This method must work for the root category as well. */ void parseCategory(Properties props, Logger logger, String optionKey, String loggerName, String value) { LogLog.debug("Parsing for [" + loggerName + "] with value=[" + value + "]."); // We must skip over ',' but not white space 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.equals(""))) { // just to be on the safe side... if (!st.hasMoreTokens()) return; 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, (Level) Level.DEBUG)); } 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(props, appenderName); if (appender != null) { logger.addAppender(appender); } } } Appender parseAppender(Properties props, String appenderName) { Appender appender = registryGet(appenderName); if ((appender != null)) { LogLog.debug("Appender \"" + appenderName + "\" was already parsed."); return appender; } // Appender was not previously initialized. String prefix = APPENDER_PREFIX + appenderName; String layoutPrefix = prefix + ".layout"; appender = (Appender) OptionConverter.instantiateByKey(props, 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()) { Layout layout = (Layout) OptionConverter.instantiateByKey(props, layoutPrefix, Layout.class, null); if (layout != null) { appender.setLayout(layout); LogLog.debug("Parsing layout options for \"" + appenderName + "\"."); // configureOptionHandler(layout, layoutPrefix + ".", props); PropertySetter.setProperties(layout, props, layoutPrefix + "."); LogLog.debug("End of parsing for \"" + appenderName + "\"."); } } final String errorHandlerPrefix = prefix + ".errorhandler"; String errorHandlerClass = OptionConverter.findAndSubst(errorHandlerPrefix, props); if (errorHandlerClass != null) { ErrorHandler eh = (ErrorHandler) OptionConverter.instantiateByKey(props, errorHandlerPrefix, ErrorHandler.class, null); if (eh != null) { appender.setErrorHandler(eh); LogLog.debug("Parsing errorhandler options for \"" + appenderName + "\"."); parseErrorHandler(eh, errorHandlerPrefix, props, repository); final Properties edited = new Properties(); final String[] keys = new String[] { errorHandlerPrefix + "." + ROOT_REF, errorHandlerPrefix + "." + LOGGER_REF, errorHandlerPrefix + "." + APPENDER_REF_TAG }; for (Iterator iter = props.entrySet().iterator(); iter.hasNext(); ) { Map.Entry entry = (Map.Entry) iter.next(); 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, props, prefix + "."); LogLog.debug("Parsed \"" + appenderName + "\" options."); } parseAppenderFilters(props, appenderName, appender); registryPut(appender); return appender; } private void parseErrorHandler(final ErrorHandler eh, final String errorHandlerPrefix, final Properties props, final LoggerRepository hierarchy) { boolean rootRef = OptionConverter.toBoolean(OptionConverter.findAndSubst(errorHandlerPrefix + ROOT_REF, props), false); if (rootRef) { eh.setLogger(hierarchy.getRootLogger()); } String loggerName = OptionConverter.findAndSubst(errorHandlerPrefix + LOGGER_REF, props); if (loggerName != null) { Logger logger = (loggerFactory == null) ? hierarchy.getLogger(loggerName) : hierarchy.getLogger(loggerName, loggerFactory); eh.setLogger(logger); } String appenderName = OptionConverter.findAndSubst(errorHandlerPrefix + APPENDER_REF_TAG, props); if (appenderName != null) { Appender backup = parseAppender(props, appenderName); if (backup != null) { eh.setBackupAppender(backup); } } } void parseAppenderFilters(Properties props, String appenderName, 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."; int fIdx = filterPrefix.length(); Hashtable filters = new Hashtable(); Enumeration e = props.keys(); String name = ""; while (e.hasMoreElements()) { String key = (String) e.nextElement(); if (key.startsWith(filterPrefix)) { 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) { String value = OptionConverter.findAndSubst(key, props); filterOpts.add(new NameValue(name, value)); } } } // sort filters by IDs, insantiate filters, set filter options, // add filters to the appender Enumeration g = new SortedKeyEnumeration(filters); while (g.hasMoreElements()) { String key = (String) g.nextElement(); String clazz = props.getProperty(key); if (clazz != null) { LogLog.debug( "Filter key: [" + key + "] class: [" + props.getProperty(key) + "] props: " + filters.get(key)); Filter filter = (Filter) OptionConverter.instantiateByClassName(clazz, Filter.class, null); if (filter != null) { PropertySetter propSetter = new PropertySetter(filter); Vector v = (Vector) filters.get(key); Enumeration filterProps = v.elements(); while (filterProps.hasMoreElements()) { 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() + "]."); appender.addFilter(filter); } } else { LogLog.warn("Missing class definition for filter: [" + key + "]"); } } } void registryPut(Appender appender) { registry.put(appender.getName(), appender); } Appender registryGet(String name) { return (Appender) registry.get(name); } } class PropertyWatchdog extends FileWatchdog { PropertyWatchdog(String filename) { super(filename); } /** * Call {@link PropertyConfigurator#configure(String)} with the * filename to reconfigure log4j. */ public void doOnChange() { new PropertyConfigurator().doConfigure(filename, LogManager.getLoggerRepository()); } } class NameValue { String key, value; public NameValue(String key, String value) { this.key = key; this.value = value; } public String toString() { return key + "=" + value; } } class SortedKeyEnumeration implements Enumeration { private Enumeration e; public SortedKeyEnumeration(Hashtable ht) { Enumeration f = ht.keys(); Vector keys = new Vector(ht.size()); for (int i, last = 0; f.hasMoreElements(); ++last) { String key = (String) f.nextElement(); for (i = 0; i < last; ++i) { String s = (String) keys.get(i); if (key.compareTo(s) <= 0) break; } keys.add(i, key); } e = keys.elements(); } public boolean hasMoreElements() { return e.hasMoreElements(); } public Object nextElement() { return e.nextElement(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy