You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by ih...@apache.org on 2011/12/13 15:30:51 UTC

svn commit: r1213710 - in /logging/log4php/trunk/src: main/php/ main/php/configurators/ site/xdoc/docs/ test/php/ test/php/filters/

Author: ihabunek
Date: Tue Dec 13 14:30:50 2011
New Revision: 1213710

URL: http://svn.apache.org/viewvc?rev=1213710&view=rev
Log:
Reintroduced the possibility of using a custom configurator which was lost when rewriting the configurator (LOG4PHP-152). Also updated docs for configuration.

Added:
    logging/log4php/trunk/src/main/php/configurators/LoggerConfiguratorDefault.php
Modified:
    logging/log4php/trunk/src/main/php/Logger.php
    logging/log4php/trunk/src/main/php/LoggerConfigurator.php
    logging/log4php/trunk/src/site/xdoc/docs/configuration.xml
    logging/log4php/trunk/src/test/php/LoggerConfiguratorTest.php
    logging/log4php/trunk/src/test/php/filters/LoggerFilterDenyAllTest.php

Modified: logging/log4php/trunk/src/main/php/Logger.php
URL: http://svn.apache.org/viewvc/logging/log4php/trunk/src/main/php/Logger.php?rev=1213710&r1=1213709&r2=1213710&view=diff
==============================================================================
--- logging/log4php/trunk/src/main/php/Logger.php (original)
+++ logging/log4php/trunk/src/main/php/Logger.php Tue Dec 13 14:30:50 2011
@@ -54,6 +54,7 @@ class Logger {
 		'LoggerReflectionUtils' => '/LoggerReflectionUtils.php',
 		'LoggerConfigurable' => '/LoggerConfigurable.php',
 		'LoggerConfigurator' => '/LoggerConfigurator.php',
+		'LoggerConfiguratorDefault' => '/configurators/LoggerConfiguratorDefault.php',
 		'LoggerConfigurationAdapter' => '/configurators/LoggerConfigurationAdapter.php',
 		'LoggerConfigurationAdapterINI' => '/configurators/LoggerConfigurationAdapterINI.php',
 		'LoggerConfigurationAdapterXML' => '/configurators/LoggerConfigurationAdapterXML.php',
@@ -558,15 +559,46 @@ class Logger {
 	 *
 	 * @param string|array $configuration Either a path to the configuration
 	 *   file, or a configuration array.
+	 *   
+	 * @param mixed $configuratorClass A custom configurator class: either a 
+	 * class name (string), or an object which implements LoggerConfigurator
+	 * interface. If left empty, the default configurator will be used. 
 	 */
-	public static function configure($configuration = null) {
+	public static function configure($configuration = null, $configuratorClass = null) {
 		self::resetConfiguration();
-		$configurator = new LoggerConfigurator();
+		$configurator = self::getConfigurator($configuratorClass);
 		$configurator->configure(self::getHierarchy(), $configuration);
 		self::$initialized = true;
 	}
 	
 	/**
+	 * Creates a logger configurator instance based on the provided 
+	 * configurator class. If no class is given, returns an instance of
+	 * the default configurator.
+	 * 
+	 * @param string $configuratorClass The configurator class.
+	 */
+	private static function getConfigurator($configuratorClass = null) {
+		if (empty($configuratorClass)) {
+			return new LoggerConfiguratorDefault();
+		}
+		
+		if (!class_exists($configuratorClass)) {
+			$this->warn("Specified configurator class [$configuratorClass] does not exist. Reverting to default configurator.");
+			return new LoggerConfiguratorDefault();
+		}
+		
+		$configurator = new $configuratorClass();
+			
+		if (!($configurator instanceof LoggerConfigurator)) {
+			$this->warn("Specified configurator class [$configuratorClass] does not implement the LoggerConfigurator interface. Reverting to default configurator.");
+			return new LoggerConfiguratorDefault();
+		}
+		
+		return $configurator;
+	}
+	
+	/**
 	 * Returns true if the log4php framework has been initialized.
 	 * @return boolean 
 	 */

Modified: logging/log4php/trunk/src/main/php/LoggerConfigurator.php
URL: http://svn.apache.org/viewvc/logging/log4php/trunk/src/main/php/LoggerConfigurator.php?rev=1213710&r1=1213709&r2=1213710&view=diff
==============================================================================
--- logging/log4php/trunk/src/main/php/LoggerConfigurator.php (original)
+++ logging/log4php/trunk/src/main/php/LoggerConfigurator.php Tue Dec 13 14:30:50 2011
@@ -19,465 +19,24 @@
  */
 
 /**
- * Configures log4php based on a provided configuration file or array.
+ * Interface for logger configurators.
  * 
  * @package log4php
  * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
  * @version $Revision$
  * @since 2.2
  */
-class LoggerConfigurator
+interface LoggerConfigurator
 {
-	/** XML configuration file format. */
-	const FORMAT_XML = 'xml';
-	
-	/** PHP configuration file format. */
-	const FORMAT_PHP = 'php';
-	
-	/** INI (properties) configuration file format. */
-	const FORMAT_INI = 'ini';
-
-	/** Defines which adapter should be used for parsing which format. */
-	private $adapters = array(
-		self::FORMAT_XML => 'LoggerConfigurationAdapterXML',
-		self::FORMAT_INI => 'LoggerConfigurationAdapterINI',
-		self::FORMAT_PHP => 'LoggerConfigurationAdapterPHP',
-	);
-	
-	/** Default configuration; used if no configuration file is provided. */
-	private static $defaultConfiguration = array(
-        'threshold' => 'ALL',
-        'rootLogger' => array(
-            'level' => 'DEBUG',
-            'appenders' => array('default'),
-        ),
-        'appenders' => array(
-            'default' => array(
-                'class' => 'LoggerAppenderEcho',
-                'layout' => array(
-                    'class' => 'LoggerLayoutTTCC',
-                ),
-            ),
-        ),
-	);
-	
-	/** Holds the appenders before they are linked to loggers. */
-	private $appenders = array();
-	
 	/**
-	 * Configures log4php based on the given configuration. The input can 
-	 * either be a path to the config file, or a PHP array holding the 
-	 * configuration. 
+	 * Configures log4php based on the given configuration. 
 	 * 
-	 * If no configuration is given, or if the given configuration cannot be
-	 * parsed for whatever reason, a warning will be issued, and log4php
-	 * will use the default configuration contained in 
-	 * {@link $defaultConfiguration}.
+	 * All configurators implementations must implement this interface.
 	 * 
 	 * @param LoggerHierarchy $hierarchy The hierarchy on which to perform 
 	 * 		the configuration. 
-	 * @param string|array $input Either path to the config file or the 
-	 * 		configuration as an array. If not set, default configuration 
-	 * 		will be used.
-	 */
-	public function configure(LoggerHierarchy $hierarchy, $input = null) {
-		$config = $this->parse($input);
-		$this->doConfigure($hierarchy, $config);
-	}
-	
-	/**
-	 * Parses the given configuration and returns the parsed configuration
-	 * as a PHP array. Does not perform any configuration. 
-	 * 
-	 * If no configuration is given, or if the given configuration cannot be
-	 * parsed for whatever reason, a warning will be issued, and the default 
-	 * configuration will be returned ({@link $defaultConfiguration}).
-	 * 
-	 * @param string|array $input Either path to the config file or the 
-	 * 		configuration as an array. If not set, default configuration 
-	 * 		will be used.
-	 * @return array The parsed configuration.
-	 */
-	public function parse($input)
-	{
-		// No input - use default configuration
-		if (!isset($input)) {
-			$config = self::$defaultConfiguration;
-		}
-		
-		// Array input - contains configuration within the array
-		else if (is_array($input)) {
-			$config = $input;
-		}
-		
-		// String input - contains path to configuration file
-		else if (is_string($input)) {
-			try {
-				$config = $this->parseFile($input);
-			} catch (LoggerException $e) {
-				$this->warn("Configuration failed. " . $e->getMessage() . " Using default configuration.");
-				$config = self::$defaultConfiguration;
-			}
-		}
-		
-		// Anything else is an error
-		else {
-			$this->warn("Invalid configuration param given. Reverting to default configuration.");
-			$config = self::$defaultConfiguration;
-		}
-		
-		return $config;
-	}
-
-	/** 
-	 * Returns the default log4php configuration.
-	 * @return array
-	 */
-	public static function getDefaultConfiguration() {
-		return self::$defaultConfiguration;
-	} 
-	
-	/**
-	 * Loads the configuration file from the given URL, determines which
-	 * adapter to use, converts the configuration to a PHP array and
-	 * returns it.
-	 *
-	 * @param string $url Path to the config file.
-	 * @return The configuration from the config file, as a PHP array.
-	 * @throws LoggerException If the configuration file cannot be loaded, or
-	 * 		if the parsing fails.
-	 */
-	private function parseFile($url) {
-		
-		if (!file_exists($url)) {
-			throw new LoggerException("File not found at [$url].");
-		}
-		
-		$type = $this->getConfigType($url);
-		$adapterClass = $this->adapters[$type];
-
-		$adapter = new $adapterClass();
-		return $adapter->convert($url);
-	}
-	
-	/** Determines configuration file type based on the file extension. */
-	private function getConfigType($url) {
-		$info = pathinfo($url);
-		$ext = strtolower($info['extension']);
-		
-		switch($ext) {
-			case 'xml':
-				return self::FORMAT_XML;
-			
-			case 'ini':
-			case 'properties':
-				return self::FORMAT_INI;
-			
-			case 'php':
-				return self::FORMAT_PHP;
-				
-			default:
-				throw new LoggerException("Unsupported configuration file extension: $ext");
-		}
-	}
-	
-	/**
-	 * Constructs the logger hierarchy based on configuration.
-	 * 
-	 * @param LoggerHierarchy $hierarchy
-	 * @param array $config
-	 */
-	private function doConfigure(LoggerHierarchy $hierarchy, $config) {
-		if (isset($config['threshold'])) {
-			$threshold = LoggerLevel::toLevel($config['threshold']);
-			if (isset($threshold)) {
-				$hierarchy->setThreshold($threshold);
-			} else {
-				$this->warn("Invalid threshold value [{$config['threshold']}] specified. Ignoring threshold definition.");
-			}
-		}
-		
-		// Configure appenders and add them to the appender pool
-		if (isset($config['appenders']) && is_array($config['appenders'])) {
-			foreach($config['appenders'] as $name => $appenderConfig) {
-				$this->configureAppender($name, $appenderConfig);
-			}
-		}
-		
-		// Configure root logger 
-		if (isset($config['rootLogger'])) {
-			$this->configureRootLogger($hierarchy, $config['rootLogger']);
-		}
-		
-		// Configure loggers
-		if (isset($config['loggers']) && is_array($config['loggers'])) {
-			foreach($config['loggers'] as $loggerName => $loggerConfig) {
-				$this->configureOtherLogger($hierarchy, $loggerName, $loggerConfig);
-			}
-		}
-
-		// Configure renderers
-		if (isset($config['renderers']) && is_array($config['renderers'])) {
-			foreach($config['renderers'] as $rendererConfig) {
-				$this->configureRenderer($hierarchy, $rendererConfig);
-			}
-		}
-	}
-	
-	private function configureRenderer(LoggerHierarchy $hierarchy, $config) {
-		if (!isset($config['renderingClass'])) {
-			$this->warn("Rendering class not specified. Skipping renderers definition.");
-			return;			
-		}
-		
-		$renderingClass = $config['renderingClass'];
-		if (!class_exists($renderingClass)) {
-			$this->warn("Nonexistant rendering class [$renderingClass] specified. Skipping renderers definition.");
-			return;
-		}
-		
-		$renderingClassInstance = new $renderingClass();
-		if (!$renderingClassInstance instanceof LoggerRendererObject) {
-			$this->warn("Invalid class [$renderingClass] given. Not a valid LoggerRenderer class. Skipping renderers definition.");
-			return;			
-		}
-	
-		if (!isset($config['renderedClass'])) {
-			$this->warn("Rendered class not specified for rendering Class [$renderingClass]. Skipping renderers definition.");
-			return;			
-		}
-		
-		$renderedClass = $config['renderedClass'];
-		if (!class_exists($renderedClass)) {
-			$this->warn("Nonexistant rendered class [$renderedClass] specified for renderer [$renderingClass]. Skipping renderers definition.");
-			return;
-		}		
-
-		$hierarchy->getRendererMap()->addRenderer($renderedClass, $renderingClassInstance);
-	}
-	
-	/** 
-	 * Configures an appender based on given config and saves it to 
-	 * {@link $appenders} array so it can be later linked to loggers. 
-	 * @param string $name Appender name. 
-	 * @param array $config Appender configuration options.
-	 */
-	private function configureAppender($name, $config) {
-
-		// TODO: add this check to other places where it might be useful
-		if (!is_array($config)) {
-			$type = gettype($config);
-			$this->warn("Invalid configuration provided for appender [$name]. Expected an array, found <$type>. Skipping appender definition.");
-			return;
-		}
-		
-		// Parse appender class
-		$class = $config['class'];
-		if (empty($class)) {
-			$this->warn("No class given for appender [$name]. Skipping appender definition.");
-			return;
-		}
-		if (!class_exists($class)) {
-			$this->warn("Invalid class [$class] given for appender [$name]. Class does not exist. Skipping appender definition.");
-			return;
-		}
-		
-		// Instantiate the appender
-		$appender = new $class($name);
-		if (!($appender instanceof LoggerAppender)) {
-			$this->warn("Invalid class [$class] given for appender [$name]. Not a valid LoggerAppender class. Skipping appender definition.");
-			return;
-		}
-		
-		// Parse the appender threshold
-		if (isset($config['threshold'])) {
-			$threshold = LoggerLevel::toLevel($config['threshold']);
-			if ($threshold instanceof LoggerLevel) {
-				$appender->setThreshold($threshold);
-			} else {
-				$this->warn("Invalid threshold value [{$config['threshold']}] specified for appender [$name]. Ignoring threshold definition.");
-			}
-		}
-		
-		// Parse the appender layout
-		if ($appender->requiresLayout() && isset($config['layout'])) {
-			$this->createAppenderLayout($appender, $config['layout']);
-		}
-		
-		// Parse filters
-		if (isset($config['filters']) && is_array($config['filters'])) {
-			foreach($config['filters'] as $filterConfig) {
-				$this->createAppenderFilter($appender, $filterConfig);
-			}
-		}
-		
-		// Set options if any
-		if (isset($config['params'])) {
-			$this->setOptions($appender, $config['params']);
-		}
-
-		// Activate and save for later linking to loggers
-		$appender->activateOptions();
-		$this->appenders[$name] = $appender;
-	}
-	
-	/**
-	 * Parses layout config, creates the layout and links it to the appender.
-	 * @param LoggerAppender $appender
-	 * @param array $config Layout configuration.
-	 */
-	private function createAppenderLayout(LoggerAppender $appender, $config) {
-		$name = $appender->getName();
-		$class = $config['class'];
-		if (empty($class)) {
-			$this->warn("Layout class not specified for appender [$name]. Reverting to default layout.");
-			return;
-		}
-		if (!class_exists($class)) {
-			$this->warn("Nonexistant layout class [$class] specified for appender [$name]. Reverting to default layout.");
-			return;
-		}
-		
-		$layout = new $class();
-		if (!($layout instanceof LoggerLayout)) {
-			$this->warn("Invalid layout class [$class] sepcified for appender [$name]. Reverting to default layout.");
-			return;
-		}
-		
-		if (isset($config['params'])) {
-			$this->setOptions($layout, $config['params']);
-		}
-		
-		$layout->activateOptions();
-		$appender->setLayout($layout);
-	}
-	
-	/**
-	 * Parses filter config, creates the filter and adds it to the appender's 
-	 * filter chain.
-	 * @param LoggerAppender $appender
-	 * @param array $config Filter configuration.
-	 */
-	private function createAppenderFilter(LoggerAppender $appender, $config) {
-		$name = $appender->getName();
-		$class = $config['class'];
-		if (!class_exists($class)) {
-			$this->warn("Nonexistant filter class [$class] specified on appender [$name]. Skipping filter definition.");
-			return;
-		}
-	
-		$filter = new $class();
-		if (!($filter instanceof LoggerFilter)) {
-			$this->warn("Invalid filter class [$class] sepcified on appender [$name]. Skipping filter definition.");
-			return;
-		}
-	
-		if (isset($config['params'])) {
-			$this->setOptions($filter, $config['params']);
-		}
-	
-		$filter->activateOptions();
-		$appender->addFilter($filter);
-	}
-	
-	/** 
-	 * Configures the root logger
-	 * @see configureLogger() 
-	 */
-	private function configureRootLogger(LoggerHierarchy $hierarchy, $config) {
-		$logger = $hierarchy->getRootLogger();
-		$this->configureLogger($logger, $config);
-	}
-
-	/**
-	 * Configures a logger which is not root.
-	 * @see configureLogger()
-	 */
-	private function configureOtherLogger(LoggerHierarchy $hierarchy, $name, $config) {
-		// Get logger from hierarchy (this creates it if it doesn't already exist)
-		$logger = $hierarchy->getLogger($name);
-		$this->configureLogger($logger, $config);
-	}
-	
-	/**
-	 * Configures a logger. 
-	 * 
-	 * @param Logger $logger The logger to configure
-	 * @param array $config Logger configuration options.
-	 */
-	private function configureLogger(Logger $logger, $config) {
-		$loggerName = $logger->getName();
-		
-		// Set logger level
-		if (isset($config['level'])) {
-			$level = LoggerLevel::toLevel($config['level']);
-			if (isset($level)) {
-				$logger->setLevel($level);
-			} else {
-				$default = $logger->getLevel();
-				$this->warn("Invalid level value [{$config['level']}] specified for logger [$loggerName]. Ignoring level definition.");
-			}
-		}
-		
-		// Link appenders to logger
-		if (isset($config['appenders'])) {
-			foreach($config['appenders'] as $appenderName) {
-				if (isset($this->appenders[$appenderName])) {
-					$logger->addAppender($this->appenders[$appenderName]);
-				} else {
-					$this->warn("Nonexistnant appender [$appenderName] linked to logger [$loggerName].");
-				}
-			}
-		}
-		
-		// Set logger additivity
-		if (isset($config['additivity'])) {
-			$additivity = LoggerOptionConverter::toBoolean($config['additivity'], null);
-			if (is_bool($additivity)) {
-				$logger->setAdditivity($additivity);
-			} else {
-				$this->warn("Invalid additivity value [{$config['additivity']}] specified for logger [$loggerName]. Ignoring additivity setting.");
-			}
-		}
-	}
-
-	/**
-	 * Helper method which applies given options to an object which has setters
-	 * for these options (such as appenders, layouts, etc.).
-	 * 
-	 * For example, if options are:
-	 * <code>
-	 * array(
-	 * 	'file' => '/tmp/myfile.log',
-	 * 	'append' => true
-	 * )
-	 * </code>
-	 * 
-	 * This method will call:
-	 * <code>
-	 * $object->setFile('/tmp/myfile.log')
-	 * $object->setAppend(true)
-	 * </code>
-	 * 
-	 * If required setters do not exist, it will produce a warning. 
-	 * 
-	 * @param mixed $object The object to configure.
-	 * @param unknown_type $options
+	 * @param mixed $input Either path to the config file or the 
+	 * 		configuration as an array.
 	 */
-	private function setOptions($object, $options) {
-		foreach($options as $name => $value) {
-			$setter = "set$name";
-			if (method_exists($object, $setter)) {
-				$object->$setter($value);
-			} else {
-				$class = get_class($object);
-				$this->warn("Nonexistant option [$name] specified on [$class]. Skipping.");
-			}
-		}
-	}
-	
-	/** Helper method to simplify error reporting. */
-	private function warn($message) {
-		trigger_error("log4php: $message", E_USER_WARNING);
-	}
+	public function configure(LoggerHierarchy $hierarchy, $input = null);
 }
\ No newline at end of file

Added: logging/log4php/trunk/src/main/php/configurators/LoggerConfiguratorDefault.php
URL: http://svn.apache.org/viewvc/logging/log4php/trunk/src/main/php/configurators/LoggerConfiguratorDefault.php?rev=1213710&view=auto
==============================================================================
--- logging/log4php/trunk/src/main/php/configurators/LoggerConfiguratorDefault.php (added)
+++ logging/log4php/trunk/src/main/php/configurators/LoggerConfiguratorDefault.php Tue Dec 13 14:30:50 2011
@@ -0,0 +1,485 @@
+<?php
+/**
+ * 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 log4php
+ */
+
+/**
+ * Default implementation of the logger configurator.
+ * 
+ * Configures log4php based on a provided configuration file or array.
+ * 
+ * @package log4php
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
+ * @version $Revision: 1212319 $
+ * @since 2.2
+ */
+class LoggerConfiguratorDefault implements LoggerConfigurator
+{
+	/** XML configuration file format. */
+	const FORMAT_XML = 'xml';
+	
+	/** PHP configuration file format. */
+	const FORMAT_PHP = 'php';
+	
+	/** INI (properties) configuration file format. */
+	const FORMAT_INI = 'ini';
+
+	/** Defines which adapter should be used for parsing which format. */
+	private $adapters = array(
+		self::FORMAT_XML => 'LoggerConfigurationAdapterXML',
+		self::FORMAT_INI => 'LoggerConfigurationAdapterINI',
+		self::FORMAT_PHP => 'LoggerConfigurationAdapterPHP',
+	);
+	
+	/** Default configuration; used if no configuration file is provided. */
+	private static $defaultConfiguration = array(
+        'threshold' => 'ALL',
+        'rootLogger' => array(
+            'level' => 'DEBUG',
+            'appenders' => array('default'),
+        ),
+        'appenders' => array(
+            'default' => array(
+                'class' => 'LoggerAppenderEcho',
+                'layout' => array(
+                    'class' => 'LoggerLayoutTTCC',
+                ),
+            ),
+        ),
+	);
+	
+	/** Holds the appenders before they are linked to loggers. */
+	private $appenders = array();
+	
+	/**
+	 * Configures log4php based on the given configuration. The input can 
+	 * either be a path to the config file, or a PHP array holding the 
+	 * configuration. 
+	 * 
+	 * If no configuration is given, or if the given configuration cannot be
+	 * parsed for whatever reason, a warning will be issued, and log4php
+	 * will use the default configuration contained in 
+	 * {@link $defaultConfiguration}.
+	 * 
+	 * @param LoggerHierarchy $hierarchy The hierarchy on which to perform 
+	 * 		the configuration. 
+	 * @param string|array $input Either path to the config file or the 
+	 * 		configuration as an array. If not set, default configuration 
+	 * 		will be used.
+	 */
+	public function configure(LoggerHierarchy $hierarchy, $input = null) {
+		$config = $this->parse($input);
+		$this->doConfigure($hierarchy, $config);
+	}
+	
+	/**
+	 * Parses the given configuration and returns the parsed configuration
+	 * as a PHP array. Does not perform any configuration. 
+	 * 
+	 * If no configuration is given, or if the given configuration cannot be
+	 * parsed for whatever reason, a warning will be issued, and the default 
+	 * configuration will be returned ({@link $defaultConfiguration}).
+	 * 
+	 * @param string|array $input Either path to the config file or the 
+	 * 		configuration as an array. If not set, default configuration 
+	 * 		will be used.
+	 * @return array The parsed configuration.
+	 */
+	public function parse($input)
+	{
+		// No input - use default configuration
+		if (!isset($input)) {
+			$config = self::$defaultConfiguration;
+		}
+		
+		// Array input - contains configuration within the array
+		else if (is_array($input)) {
+			$config = $input;
+		}
+		
+		// String input - contains path to configuration file
+		else if (is_string($input)) {
+			try {
+				$config = $this->parseFile($input);
+			} catch (LoggerException $e) {
+				$this->warn("Configuration failed. " . $e->getMessage() . " Using default configuration.");
+				$config = self::$defaultConfiguration;
+			}
+		}
+		
+		// Anything else is an error
+		else {
+			$this->warn("Invalid configuration param given. Reverting to default configuration.");
+			$config = self::$defaultConfiguration;
+		}
+		
+		return $config;
+	}
+
+	/** 
+	 * Returns the default log4php configuration.
+	 * @return array
+	 */
+	public static function getDefaultConfiguration() {
+		return self::$defaultConfiguration;
+	} 
+	
+	/**
+	 * Loads the configuration file from the given URL, determines which
+	 * adapter to use, converts the configuration to a PHP array and
+	 * returns it.
+	 *
+	 * @param string $url Path to the config file.
+	 * @return The configuration from the config file, as a PHP array.
+	 * @throws LoggerException If the configuration file cannot be loaded, or
+	 * 		if the parsing fails.
+	 */
+	private function parseFile($url) {
+		
+		if (!file_exists($url)) {
+			throw new LoggerException("File not found at [$url].");
+		}
+		
+		$type = $this->getConfigType($url);
+		$adapterClass = $this->adapters[$type];
+
+		$adapter = new $adapterClass();
+		return $adapter->convert($url);
+	}
+	
+	/** Determines configuration file type based on the file extension. */
+	private function getConfigType($url) {
+		$info = pathinfo($url);
+		$ext = strtolower($info['extension']);
+		
+		switch($ext) {
+			case 'xml':
+				return self::FORMAT_XML;
+			
+			case 'ini':
+			case 'properties':
+				return self::FORMAT_INI;
+			
+			case 'php':
+				return self::FORMAT_PHP;
+				
+			default:
+				throw new LoggerException("Unsupported configuration file extension: $ext");
+		}
+	}
+	
+	/**
+	 * Constructs the logger hierarchy based on configuration.
+	 * 
+	 * @param LoggerHierarchy $hierarchy
+	 * @param array $config
+	 */
+	private function doConfigure(LoggerHierarchy $hierarchy, $config) {
+		if (isset($config['threshold'])) {
+			$threshold = LoggerLevel::toLevel($config['threshold']);
+			if (isset($threshold)) {
+				$hierarchy->setThreshold($threshold);
+			} else {
+				$this->warn("Invalid threshold value [{$config['threshold']}] specified. Ignoring threshold definition.");
+			}
+		}
+		
+		// Configure appenders and add them to the appender pool
+		if (isset($config['appenders']) && is_array($config['appenders'])) {
+			foreach($config['appenders'] as $name => $appenderConfig) {
+				$this->configureAppender($name, $appenderConfig);
+			}
+		}
+		
+		// Configure root logger 
+		if (isset($config['rootLogger'])) {
+			$this->configureRootLogger($hierarchy, $config['rootLogger']);
+		}
+		
+		// Configure loggers
+		if (isset($config['loggers']) && is_array($config['loggers'])) {
+			foreach($config['loggers'] as $loggerName => $loggerConfig) {
+				$this->configureOtherLogger($hierarchy, $loggerName, $loggerConfig);
+			}
+		}
+
+		// Configure renderers
+		if (isset($config['renderers']) && is_array($config['renderers'])) {
+			foreach($config['renderers'] as $rendererConfig) {
+				$this->configureRenderer($hierarchy, $rendererConfig);
+			}
+		}
+	}
+	
+	private function configureRenderer(LoggerHierarchy $hierarchy, $config) {
+		if (!isset($config['renderingClass'])) {
+			$this->warn("Rendering class not specified. Skipping renderers definition.");
+			return;			
+		}
+		
+		$renderingClass = $config['renderingClass'];
+		if (!class_exists($renderingClass)) {
+			$this->warn("Nonexistant rendering class [$renderingClass] specified. Skipping renderers definition.");
+			return;
+		}
+		
+		$renderingClassInstance = new $renderingClass();
+		if (!$renderingClassInstance instanceof LoggerRendererObject) {
+			$this->warn("Invalid class [$renderingClass] given. Not a valid LoggerRenderer class. Skipping renderers definition.");
+			return;			
+		}
+	
+		if (!isset($config['renderedClass'])) {
+			$this->warn("Rendered class not specified for rendering Class [$renderingClass]. Skipping renderers definition.");
+			return;			
+		}
+		
+		$renderedClass = $config['renderedClass'];
+		if (!class_exists($renderedClass)) {
+			$this->warn("Nonexistant rendered class [$renderedClass] specified for renderer [$renderingClass]. Skipping renderers definition.");
+			return;
+		}		
+
+		$hierarchy->getRendererMap()->addRenderer($renderedClass, $renderingClassInstance);
+	}
+	
+	/** 
+	 * Configures an appender based on given config and saves it to 
+	 * {@link $appenders} array so it can be later linked to loggers. 
+	 * @param string $name Appender name. 
+	 * @param array $config Appender configuration options.
+	 */
+	private function configureAppender($name, $config) {
+
+		// TODO: add this check to other places where it might be useful
+		if (!is_array($config)) {
+			$type = gettype($config);
+			$this->warn("Invalid configuration provided for appender [$name]. Expected an array, found <$type>. Skipping appender definition.");
+			return;
+		}
+		
+		// Parse appender class
+		$class = $config['class'];
+		if (empty($class)) {
+			$this->warn("No class given for appender [$name]. Skipping appender definition.");
+			return;
+		}
+		if (!class_exists($class)) {
+			$this->warn("Invalid class [$class] given for appender [$name]. Class does not exist. Skipping appender definition.");
+			return;
+		}
+		
+		// Instantiate the appender
+		$appender = new $class($name);
+		if (!($appender instanceof LoggerAppender)) {
+			$this->warn("Invalid class [$class] given for appender [$name]. Not a valid LoggerAppender class. Skipping appender definition.");
+			return;
+		}
+		
+		// Parse the appender threshold
+		if (isset($config['threshold'])) {
+			$threshold = LoggerLevel::toLevel($config['threshold']);
+			if ($threshold instanceof LoggerLevel) {
+				$appender->setThreshold($threshold);
+			} else {
+				$this->warn("Invalid threshold value [{$config['threshold']}] specified for appender [$name]. Ignoring threshold definition.");
+			}
+		}
+		
+		// Parse the appender layout
+		if ($appender->requiresLayout() && isset($config['layout'])) {
+			$this->createAppenderLayout($appender, $config['layout']);
+		}
+		
+		// Parse filters
+		if (isset($config['filters']) && is_array($config['filters'])) {
+			foreach($config['filters'] as $filterConfig) {
+				$this->createAppenderFilter($appender, $filterConfig);
+			}
+		}
+		
+		// Set options if any
+		if (isset($config['params'])) {
+			$this->setOptions($appender, $config['params']);
+		}
+
+		// Activate and save for later linking to loggers
+		$appender->activateOptions();
+		$this->appenders[$name] = $appender;
+	}
+	
+	/**
+	 * Parses layout config, creates the layout and links it to the appender.
+	 * @param LoggerAppender $appender
+	 * @param array $config Layout configuration.
+	 */
+	private function createAppenderLayout(LoggerAppender $appender, $config) {
+		$name = $appender->getName();
+		$class = $config['class'];
+		if (empty($class)) {
+			$this->warn("Layout class not specified for appender [$name]. Reverting to default layout.");
+			return;
+		}
+		if (!class_exists($class)) {
+			$this->warn("Nonexistant layout class [$class] specified for appender [$name]. Reverting to default layout.");
+			return;
+		}
+		
+		$layout = new $class();
+		if (!($layout instanceof LoggerLayout)) {
+			$this->warn("Invalid layout class [$class] sepcified for appender [$name]. Reverting to default layout.");
+			return;
+		}
+		
+		if (isset($config['params'])) {
+			$this->setOptions($layout, $config['params']);
+		}
+		
+		$layout->activateOptions();
+		$appender->setLayout($layout);
+	}
+	
+	/**
+	 * Parses filter config, creates the filter and adds it to the appender's 
+	 * filter chain.
+	 * @param LoggerAppender $appender
+	 * @param array $config Filter configuration.
+	 */
+	private function createAppenderFilter(LoggerAppender $appender, $config) {
+		$name = $appender->getName();
+		$class = $config['class'];
+		if (!class_exists($class)) {
+			$this->warn("Nonexistant filter class [$class] specified on appender [$name]. Skipping filter definition.");
+			return;
+		}
+	
+		$filter = new $class();
+		if (!($filter instanceof LoggerFilter)) {
+			$this->warn("Invalid filter class [$class] sepcified on appender [$name]. Skipping filter definition.");
+			return;
+		}
+	
+		if (isset($config['params'])) {
+			$this->setOptions($filter, $config['params']);
+		}
+	
+		$filter->activateOptions();
+		$appender->addFilter($filter);
+	}
+	
+	/** 
+	 * Configures the root logger
+	 * @see configureLogger() 
+	 */
+	private function configureRootLogger(LoggerHierarchy $hierarchy, $config) {
+		$logger = $hierarchy->getRootLogger();
+		$this->configureLogger($logger, $config);
+	}
+
+	/**
+	 * Configures a logger which is not root.
+	 * @see configureLogger()
+	 */
+	private function configureOtherLogger(LoggerHierarchy $hierarchy, $name, $config) {
+		// Get logger from hierarchy (this creates it if it doesn't already exist)
+		$logger = $hierarchy->getLogger($name);
+		$this->configureLogger($logger, $config);
+	}
+	
+	/**
+	 * Configures a logger. 
+	 * 
+	 * @param Logger $logger The logger to configure
+	 * @param array $config Logger configuration options.
+	 */
+	private function configureLogger(Logger $logger, $config) {
+		$loggerName = $logger->getName();
+		
+		// Set logger level
+		if (isset($config['level'])) {
+			$level = LoggerLevel::toLevel($config['level']);
+			if (isset($level)) {
+				$logger->setLevel($level);
+			} else {
+				$default = $logger->getLevel();
+				$this->warn("Invalid level value [{$config['level']}] specified for logger [$loggerName]. Ignoring level definition.");
+			}
+		}
+		
+		// Link appenders to logger
+		if (isset($config['appenders'])) {
+			foreach($config['appenders'] as $appenderName) {
+				if (isset($this->appenders[$appenderName])) {
+					$logger->addAppender($this->appenders[$appenderName]);
+				} else {
+					$this->warn("Nonexistnant appender [$appenderName] linked to logger [$loggerName].");
+				}
+			}
+		}
+		
+		// Set logger additivity
+		if (isset($config['additivity'])) {
+			$additivity = LoggerOptionConverter::toBoolean($config['additivity'], null);
+			if (is_bool($additivity)) {
+				$logger->setAdditivity($additivity);
+			} else {
+				$this->warn("Invalid additivity value [{$config['additivity']}] specified for logger [$loggerName]. Ignoring additivity setting.");
+			}
+		}
+	}
+
+	/**
+	 * Helper method which applies given options to an object which has setters
+	 * for these options (such as appenders, layouts, etc.).
+	 * 
+	 * For example, if options are:
+	 * <code>
+	 * array(
+	 * 	'file' => '/tmp/myfile.log',
+	 * 	'append' => true
+	 * )
+	 * </code>
+	 * 
+	 * This method will call:
+	 * <code>
+	 * $object->setFile('/tmp/myfile.log')
+	 * $object->setAppend(true)
+	 * </code>
+	 * 
+	 * If required setters do not exist, it will produce a warning. 
+	 * 
+	 * @param mixed $object The object to configure.
+	 * @param unknown_type $options
+	 */
+	private function setOptions($object, $options) {
+		foreach($options as $name => $value) {
+			$setter = "set$name";
+			if (method_exists($object, $setter)) {
+				$object->$setter($value);
+			} else {
+				$class = get_class($object);
+				$this->warn("Nonexistant option [$name] specified on [$class]. Skipping.");
+			}
+		}
+	}
+	
+	/** Helper method to simplify error reporting. */
+	private function warn($message) {
+		trigger_error("log4php: $message", E_USER_WARNING);
+	}
+}
\ No newline at end of file

Modified: logging/log4php/trunk/src/site/xdoc/docs/configuration.xml
URL: http://svn.apache.org/viewvc/logging/log4php/trunk/src/site/xdoc/docs/configuration.xml?rev=1213710&r1=1213709&r2=1213710&view=diff
==============================================================================
--- logging/log4php/trunk/src/site/xdoc/docs/configuration.xml (original)
+++ logging/log4php/trunk/src/site/xdoc/docs/configuration.xml Tue Dec 13 14:30:50 2011
@@ -203,26 +203,70 @@ array(
 
 				<div class="alert-message block-message info">
 					<p><strong>Hint:</strong> You can fetch the default configuration as a PHP array by running:</p>
-					<pre class="prettyprint">LoggerConfigurator::getDefaultConfiguration();</pre>
+					<pre class="prettyprint">LoggerConfiguratorDefault::getDefaultConfiguration();</pre>
 				</div>
 			</subsection>
 			
 			<subsection name="Programmatic configuration">
 				
-				<p>It is possible to configure log4php fully programmatically. Here is an example:</p>
+				<p>It is possible to configure log4php fully programmatically. This requires the user to implement 
+				their own configurator object. Configurators must implement the <code>LoggerConfigurator</code> 
+				interface.</p>
+				
+				<p>Here is an example:</p>
 
 <pre class="prettyprint">
+class MyConfigurator implements LoggerConfigurator {
+	
+    public function configure(LoggerHierarchy $hierarchy, $input = null) {
+
+        // Create an appender which logs to file
+        $appFile = new LoggerAppenderFile('foo');
+        $appFile->setFile('D:/Temp/log.txt');
+        $appFile->setAppend(true);
+        $appFile->setThreshold('all');
+        $appFile->activateOptions();
+        
+        // Use a different layout for the next appender
+        $layout = new LoggerLayoutTTCC();
+        $layout->setContextPrinting(false);
+        $layout->setDateFormat('%Y-%m-%d %H:%M:%S');
+        $layout->activateOptions();
+        
+        // Create an appender which echoes log events, using a custom layout
+        // and with the threshold set to INFO 
+        $appEcho = new LoggerAppenderEcho('bar');
+        $appEcho->setLayout($layout);
+        $appEcho->setThreshold('info');
+        $appEcho->activateOptions();
+        
+        // Add both appenders to the root logger
+        $root = $hierarchy->getRootLogger();
+        $root->addAppender($appFile);
+        $root->addAppender($appEcho);
+    }
+}
+</pre>
+
+				<p>To use the configurator, pass it as a second parameter to <code>Logger::configure()</code> 
+				(either the name of the class as a string or an instance).</p>
 
+<pre class="prettyprint">
+Logger::configure(null, 'MyConfigurator');
 
+$log = Logger::getRootLogger();
+$log->trace('one');
+$log->debug('two');
+$log->info('three');
+$log->warn('four');
+$log->error('five');
 </pre>
 
 				<div class="alert-message block-message warn">
-					<p>Note that named loggers should always be created by calling Logger::getLogger('name'). This 
-					will create the logger if it doesn't already exist and place it in the logger hierarchy.</p>
+					<p><strong>Note: </strong>Always call <code>activateOptions()</code> on all appenders, filters and layouts after setting
+					their configuration parameters. Otherwise, the configuration may not be properly activated.</p>
 				</div>
 			</subsection>
-
-
 		</section>
 	</body>
 </document>

Modified: logging/log4php/trunk/src/test/php/LoggerConfiguratorTest.php
URL: http://svn.apache.org/viewvc/logging/log4php/trunk/src/test/php/LoggerConfiguratorTest.php?rev=1213710&r1=1213709&r2=1213710&view=diff
==============================================================================
--- logging/log4php/trunk/src/test/php/LoggerConfiguratorTest.php (original)
+++ logging/log4php/trunk/src/test/php/LoggerConfiguratorTest.php Tue Dec 13 14:30:50 2011
@@ -98,7 +98,7 @@
 	        ),
         );
 
-        $configurator = new LoggerConfigurator();
+        $configurator = new LoggerConfiguratorDefault();
         $configurator->configure($hierachyMock, $config);
  	}
  	

Modified: logging/log4php/trunk/src/test/php/filters/LoggerFilterDenyAllTest.php
URL: http://svn.apache.org/viewvc/logging/log4php/trunk/src/test/php/filters/LoggerFilterDenyAllTest.php?rev=1213710&r1=1213709&r2=1213710&view=diff
==============================================================================
--- logging/log4php/trunk/src/test/php/filters/LoggerFilterDenyAllTest.php (original)
+++ logging/log4php/trunk/src/test/php/filters/LoggerFilterDenyAllTest.php Tue Dec 13 14:30:50 2011
@@ -47,7 +47,7 @@ class LoggerFilterDenyAllTest extends PH
     }
     
     public function testConfiguration() {
-    	$config = LoggerConfigurator::getDefaultConfiguration();
+    	$config = LoggerConfiguratorDefault::getDefaultConfiguration();
     	$config['appenders']['default']['filters'] = array(
     		array(
     			'class' => 'LoggerFilterDenyAll'