Save This Page
Home » apache-log4j-1.2.16 » org.apache » log4j » [javadoc | source]
    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements.  See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache License, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License.  You may obtain a copy of the License at
    8    * 
    9    *      http://www.apache.org/licenses/LICENSE-2.0
   10    * 
   11    * Unless required by applicable law or agreed to in writing, software
   12    * distributed under the License is distributed on an "AS IS" BASIS,
   13    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   14    * See the License for the specific language governing permissions and
   15    * limitations under the License.
   16    */
   17   
   18   
   19   // Contibutors: "Luke Blanshard" <Luke@quiq.com>
   20   //              "Mark DONSZELMANN" <Mark.Donszelmann@cern.ch>
   21   //               Anders Kristensen <akristensen@dynamicsoft.com>
   22   
   23   package org.apache.log4j;
   24   
   25   import java.io.FileInputStream;
   26   import java.io.IOException;
   27   import java.io.InputStream;
   28   import java.io.InterruptedIOException;
   29   import java.net.URLConnection;
   30   import java.util.Enumeration;
   31   import java.util.Hashtable;
   32   import java.util.Properties;
   33   import java.util.StringTokenizer;
   34   import java.util.Vector;
   35   import java.util.Iterator;
   36   import java.util.Map;
   37   
   38   import org.apache.log4j.config.PropertySetter;
   39   import org.apache.log4j.helpers.FileWatchdog;
   40   import org.apache.log4j.helpers.LogLog;
   41   import org.apache.log4j.helpers.OptionConverter;
   42   import org.apache.log4j.or.RendererMap;
   43   import org.apache.log4j.spi.Configurator;
   44   import org.apache.log4j.spi.Filter;
   45   import org.apache.log4j.spi.LoggerFactory;
   46   import org.apache.log4j.spi.LoggerRepository;
   47   import org.apache.log4j.spi.OptionHandler;
   48   import org.apache.log4j.spi.RendererSupport;
   49   import org.apache.log4j.spi.ThrowableRenderer;
   50   import org.apache.log4j.spi.ThrowableRendererSupport;
   51   import org.apache.log4j.spi.ErrorHandler;
   52   
   53   /**
   54      Allows the configuration of log4j from an external file.  See
   55      <b>{@link #doConfigure(String, LoggerRepository)}</b> for the
   56      expected format.
   57   
   58      <p>It is sometimes useful to see how log4j is reading configuration
   59      files. You can enable log4j internal logging by defining the
   60      <b>log4j.debug</b> variable.
   61   
   62      <P>As of log4j version 0.8.5, at class initialization time class,
   63      the file <b>log4j.properties</b> will be searched from the search
   64      path used to load classes. If the file can be found, then it will
   65      be fed to the {@link PropertyConfigurator#configure(java.net.URL)}
   66      method.
   67   
   68      <p>The <code>PropertyConfigurator</code> does not handle the
   69      advanced configuration features supported by the {@link
   70      org.apache.log4j.xml.DOMConfigurator DOMConfigurator} such as
   71      support custom {@link org.apache.log4j.spi.ErrorHandler ErrorHandlers},
   72      nested appenders such as the {@link org.apache.log4j.AsyncAppender
   73      AsyncAppender}, etc.
   74   
   75      <p>All option <em>values</em> admit variable substitution. The
   76      syntax of variable substitution is similar to that of Unix
   77      shells. The string between an opening <b>&quot;${&quot;</b> and
   78      closing <b>&quot;}&quot;</b> is interpreted as a key. The value of
   79      the substituted variable can be defined as a system property or in
   80      the configuration file itself. The value of the key is first
   81      searched in the system properties, and if not found there, it is
   82      then searched in the configuration file being parsed.  The
   83      corresponding value replaces the ${variableName} sequence. For
   84      example, if <code>java.home</code> system property is set to
   85      <code>/home/xyz</code>, then every occurrence of the sequence
   86      <code>${java.home}</code> will be interpreted as
   87      <code>/home/xyz</code>.
   88   
   89   
   90      @author Ceki G&uuml;lc&uuml;
   91      @author Anders Kristensen
   92      @since 0.8.1 */
   93   public class PropertyConfigurator implements Configurator {
   94   
   95     /**
   96        Used internally to keep track of configured appenders.
   97      */
   98     protected Hashtable registry = new Hashtable(11);  
   99     private LoggerRepository repository;
  100     protected LoggerFactory loggerFactory = new DefaultCategoryFactory();
  101   
  102     static final String      CATEGORY_PREFIX = "log4j.category.";
  103     static final String      LOGGER_PREFIX   = "log4j.logger.";
  104     static final String       FACTORY_PREFIX = "log4j.factory";
  105     static final String    ADDITIVITY_PREFIX = "log4j.additivity.";
  106     static final String ROOT_CATEGORY_PREFIX = "log4j.rootCategory";
  107     static final String ROOT_LOGGER_PREFIX   = "log4j.rootLogger";
  108     static final String      APPENDER_PREFIX = "log4j.appender.";
  109     static final String      RENDERER_PREFIX = "log4j.renderer.";
  110     static final String      THRESHOLD_PREFIX = "log4j.threshold";
  111     private static final String      THROWABLE_RENDERER_PREFIX = "log4j.throwableRenderer";
  112     private static final String LOGGER_REF	= "logger-ref";
  113     private static final String ROOT_REF		= "root-ref";
  114     private static final String APPENDER_REF_TAG 	= "appender-ref";  
  115     
  116   
  117     /** Key for specifying the {@link org.apache.log4j.spi.LoggerFactory
  118         LoggerFactory}.  Currently set to "<code>log4j.loggerFactory</code>".  */
  119     public static final String LOGGER_FACTORY_KEY = "log4j.loggerFactory";
  120   
  121       /**
  122        * If property set to true, then hierarchy will be reset before configuration.
  123        */
  124     private static final String RESET_KEY = "log4j.reset";
  125   
  126     static final private String INTERNAL_ROOT_NAME = "root";
  127   
  128     /**
  129       Read configuration from a file. <b>The existing configuration is
  130       not cleared nor reset.</b> If you require a different behavior,
  131       then call {@link  LogManager#resetConfiguration
  132       resetConfiguration} method before calling
  133       <code>doConfigure</code>.
  134   
  135       <p>The configuration file consists of statements in the format
  136       <code>key=value</code>. The syntax of different configuration
  137       elements are discussed below.
  138   
  139       <h3>Repository-wide threshold</h3>
  140   
  141       <p>The repository-wide threshold filters logging requests by level
  142       regardless of logger. The syntax is:
  143   
  144       <pre>
  145       log4j.threshold=[level]
  146       </pre>
  147   
  148       <p>The level value can consist of the string values OFF, FATAL,
  149       ERROR, WARN, INFO, DEBUG, ALL or a <em>custom level</em> value. A
  150       custom level value can be specified in the form
  151       level#classname. By default the repository-wide threshold is set
  152       to the lowest possible value, namely the level <code>ALL</code>.
  153       </p>
  154   
  155   
  156       <h3>Appender configuration</h3>
  157   
  158       <p>Appender configuration syntax is:
  159       <pre>
  160       # For appender named <i>appenderName</i>, set its class.
  161       # Note: The appender name can contain dots.
  162       log4j.appender.appenderName=fully.qualified.name.of.appender.class
  163   
  164       # Set appender specific options.
  165       log4j.appender.appenderName.option1=value1
  166       ...
  167       log4j.appender.appenderName.optionN=valueN
  168       </pre>
  169   
  170       For each named appender you can configure its {@link Layout}. The
  171       syntax for configuring an appender's layout is:
  172       <pre>
  173       log4j.appender.appenderName.layout=fully.qualified.name.of.layout.class
  174       log4j.appender.appenderName.layout.option1=value1
  175       ....
  176       log4j.appender.appenderName.layout.optionN=valueN
  177       </pre>
  178   
  179       The syntax for adding {@link Filter}s to an appender is:
  180       <pre>
  181       log4j.appender.appenderName.filter.ID=fully.qualified.name.of.filter.class
  182       log4j.appender.appenderName.filter.ID.option1=value1
  183       ...
  184       log4j.appender.appenderName.filter.ID.optionN=valueN
  185       </pre>
  186       The first line defines the class name of the filter identified by ID;
  187       subsequent lines with the same ID specify filter option - value
  188       paris. Multiple filters are added to the appender in the lexicographic
  189       order of IDs.
  190   
  191       The syntax for adding an {@link ErrorHandler} to an appender is:
  192       <pre>
  193       log4j.appender.appenderName.errorhandler=fully.qualified.name.of.filter.class
  194       log4j.appender.appenderName.errorhandler.root-ref={true|false}
  195       log4j.appender.appenderName.errorhandler.logger-ref=loggerName
  196       log4j.appender.appenderName.errorhandler.appender-ref=appenderName
  197       log4j.appender.appenderName.errorhandler.option1=value1
  198       ...
  199       log4j.appender.appenderName.errorhandler.optionN=valueN
  200       </pre>
  201   
  202       <h3>Configuring loggers</h3>
  203   
  204       <p>The syntax for configuring the root logger is:
  205       <pre>
  206         log4j.rootLogger=[level], appenderName, appenderName, ...
  207       </pre>
  208   
  209       <p>This syntax means that an optional <em>level</em> can be
  210       supplied followed by appender names separated by commas.
  211   
  212       <p>The level value can consist of the string values OFF, FATAL,
  213       ERROR, WARN, INFO, DEBUG, ALL or a <em>custom level</em> value. A
  214       custom level value can be specified in the form
  215       <code>level#classname</code>.
  216   
  217       <p>If a level value is specified, then the root level is set
  218       to the corresponding level.  If no level value is specified,
  219       then the root level remains untouched.
  220   
  221       <p>The root logger can be assigned multiple appenders.
  222   
  223       <p>Each <i>appenderName</i> (separated by commas) will be added to
  224       the root logger. The named appender is defined using the
  225       appender syntax defined above.
  226   
  227       <p>For non-root categories the syntax is almost the same:
  228       <pre>
  229       log4j.logger.logger_name=[level|INHERITED|NULL], appenderName, appenderName, ...
  230       </pre>
  231   
  232       <p>The meaning of the optional level value is discussed above
  233       in relation to the root logger. In addition however, the value
  234       INHERITED can be specified meaning that the named logger should
  235       inherit its level from the logger hierarchy.
  236   
  237       <p>If no level value is supplied, then the level of the
  238       named logger remains untouched.
  239   
  240       <p>By default categories inherit their level from the
  241       hierarchy. However, if you set the level of a logger and later
  242       decide that that logger should inherit its level, then you should
  243       specify INHERITED as the value for the level value. NULL is a
  244       synonym for INHERITED.
  245   
  246       <p>Similar to the root logger syntax, each <i>appenderName</i>
  247       (separated by commas) will be attached to the named logger.
  248   
  249       <p>See the <a href="../../../../manual.html#additivity">appender
  250       additivity rule</a> in the user manual for the meaning of the
  251       <code>additivity</code> flag.
  252   
  253       <h3>ObjectRenderers</h3>
  254   
  255       You can customize the way message objects of a given type are
  256       converted to String before being logged. This is done by
  257       specifying an {@link org.apache.log4j.or.ObjectRenderer ObjectRenderer}
  258       for the object type would like to customize.
  259   
  260       <p>The syntax is:
  261   
  262       <pre>
  263       log4j.renderer.fully.qualified.name.of.rendered.class=fully.qualified.name.of.rendering.class
  264       </pre>
  265   
  266       As in,
  267       <pre>
  268       log4j.renderer.my.Fruit=my.FruitRenderer
  269       </pre>
  270   
  271      <h3>ThrowableRenderer</h3>
  272   
  273      You can customize the way an instance of Throwable is
  274      converted to String before being logged. This is done by
  275      specifying an {@link org.apache.log4j.spi.ThrowableRenderer ThrowableRenderer}.
  276   
  277      <p>The syntax is:
  278   
  279      <pre>
  280      log4j.throwableRenderer=fully.qualified.name.of.rendering.class
  281      log4j.throwableRenderer.paramName=paramValue
  282      </pre>
  283   
  284      As in,
  285      <pre>
  286      log4j.throwableRenderer=org.apache.log4j.EnhancedThrowableRenderer
  287      </pre>
  288   
  289       <h3>Logger Factories</h3>
  290   
  291       The usage of custom logger factories is discouraged and no longer
  292       documented.
  293   
  294       <h3>Resetting Hierarchy</h3>
  295   
  296       The hierarchy will be reset before configuration when
  297       log4j.reset=true is present in the properties file.
  298   
  299       <h3>Example</h3>
  300   
  301       <p>An example configuration is given below. Other configuration
  302       file examples are given in the <code>examples</code> folder.
  303   
  304       <pre>
  305   
  306       # Set options for appender named "A1".
  307       # Appender "A1" will be a SyslogAppender
  308       log4j.appender.A1=org.apache.log4j.net.SyslogAppender
  309   
  310       # The syslog daemon resides on www.abc.net
  311       log4j.appender.A1.SyslogHost=www.abc.net
  312   
  313       # A1's layout is a PatternLayout, using the conversion pattern
  314       # <b>%r %-5p %c{2} %M.%L %x - %m\n</b>. Thus, the log output will
  315       # include # the relative time since the start of the application in
  316       # milliseconds, followed by the level of the log request,
  317       # followed by the two rightmost components of the logger name,
  318       # followed by the callers method name, followed by the line number,
  319       # the nested disgnostic context and finally the message itself.
  320       # Refer to the documentation of {@link PatternLayout} for further information
  321       # on the syntax of the ConversionPattern key.
  322       log4j.appender.A1.layout=org.apache.log4j.PatternLayout
  323       log4j.appender.A1.layout.ConversionPattern=%-4r %-5p %c{2} %M.%L %x - %m\n
  324   
  325       # Set options for appender named "A2"
  326       # A2 should be a RollingFileAppender, with maximum file size of 10 MB
  327       # using at most one backup file. A2's layout is TTCC, using the
  328       # ISO8061 date format with context printing enabled.
  329       log4j.appender.A2=org.apache.log4j.RollingFileAppender
  330       log4j.appender.A2.MaxFileSize=10MB
  331       log4j.appender.A2.MaxBackupIndex=1
  332       log4j.appender.A2.layout=org.apache.log4j.TTCCLayout
  333       log4j.appender.A2.layout.ContextPrinting=enabled
  334       log4j.appender.A2.layout.DateFormat=ISO8601
  335   
  336       # Root logger set to DEBUG using the A2 appender defined above.
  337       log4j.rootLogger=DEBUG, A2
  338   
  339       # Logger definitions:
  340       # The SECURITY logger inherits is level from root. However, it's output
  341       # will go to A1 appender defined above. It's additivity is non-cumulative.
  342       log4j.logger.SECURITY=INHERIT, A1
  343       log4j.additivity.SECURITY=false
  344   
  345       # Only warnings or above will be logged for the logger "SECURITY.access".
  346       # Output will go to A1.
  347       log4j.logger.SECURITY.access=WARN
  348   
  349   
  350       # The logger "class.of.the.day" inherits its level from the
  351       # logger hierarchy.  Output will go to the appender's of the root
  352       # logger, A2 in this case.
  353       log4j.logger.class.of.the.day=INHERIT
  354       </pre>
  355   
  356       <p>Refer to the <b>setOption</b> method in each Appender and
  357       Layout for class specific options.
  358   
  359       <p>Use the <code>#</code> or <code>!</code> characters at the
  360       beginning of a line for comments.
  361   
  362      <p>
  363      @param configFileName The name of the configuration file where the
  364      configuration information is stored.
  365   
  366     */
  367     public
  368     void doConfigure(String configFileName, LoggerRepository hierarchy) {
  369       Properties props = new Properties();
  370       FileInputStream istream = null;
  371       try {
  372         istream = new FileInputStream(configFileName);
  373         props.load(istream);
  374         istream.close();
  375       }
  376       catch (Exception e) {
  377         if (e instanceof InterruptedIOException || e instanceof InterruptedException) {
  378             Thread.currentThread().interrupt();
  379         }
  380         LogLog.error("Could not read configuration file ["+configFileName+"].", e);
  381         LogLog.error("Ignoring configuration file [" + configFileName+"].");
  382         return;
  383       } finally {
  384           if(istream != null) {
  385               try {
  386                   istream.close();
  387               } catch(InterruptedIOException ignore) {
  388                   Thread.currentThread().interrupt();
  389               } catch(Throwable ignore) {
  390               }
  391   
  392           }
  393       }
  394       // If we reach here, then the config file is alright.
  395       doConfigure(props, hierarchy);
  396     }
  397   
  398     /**
  399      */
  400     static
  401     public
  402     void configure(String configFilename) {
  403       new PropertyConfigurator().doConfigure(configFilename,
  404   					   LogManager.getLoggerRepository());
  405     }
  406   
  407     /**
  408        Read configuration options from url <code>configURL</code>.
  409   
  410        @since 0.8.2
  411      */
  412     public
  413     static
  414     void configure(java.net.URL configURL) {
  415       new PropertyConfigurator().doConfigure(configURL,
  416   					   LogManager.getLoggerRepository());
  417     }
  418   
  419   
  420     /**
  421        Read configuration options from <code>properties</code>.
  422   
  423        See {@link #doConfigure(String, LoggerRepository)} for the expected format.
  424     */
  425     static
  426     public
  427     void configure(Properties properties) {
  428       new PropertyConfigurator().doConfigure(properties,
  429   					   LogManager.getLoggerRepository());
  430     }
  431   
  432     /**
  433        Like {@link #configureAndWatch(String, long)} except that the
  434        default delay as defined by {@link FileWatchdog#DEFAULT_DELAY} is
  435        used.
  436   
  437        @param configFilename A file in key=value format.
  438   
  439     */
  440     static
  441     public
  442     void configureAndWatch(String configFilename) {
  443       configureAndWatch(configFilename, FileWatchdog.DEFAULT_DELAY);
  444     }
  445   
  446   
  447     /**
  448        Read the configuration file <code>configFilename</code> if it
  449        exists. Moreover, a thread will be created that will periodically
  450        check if <code>configFilename</code> has been created or
  451        modified. The period is determined by the <code>delay</code>
  452        argument. If a change or file creation is detected, then
  453        <code>configFilename</code> is read to configure log4j.
  454   
  455         @param configFilename A file in key=value format.
  456         @param delay The delay in milliseconds to wait between each check.
  457     */
  458     static
  459     public
  460     void configureAndWatch(String configFilename, long delay) {
  461       PropertyWatchdog pdog = new PropertyWatchdog(configFilename);
  462       pdog.setDelay(delay);
  463       pdog.start();
  464     }
  465   
  466   
  467     /**
  468        Read configuration options from <code>properties</code>.
  469   
  470        See {@link #doConfigure(String, LoggerRepository)} for the expected format.
  471     */
  472     public
  473     void doConfigure(Properties properties, LoggerRepository hierarchy) {
  474   	repository = hierarchy;
  475       String value = properties.getProperty(LogLog.DEBUG_KEY);
  476       if(value == null) {
  477         value = properties.getProperty("log4j.configDebug");
  478         if(value != null)
  479   	LogLog.warn("[log4j.configDebug] is deprecated. Use [log4j.debug] instead.");
  480       }
  481   
  482       if(value != null) {
  483         LogLog.setInternalDebugging(OptionConverter.toBoolean(value, true));
  484       }
  485   
  486         //
  487         //   if log4j.reset=true then
  488         //        reset hierarchy
  489       String reset = properties.getProperty(RESET_KEY);
  490       if (reset != null && OptionConverter.toBoolean(reset, false)) {
  491             hierarchy.resetConfiguration();
  492       }
  493   
  494       String thresholdStr = OptionConverter.findAndSubst(THRESHOLD_PREFIX,
  495   						       properties);
  496       if(thresholdStr != null) {
  497         hierarchy.setThreshold(OptionConverter.toLevel(thresholdStr,
  498   						     (Level) Level.ALL));
  499         LogLog.debug("Hierarchy threshold set to ["+hierarchy.getThreshold()+"].");
  500       }
  501       
  502       configureRootCategory(properties, hierarchy);
  503       configureLoggerFactory(properties);
  504       parseCatsAndRenderers(properties, hierarchy);
  505   
  506       LogLog.debug("Finished configuring.");
  507       // We don't want to hold references to appenders preventing their
  508       // garbage collection.
  509       registry.clear();
  510     }
  511   
  512     /**
  513        Read configuration options from url <code>configURL</code>.
  514      */
  515     public
  516     void doConfigure(java.net.URL configURL, LoggerRepository hierarchy) {
  517       Properties props = new Properties();
  518       LogLog.debug("Reading configuration from URL " + configURL);
  519       InputStream istream = null;
  520       URLConnection uConn = null;
  521       try {
  522         uConn = configURL.openConnection();
  523         uConn.setUseCaches(false);
  524         istream = uConn.getInputStream();
  525         props.load(istream);
  526       }
  527       catch (Exception e) {
  528         if (e instanceof InterruptedIOException || e instanceof InterruptedException) {
  529             Thread.currentThread().interrupt();
  530         }
  531         LogLog.error("Could not read configuration file from URL [" + configURL
  532   		   + "].", e);
  533         LogLog.error("Ignoring configuration file [" + configURL +"].");
  534         return;
  535       }
  536       finally {
  537           if (istream != null) {
  538               try {
  539                   istream.close();
  540               } catch(InterruptedIOException ignore) {
  541                   Thread.currentThread().interrupt();
  542               } catch(IOException ignore) {
  543               } catch(RuntimeException ignore) {
  544               }
  545           }
  546       }
  547       doConfigure(props, hierarchy);
  548     }
  549   
  550   
  551     // --------------------------------------------------------------------------
  552     // Internal stuff
  553     // --------------------------------------------------------------------------
  554   
  555     /**
  556        Check the provided <code>Properties</code> object for a
  557        {@link org.apache.log4j.spi.LoggerFactory LoggerFactory}
  558        entry specified by {@link #LOGGER_FACTORY_KEY}.  If such an entry
  559        exists, an attempt is made to create an instance using the default
  560        constructor.  This instance is used for subsequent Category creations
  561        within this configurator.
  562   
  563        @see #parseCatsAndRenderers
  564      */
  565     protected void configureLoggerFactory(Properties props) {
  566       String factoryClassName = OptionConverter.findAndSubst(LOGGER_FACTORY_KEY,
  567   							   props);
  568       if(factoryClassName != null) {
  569         LogLog.debug("Setting category factory to ["+factoryClassName+"].");
  570         loggerFactory = (LoggerFactory)
  571   	          OptionConverter.instantiateByClassName(factoryClassName,
  572   							 LoggerFactory.class,
  573   							 loggerFactory);
  574         PropertySetter.setProperties(loggerFactory, props, FACTORY_PREFIX + ".");
  575       }
  576     }
  577   
  578     /*
  579     void configureOptionHandler(OptionHandler oh, String prefix,
  580   			      Properties props) {
  581       String[] options = oh.getOptionStrings();
  582       if(options == null)
  583         return;
  584   
  585       String value;
  586       for(int i = 0; i < options.length; i++) {
  587         value =  OptionConverter.findAndSubst(prefix + options[i], props);
  588         LogLog.debug(
  589            "Option " + options[i] + "=[" + (value == null? "N/A" : value)+"].");
  590         // Some option handlers assume that null value are not passed to them.
  591         // So don't remove this check
  592         if(value != null) {
  593   	oh.setOption(options[i], value);
  594         }
  595       }
  596       oh.activateOptions();
  597     }
  598     */
  599   
  600   
  601     void configureRootCategory(Properties props, LoggerRepository hierarchy) {
  602       String effectiveFrefix = ROOT_LOGGER_PREFIX;
  603       String value = OptionConverter.findAndSubst(ROOT_LOGGER_PREFIX, props);
  604   
  605       if(value == null) {
  606         value = OptionConverter.findAndSubst(ROOT_CATEGORY_PREFIX, props);
  607         effectiveFrefix = ROOT_CATEGORY_PREFIX;
  608       }
  609   
  610       if(value == null)
  611         LogLog.debug("Could not find root logger information. Is this OK?");
  612       else {
  613         Logger root = hierarchy.getRootLogger();
  614         synchronized(root) {
  615   	parseCategory(props, root, effectiveFrefix, INTERNAL_ROOT_NAME, value);
  616         }
  617       }
  618     }
  619   
  620   
  621     /**
  622        Parse non-root elements, such non-root categories and renderers.
  623     */
  624     protected
  625     void parseCatsAndRenderers(Properties props, LoggerRepository hierarchy) {
  626       Enumeration enumeration = props.propertyNames();
  627       while(enumeration.hasMoreElements()) {
  628         String key = (String) enumeration.nextElement();
  629         if(key.startsWith(CATEGORY_PREFIX) || key.startsWith(LOGGER_PREFIX)) {
  630   	String loggerName = null;
  631   	if(key.startsWith(CATEGORY_PREFIX)) {
  632   	  loggerName = key.substring(CATEGORY_PREFIX.length());
  633   	} else if(key.startsWith(LOGGER_PREFIX)) {
  634   	  loggerName = key.substring(LOGGER_PREFIX.length());
  635   	}
  636   	String value =  OptionConverter.findAndSubst(key, props);
  637   	Logger logger = hierarchy.getLogger(loggerName, loggerFactory);
  638   	synchronized(logger) {
  639   	  parseCategory(props, logger, key, loggerName, value);
  640   	  parseAdditivityForLogger(props, logger, loggerName);
  641   	}
  642         } else if(key.startsWith(RENDERER_PREFIX)) {
  643   	String renderedClass = key.substring(RENDERER_PREFIX.length());
  644   	String renderingClass = OptionConverter.findAndSubst(key, props);
  645   	if(hierarchy instanceof RendererSupport) {
  646   	  RendererMap.addRenderer((RendererSupport) hierarchy, renderedClass,
  647   				  renderingClass);
  648   	}
  649         } else if (key.equals(THROWABLE_RENDERER_PREFIX)) {
  650             if (hierarchy instanceof ThrowableRendererSupport) {
  651               ThrowableRenderer tr = (ThrowableRenderer)
  652                     OptionConverter.instantiateByKey(props,
  653                             THROWABLE_RENDERER_PREFIX,
  654                             org.apache.log4j.spi.ThrowableRenderer.class,
  655                             null);
  656               if(tr == null) {
  657                   LogLog.error(
  658                       "Could not instantiate throwableRenderer.");
  659               } else {
  660                   PropertySetter setter = new PropertySetter(tr);
  661                   setter.setProperties(props, THROWABLE_RENDERER_PREFIX + ".");
  662                   ((ThrowableRendererSupport) hierarchy).setThrowableRenderer(tr);
  663   
  664               }
  665             }
  666         }
  667       }
  668     }
  669   
  670     /**
  671        Parse the additivity option for a non-root category.
  672      */
  673     void parseAdditivityForLogger(Properties props, Logger cat,
  674   				  String loggerName) {
  675       String value = OptionConverter.findAndSubst(ADDITIVITY_PREFIX + loggerName,
  676   					     props);
  677       LogLog.debug("Handling "+ADDITIVITY_PREFIX + loggerName+"=["+value+"]");
  678       // touch additivity only if necessary
  679       if((value != null) && (!value.equals(""))) {
  680         boolean additivity = OptionConverter.toBoolean(value, true);
  681         LogLog.debug("Setting additivity for \""+loggerName+"\" to "+
  682   		   additivity);
  683         cat.setAdditivity(additivity);
  684       }
  685     }
  686   
  687     /**
  688        This method must work for the root category as well.
  689      */
  690     void parseCategory(Properties props, Logger logger, String optionKey,
  691   		     String loggerName, String value) {
  692   
  693       LogLog.debug("Parsing for [" +loggerName +"] with value=[" + value+"].");
  694       // We must skip over ',' but not white space
  695       StringTokenizer st = new StringTokenizer(value, ",");
  696   
  697       // If value is not in the form ", appender.." or "", then we should set
  698       // the level of the loggeregory.
  699   
  700       if(!(value.startsWith(",") || value.equals(""))) {
  701   
  702         // just to be on the safe side...
  703         if(!st.hasMoreTokens())
  704   	return;
  705   
  706         String levelStr = st.nextToken();
  707         LogLog.debug("Level token is [" + levelStr + "].");
  708   
  709         // If the level value is inherited, set category level value to
  710         // null. We also check that the user has not specified inherited for the
  711         // root category.
  712         if(INHERITED.equalsIgnoreCase(levelStr) || 
  713    	                                  NULL.equalsIgnoreCase(levelStr)) {
  714   	if(loggerName.equals(INTERNAL_ROOT_NAME)) {
  715   	  LogLog.warn("The root logger cannot be set to null.");
  716   	} else {
  717   	  logger.setLevel(null);
  718   	}
  719         } else {
  720   	logger.setLevel(OptionConverter.toLevel(levelStr, (Level) Level.DEBUG));
  721         }
  722         LogLog.debug("Category " + loggerName + " set to " + logger.getLevel());
  723       }
  724   
  725       // Begin by removing all existing appenders.
  726       logger.removeAllAppenders();
  727   
  728       Appender appender;
  729       String appenderName;
  730       while(st.hasMoreTokens()) {
  731         appenderName = st.nextToken().trim();
  732         if(appenderName == null || appenderName.equals(","))
  733   	continue;
  734         LogLog.debug("Parsing appender named \"" + appenderName +"\".");
  735         appender = parseAppender(props, appenderName);
  736         if(appender != null) {
  737   	logger.addAppender(appender);
  738         }
  739       }
  740     }
  741   
  742     Appender parseAppender(Properties props, String appenderName) {
  743       Appender appender = registryGet(appenderName);
  744       if((appender != null)) {
  745         LogLog.debug("Appender \"" + appenderName + "\" was already parsed.");
  746         return appender;
  747       }
  748       // Appender was not previously initialized.
  749       String prefix = APPENDER_PREFIX + appenderName;
  750       String layoutPrefix = prefix + ".layout";
  751   
  752       appender = (Appender) OptionConverter.instantiateByKey(props, prefix,
  753   					      org.apache.log4j.Appender.class,
  754   					      null);
  755       if(appender == null) {
  756         LogLog.error(
  757                 "Could not instantiate appender named \"" + appenderName+"\".");
  758         return null;
  759       }
  760       appender.setName(appenderName);
  761   
  762       if(appender instanceof OptionHandler) {
  763         if(appender.requiresLayout()) {
  764   	Layout layout = (Layout) OptionConverter.instantiateByKey(props,
  765   								  layoutPrefix,
  766   								  Layout.class,
  767   								  null);
  768   	if(layout != null) {
  769   	  appender.setLayout(layout);
  770   	  LogLog.debug("Parsing layout options for \"" + appenderName +"\".");
  771   	  //configureOptionHandler(layout, layoutPrefix + ".", props);
  772             PropertySetter.setProperties(layout, props, layoutPrefix + ".");
  773   	  LogLog.debug("End of parsing for \"" + appenderName +"\".");
  774   	}
  775         }
  776         final String errorHandlerPrefix = prefix + ".errorhandler";
  777         String errorHandlerClass = OptionConverter.findAndSubst(errorHandlerPrefix, props);
  778         if (errorHandlerClass != null) {
  779       		ErrorHandler eh = (ErrorHandler) OptionConverter.instantiateByKey(props,
  780   					  errorHandlerPrefix,
  781   					  ErrorHandler.class,
  782   					  null);
  783       		if (eh != null) {
  784       			  appender.setErrorHandler(eh);
  785       			  LogLog.debug("Parsing errorhandler options for \"" + appenderName +"\".");
  786       			  parseErrorHandler(eh, errorHandlerPrefix, props, repository);
  787       			  final Properties edited = new Properties();
  788       			  final String[] keys = new String[] { 
  789       					  errorHandlerPrefix + "." + ROOT_REF,
  790       					  errorHandlerPrefix + "." + LOGGER_REF,
  791       					  errorHandlerPrefix + "." + APPENDER_REF_TAG
  792       			  };
  793       			  for(Iterator iter = props.entrySet().iterator();iter.hasNext();) {
  794       				  Map.Entry entry = (Map.Entry) iter.next();
  795       				  int i = 0;
  796       				  for(; i < keys.length; i++) {
  797       					  if(keys[i].equals(entry.getKey())) break;
  798       				  }
  799       				  if (i == keys.length) {
  800       					  edited.put(entry.getKey(), entry.getValue());
  801       				  }
  802       			  }
  803       		      PropertySetter.setProperties(eh, edited, errorHandlerPrefix + ".");
  804       			  LogLog.debug("End of errorhandler parsing for \"" + appenderName +"\".");
  805       		}
  806       	  
  807         }
  808         //configureOptionHandler((OptionHandler) appender, prefix + ".", props);
  809         PropertySetter.setProperties(appender, props, prefix + ".");
  810         LogLog.debug("Parsed \"" + appenderName +"\" options.");
  811       }
  812       parseAppenderFilters(props, appenderName, appender);
  813       registryPut(appender);
  814       return appender;
  815     }
  816     
  817     private void parseErrorHandler(
  818   		  final ErrorHandler eh,
  819   		  final String errorHandlerPrefix,
  820   		  final Properties props, 
  821   		  final LoggerRepository hierarchy) {
  822   		boolean rootRef = OptionConverter.toBoolean(
  823   					  OptionConverter.findAndSubst(errorHandlerPrefix + ROOT_REF, props), false);
  824   		if (rootRef) {
  825   				  eh.setLogger(hierarchy.getRootLogger());
  826   	    }
  827   		String loggerName = OptionConverter.findAndSubst(errorHandlerPrefix + LOGGER_REF , props);
  828   		if (loggerName != null) {
  829   			Logger logger = (loggerFactory == null) ? hierarchy.getLogger(loggerName)
  830   			                : hierarchy.getLogger(loggerName, loggerFactory);
  831   			eh.setLogger(logger);
  832   		}
  833   		String appenderName = OptionConverter.findAndSubst(errorHandlerPrefix + APPENDER_REF_TAG, props);
  834   		if (appenderName != null) {
  835   			Appender backup = parseAppender(props, appenderName);
  836   			if (backup != null) {
  837   				eh.setBackupAppender(backup);
  838   			}
  839   		}
  840     }
  841   				
  842     
  843     void parseAppenderFilters(Properties props, String appenderName, Appender appender) {
  844       // extract filters and filter options from props into a hashtable mapping
  845       // the property name defining the filter class to a list of pre-parsed
  846       // name-value pairs associated to that filter
  847       final String filterPrefix = APPENDER_PREFIX + appenderName + ".filter.";
  848       int fIdx = filterPrefix.length();
  849       Hashtable filters = new Hashtable();
  850       Enumeration e = props.keys();
  851       String name = "";
  852       while (e.hasMoreElements()) {
  853         String key = (String) e.nextElement();
  854         if (key.startsWith(filterPrefix)) {
  855           int dotIdx = key.indexOf('.', fIdx);
  856           String filterKey = key;
  857           if (dotIdx != -1) {
  858             filterKey = key.substring(0, dotIdx);
  859             name = key.substring(dotIdx+1);
  860           }
  861           Vector filterOpts = (Vector) filters.get(filterKey);
  862           if (filterOpts == null) {
  863             filterOpts = new Vector();
  864             filters.put(filterKey, filterOpts);
  865           }
  866           if (dotIdx != -1) {
  867             String value = OptionConverter.findAndSubst(key, props);
  868             filterOpts.add(new NameValue(name, value));
  869           }
  870         }
  871       }
  872   
  873       // sort filters by IDs, insantiate filters, set filter options,
  874       // add filters to the appender
  875       Enumeration g = new SortedKeyEnumeration(filters);
  876       while (g.hasMoreElements()) {
  877         String key = (String) g.nextElement();
  878         String clazz = props.getProperty(key);
  879         if (clazz != null) {
  880           LogLog.debug("Filter key: ["+key+"] class: ["+props.getProperty(key) +"] props: "+filters.get(key));
  881           Filter filter = (Filter) OptionConverter.instantiateByClassName(clazz, Filter.class, null);
  882           if (filter != null) {
  883             PropertySetter propSetter = new PropertySetter(filter);
  884             Vector v = (Vector)filters.get(key);
  885             Enumeration filterProps = v.elements();
  886             while (filterProps.hasMoreElements()) {
  887               NameValue kv = (NameValue)filterProps.nextElement();
  888               propSetter.setProperty(kv.key, kv.value);
  889             }
  890             propSetter.activate();
  891             LogLog.debug("Adding filter of type ["+filter.getClass()
  892              +"] to appender named ["+appender.getName()+"].");
  893             appender.addFilter(filter);
  894           }
  895         } else {
  896           LogLog.warn("Missing class definition for filter: ["+key+"]");
  897         }
  898       }
  899     }
  900   
  901   
  902     void  registryPut(Appender appender) {
  903       registry.put(appender.getName(), appender);
  904     }
  905   
  906     Appender registryGet(String name) {
  907       return (Appender) registry.get(name);
  908     }
  909   }
  910   
  911   class PropertyWatchdog extends FileWatchdog {
  912   
  913     PropertyWatchdog(String filename) {
  914       super(filename);
  915     }
  916   
  917     /**
  918        Call {@link PropertyConfigurator#configure(String)} with the
  919        <code>filename</code> to reconfigure log4j. */
  920     public
  921     void doOnChange() {
  922       new PropertyConfigurator().doConfigure(filename,
  923   					   LogManager.getLoggerRepository());
  924     }
  925   }
  926   
  927   class NameValue {
  928     String key, value;
  929     public NameValue(String key, String value) {
  930       this.key = key;
  931       this.value = value;
  932     }
  933     public String toString() {
  934       return key + "=" + value;
  935     }
  936   }
  937   
  938   class SortedKeyEnumeration implements Enumeration {
  939   
  940     private Enumeration e;
  941   
  942     public SortedKeyEnumeration(Hashtable ht) {
  943       Enumeration f = ht.keys();
  944       Vector keys = new Vector(ht.size());
  945       for (int i, last = 0; f.hasMoreElements(); ++last) {
  946         String key = (String) f.nextElement();
  947         for (i = 0; i < last; ++i) {
  948           String s = (String) keys.get(i);
  949           if (key.compareTo(s) <= 0) break;
  950         }
  951         keys.add(i, key);
  952       }
  953       e = keys.elements();
  954     }
  955   
  956     public boolean hasMoreElements() {
  957       return e.hasMoreElements();
  958     }
  959   
  960     public Object nextElement() {
  961       return e.nextElement();
  962     }
  963   }

Save This Page
Home » apache-log4j-1.2.16 » org.apache » log4j » [javadoc | source]