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 2013/11/28 17:03:56 UTC

[26/43] LOG4PHP-121: Reorganized classes into namespaces

http://git-wip-us.apache.org/repos/asf/logging-log4php/blob/79ed2d0d/src/Appenders/PhpAppender.php
----------------------------------------------------------------------
diff --git a/src/Appenders/PhpAppender.php b/src/Appenders/PhpAppender.php
new file mode 100644
index 0000000..287c56a
--- /dev/null
+++ b/src/Appenders/PhpAppender.php
@@ -0,0 +1,50 @@
+<?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.
+ */
+
+namespace Apache\Log4php\Appenders;
+
+use Apache\Log4php\Level;
+use Apache\Log4php\LoggingEvent;
+
+/**
+ * PhpAppender logs events by creating a PHP user-level message using
+ * the PHP's trigger_error()function.
+ *
+ * This appender has no configurable parameters.
+ *
+ * Levels are mapped as follows:
+ *
+ * - <b>level < WARN</b> mapped to E_USER_NOTICE
+ * - <b>WARN <= level < ERROR</b> mapped to E_USER_WARNING
+ * - <b>level >= ERROR</b> mapped to E_USER_ERROR
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
+ * @link http://logging.apache.org/log4php/docs/appenders/php.html Appender documentation
+ */
+class PhpAppender extends AbstractAppender {
+
+	public function append(LoggingEvent $event) {
+		$level = $event->getLevel();
+		if($level->isGreaterOrEqual(Level::getLevelError())) {
+			trigger_error($this->layout->format($event), E_USER_ERROR);
+		} else if ($level->isGreaterOrEqual(Level::getLevelWarn())) {
+			trigger_error($this->layout->format($event), E_USER_WARNING);
+		} else {
+			trigger_error($this->layout->format($event), E_USER_NOTICE);
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/logging-log4php/blob/79ed2d0d/src/Appenders/RollingFileAppender.php
----------------------------------------------------------------------
diff --git a/src/Appenders/RollingFileAppender.php b/src/Appenders/RollingFileAppender.php
new file mode 100644
index 0000000..8d54a4a
--- /dev/null
+++ b/src/Appenders/RollingFileAppender.php
@@ -0,0 +1,303 @@
+<?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.
+ */
+
+namespace Apache\Log4php\Appenders;
+
+use Apache\Log4php\LoggingEvent;
+
+/**
+ * RollingFileAppender writes logging events to a specified file. The
+ * file is rolled over after a specified size has been reached.
+ *
+ * This appender uses a layout.
+ *
+ * ## Configurable parameters: ##
+ *
+ * - **file** - Path to the target file.
+ * - **append** - If set to true, the appender will append to the file,
+ *     otherwise the file contents will be overwritten.
+ * - **maxBackupIndex** - Maximum number of backup files to keep. Default is 1.
+ * - **maxFileSize** - Maximum allowed file size (in bytes) before rolling
+ *     over. Suffixes "KB", "MB" and "GB" are allowed. 10KB = 10240 bytes, etc.
+ *     Default is 10M.
+ * - **compress** - If set to true, rolled-over files will be compressed.
+ *     Requires the zlib extension.
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
+ * @link http://logging.apache.org/log4php/docs/appenders/rolling-file.html Appender documentation
+ */
+class RollingFileAppender extends FileAppender {
+
+	/** Compressing backup files is done in chunks, this determines how large. */
+	const COMPRESS_CHUNK_SIZE = 102400; // 100KB
+
+	/**
+	 * The maximum size (in bytes) that the output file is allowed to reach
+	 * before being rolled over to backup files.
+	 *
+	 * The default maximum file size is 10MB (10485760 bytes). Maximum value
+	 * for this option may depend on the file system.
+	 *
+	 * @var integer
+	 */
+	protected $maxFileSize = 10485760;
+
+	/**
+	 * Set the maximum number of backup files to keep around.
+	 *
+	 * Determines how many backup files are kept before the oldest is erased.
+	 * This option takes a positive integer value. If set to zero, then there
+	 * will be no backup files and the log file will be truncated when it
+	 * reaches <var>maxFileSize</var>.
+	 *
+	 * There is one backup file by default.
+	 *
+	 * @var integer
+	 */
+	protected $maxBackupIndex = 1;
+
+	/**
+	 * The <var>compress</var> parameter determindes the compression with zlib.
+	 * If set to true, the rollover files are compressed and saved with the .gz extension.
+	 * @var boolean
+	 */
+	protected $compress = false;
+
+	/**
+	 * Set to true in the constructor if PHP >= 5.3.0. In that case clearstatcache
+	 * supports conditional clearing of statistics.
+	 * @var boolean
+	 * @see http://php.net/manual/en/function.clearstatcache.php
+	 */
+	private $clearConditional = false;
+
+	/**
+	 * Get the maximum size that the output file is allowed to reach
+	 * before being rolled over to backup files.
+	 * @return integer
+	 */
+	public function getMaximumFileSize() {
+		return $this->maxFileSize;
+	}
+
+	public function __construct($name = '') {
+		parent::__construct($name);
+		if (version_compare(PHP_VERSION, '5.3.0') >= 0) {
+			$this->clearConditional = true;
+		}
+	}
+
+	/**
+	 * Implements the usual roll over behaviour.
+	 *
+	 * If MaxBackupIndex is positive, then files File.1, ..., File.MaxBackupIndex -1 are renamed to File.2, ..., File.MaxBackupIndex.
+	 * Moreover, File is renamed File.1 and closed. A new File is created to receive further log output.
+	 *
+	 * If MaxBackupIndex is equal to zero, then the File is truncated with no backup files created.
+	 *
+	 * Rollover must be called while the file is locked so that it is safe for concurrent access.
+	 *
+	 * @throws LoggerException If any part of the rollover procedure fails.
+	 */
+	private function rollOver() {
+		// If maxBackups <= 0, then there is no file renaming to be done.
+		if($this->maxBackupIndex > 0) {
+			// Delete the oldest file, to keep Windows happy.
+			$file = $this->file . '.' . $this->maxBackupIndex;
+
+			if (file_exists($file) && !unlink($file)) {
+				throw new LoggerException("Unable to delete oldest backup file from [$file].");
+			}
+
+			// Map {(maxBackupIndex - 1), ..., 2, 1} to {maxBackupIndex, ..., 3, 2}
+			$this->renameArchievedLogs($this->file);
+
+			// Backup the active file
+			$this->moveToBackup($this->file);
+		}
+
+		// Truncate the active file
+		ftruncate($this->fp, 0);
+		rewind($this->fp);
+	}
+
+	private function moveToBackup($source) {
+		if ($this->compress) {
+			$target = $source . '.1.gz';
+			$this->compressFile($source, $target);
+		} else {
+			$target = $source . '.1';
+			copy($source, $target);
+		}
+	}
+
+	private function compressFile($source, $target) {
+		$target = 'compress.zlib://' . $target;
+
+		$fin = fopen($source, 'rb');
+		if ($fin === false) {
+			throw new LoggerException("Unable to open file for reading: [$source].");
+		}
+
+		$fout = fopen($target, 'wb');
+		if ($fout === false) {
+			throw new LoggerException("Unable to open file for writing: [$target].");
+		}
+
+		while (!feof($fin)) {
+			$chunk = fread($fin, self::COMPRESS_CHUNK_SIZE);
+			if (false === fwrite($fout, $chunk)) {
+				throw new LoggerException("Failed writing to compressed file.");
+			}
+		}
+
+		fclose($fin);
+		fclose($fout);
+	}
+
+	private function renameArchievedLogs($fileName) {
+		for($i = $this->maxBackupIndex - 1; $i >= 1; $i--) {
+
+			$source = $fileName . "." . $i;
+			if ($this->compress) {
+				$source .= '.gz';
+			}
+
+			if(file_exists($source)) {
+				$target = $fileName . '.' . ($i + 1);
+				if ($this->compress) {
+					$target .= '.gz';
+				}
+
+				rename($source, $target);
+			}
+		}
+	}
+
+	/**
+	 * Writes a string to the target file. Opens file if not already open.
+	 * @param string $string Data to write.
+	 */
+	protected function write($string) {
+		// Lazy file open
+		if(!isset($this->fp)) {
+			if ($this->openFile() === false) {
+				return; // Do not write if file open failed.
+			}
+		}
+
+		// Lock the file while writing and possible rolling over
+		if(flock($this->fp, LOCK_EX)) {
+
+			// Write to locked file
+			if(fwrite($this->fp, $string) === false) {
+				$this->warn("Failed writing to file. Closing appender.");
+				$this->closed = true;
+			}
+
+			// Stats cache must be cleared, otherwise filesize() returns cached results
+			// If supported (PHP 5.3+), clear only the state cache for the target file
+			if ($this->clearConditional) {
+				clearstatcache(true, $this->file);
+			} else {
+				clearstatcache();
+			}
+
+			// Rollover if needed
+			if (filesize($this->file) > $this->maxFileSize) {
+				try {
+					$this->rollOver();
+				} catch (LoggerException $ex) {
+					$this->warn("Rollover failed: " . $ex->getMessage() . " Closing appender.");
+					$this->closed = true;
+				}
+			}
+
+			flock($this->fp, LOCK_UN);
+		} else {
+			$this->warn("Failed locking file for writing. Closing appender.");
+			$this->closed = true;
+		}
+	}
+
+	public function activateOptions() {
+		parent::activateOptions();
+
+		if ($this->compress && !extension_loaded('zlib')) {
+			$this->warn("The 'zlib' extension is required for file compression. Disabling compression.");
+			$this->compression = false;
+		}
+	}
+
+	/**
+	 * Set the 'maxBackupIndex' parameter.
+	 * @param integer $maxBackupIndex
+	 */
+	public function setMaxBackupIndex($maxBackupIndex) {
+		$this->setPositiveInteger('maxBackupIndex', $maxBackupIndex);
+	}
+
+	/**
+	 * Returns the 'maxBackupIndex' parameter.
+	 * @return integer
+	 */
+	public function getMaxBackupIndex() {
+		return $this->maxBackupIndex;
+	}
+
+	/**
+	 * Set the 'maxFileSize' parameter.
+	 * @param mixed $maxFileSize
+	 */
+	public function setMaxFileSize($maxFileSize) {
+		$this->setFileSize('maxFileSize', $maxFileSize);
+	}
+
+	/**
+	 * Returns the 'maxFileSize' parameter.
+	 * @return integer
+	 */
+	public function getMaxFileSize() {
+		return $this->maxFileSize;
+	}
+
+	/**
+	 * Set the 'maxFileSize' parameter (kept for backward compatibility).
+	 * @param mixed $maxFileSize
+	 * @deprecated Use setMaxFileSize() instead.
+	 */
+	public function setMaximumFileSize($maxFileSize) {
+		$this->warn("The 'maximumFileSize' parameter is deprecated. Use 'maxFileSize' instead.");
+		return $this->setMaxFileSize($maxFileSize);
+	}
+
+	/**
+	 * Sets the 'compress' parameter.
+	 * @param boolean $compress
+	 */
+	public function setCompress($compress) {
+		$this->setBoolean('compress', $compress);
+	}
+
+	/**
+	 * Returns the 'compress' parameter.
+	 * @param boolean
+	 */
+	public function getCompress() {
+		return $this->compress;
+	}
+}

http://git-wip-us.apache.org/repos/asf/logging-log4php/blob/79ed2d0d/src/Appenders/SocketAppender.php
----------------------------------------------------------------------
diff --git a/src/Appenders/SocketAppender.php b/src/Appenders/SocketAppender.php
new file mode 100644
index 0000000..024c91f
--- /dev/null
+++ b/src/Appenders/SocketAppender.php
@@ -0,0 +1,123 @@
+<?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.
+ */
+
+namespace Apache\Log4php\Appenders;
+
+use Apache\Log4php\Layouts\SerializedLayout;
+use Apache\Log4php\LoggingEvent;
+
+/**
+ * SocketAppender appends to a network socket.
+ *
+ * ## Configurable parameters: ##
+ *
+ * - **remoteHost** - Target remote host.
+ * - **port** - Target port (optional, defaults to 4446).
+ * - **timeout** - Connection timeout in seconds (optional, defaults to
+ *     'default_socket_timeout' from php.ini)
+ *
+ * The socket will by default be opened in blocking mode.
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
+ * @link http://logging.apache.org/log4php/docs/appenders/socket.html Appender documentation
+ */
+class SocketAppender extends AbstractAppender {
+
+	/**
+	 * Target host.
+	 * @see http://php.net/manual/en/function.fsockopen.php
+	 */
+	protected $remoteHost;
+
+	/** Target port */
+	protected $port = 4446;
+
+	/** Connection timeout in ms. */
+	protected $timeout;
+
+	// ******************************************
+	// *** Appender methods                   ***
+	// ******************************************
+
+	/** Override the default layout to use serialized. */
+	public function getDefaultLayout() {
+		return new SerializedLayout();
+	}
+
+	public function activateOptions() {
+		if (empty($this->remoteHost)) {
+			$this->warn("Required parameter [remoteHost] not set. Closing appender.");
+			$this->closed = true;
+			return;
+		}
+
+		if (empty($this->timeout)) {
+			$this->timeout = ini_get("default_socket_timeout");
+		}
+
+		$this->closed = false;
+	}
+
+	public function append(LoggingEvent $event) {
+		$socket = fsockopen($this->remoteHost, $this->port, $errno, $errstr, $this->timeout);
+		if ($socket === false) {
+			$this->warn("Could not open socket to {$this->remoteHost}:{$this->port}. Closing appender.");
+			$this->closed = true;
+			return;
+		}
+
+		if (false === fwrite($socket, $this->layout->format($event))) {
+			$this->warn("Error writing to socket. Closing appender.");
+			$this->closed = true;
+		}
+		fclose($socket);
+	}
+
+	// ******************************************
+	// *** Accessor methods                   ***
+	// ******************************************
+
+	/** Sets the target host. */
+	public function setRemoteHost($hostname) {
+		$this->setString('remoteHost', $hostname);
+	}
+
+	/** Sets the target port */
+	public function setPort($port) {
+		$this->setPositiveInteger('port', $port);
+	}
+
+	/** Sets the timeout. */
+	public function setTimeout($timeout) {
+		$this->setPositiveInteger('timeout', $timeout);
+	}
+
+	/** Returns the target host. */
+	public function getRemoteHost() {
+		return $this->getRemoteHost();
+	}
+
+	/** Returns the target port. */
+	public function getPort() {
+		return $this->port;
+	}
+
+	/** Returns the timeout */
+	public function getTimeout() {
+		return $this->timeout;
+	}
+}

http://git-wip-us.apache.org/repos/asf/logging-log4php/blob/79ed2d0d/src/Appenders/SyslogAppender.php
----------------------------------------------------------------------
diff --git a/src/Appenders/SyslogAppender.php b/src/Appenders/SyslogAppender.php
new file mode 100644
index 0000000..ae7351b
--- /dev/null
+++ b/src/Appenders/SyslogAppender.php
@@ -0,0 +1,304 @@
+<?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.
+ */
+
+namespace Apache\Log4php\Appenders;
+
+use Apache\Log4php\Level;
+use Apache\Log4php\LoggingEvent;
+
+/**
+ * Log events to a system log using the PHP syslog() function.
+ *
+ * This appenders requires a layout.
+ *
+ * ## Configurable parameters: ##
+ *
+ * - **ident** - The ident of the syslog message.
+ * - **priority** - The priority for the syslog message (used when overriding
+ *     priority).
+ * - **facility** - The facility for the syslog message
+ * - **overridePriority** - If set to true, the message priority will always
+ *     use the value defined in {@link $priority}, otherwise the priority will
+ *     be determined by the message's log level.
+ * - **option** - The option value for the syslog message.
+ *
+ * Recognised syslog options are:
+ *
+ * - CONS 	 - if there is an error while sending data to the system logger, write directly to the system console
+ * - NDELAY - open the connection to the logger immediately
+ * - ODELAY - delay opening the connection until the first message is logged (default)
+ * - PERROR - print log message also to standard error
+ * - PID    - include PID with each message
+ *
+ * Multiple options can be set by delimiting them with a pipe character,
+ * e.g.: "CONS|PID|PERROR".
+ *
+ * Recognised syslog priorities are:
+ *
+ * - EMERG
+ * - ALERT
+ * - CRIT
+ * - ERR
+ * - WARNING
+ * - NOTICE
+ * - INFO
+ * - DEBUG
+ *
+ * Levels are mapped as follows:
+ *
+ * - <b>FATAL</b> to LOG_ALERT
+ * - <b>ERROR</b> to LOG_ERR
+ * - <b>WARN</b> to LOG_WARNING
+ * - <b>INFO</b> to LOG_INFO
+ * - <b>DEBUG</b> to LOG_DEBUG
+ * - <b>TRACE</b> to LOG_DEBUG
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
+ * @link http://logging.apache.org/log4php/docs/appenders/syslog.html Appender documentation
+ */
+class SyslogAppender extends AbstractAppender {
+
+	/**
+	 * The ident string is added to each message. Typically the name of your application.
+	 *
+	 * @var string
+	 */
+	protected $ident = "Apache log4php";
+
+	/**
+	 * The syslog priority to use when overriding priority. This setting is
+	 * required if {@link overridePriority} is set to true.
+	 *
+	 * @var string
+	 */
+	protected $priority;
+
+	/**
+	 * The option used when opening the syslog connection.
+	 *
+	 * @var string
+	 */
+	protected $option = 'PID|CONS';
+
+	/**
+	 * The facility value indicates the source of the message.
+	 *
+	 * @var string
+	 */
+	protected $facility = 'USER';
+
+	/**
+	 * If set to true, the message priority will always use the value defined
+	 * in {@link $priority}, otherwise the priority will be determined by the
+	 * message's log level.
+	 *
+	 * @var string
+	 */
+	protected $overridePriority = false;
+
+	/**
+	 * Holds the int value of the {@link $priority}.
+	 * @var int
+	 */
+	private $intPriority;
+
+	/**
+	 * Holds the int value of the {@link $facility}.
+	 * @var int
+	 */
+	private $intFacility;
+
+	/**
+	 * Holds the int value of the {@link $option}.
+	 * @var int
+	 */
+	private $intOption;
+
+	/**
+	 * Sets the {@link $ident}.
+	 *
+	 * @param string $ident
+	 */
+	public function setIdent($ident) {
+		$this->ident = $ident;
+	}
+
+	/**
+	 * Sets the {@link $priority}.
+	 *
+	 * @param string $priority
+	 */
+	public function setPriority($priority) {
+		$this->priority = $priority;
+	}
+
+	/**
+	 * Sets the {@link $facility}.
+	 *
+	 * @param string $facility
+	 */
+	public function setFacility($facility) {
+		$this->facility = $facility;
+	}
+
+	/**
+	 * Sets the {@link $overridePriority}.
+	 *
+	 * @param string $overridePriority
+	 */
+	public function setOverridePriority($overridePriority) {
+		$this->overridePriority = $overridePriority;
+	}
+
+	/**
+	* Sets the 'option' parameter.
+	*
+	* @param string $option
+	*/
+	public function setOption($option) {
+		$this->option = $option;
+	}
+
+	/**
+	* Returns the 'ident' parameter.
+	*
+	* @return string $ident
+	*/
+	public function getIdent() {
+		return $this->ident;
+	}
+
+	/**
+	 * Returns the 'priority' parameter.
+	 *
+	 * @return string
+	 */
+	public function getPriority() {
+		return $this->priority;
+	}
+
+	/**
+	 * Returns the 'facility' parameter.
+	 *
+	 * @return string
+	 */
+	public function getFacility() {
+		return $this->facility;
+	}
+
+	/**
+	 * Returns the 'overridePriority' parameter.
+	 *
+	 * @return string
+	 */
+	public function getOverridePriority() {
+		return $this->overridePriority;
+	}
+
+	/**
+	 * Returns the 'option' parameter.
+	 *
+	 * @return string
+	 */
+	public function getOption() {
+		return $this->option;
+	}
+
+
+	public function activateOptions() {
+		$this->intPriority = $this->parsePriority();
+		$this->intOption   = $this->parseOption();
+		$this->intFacility = $this->parseFacility();
+
+		$this->closed = false;
+	}
+
+	public function close() {
+		if($this->closed != true) {
+			closelog();
+			$this->closed = true;
+		}
+	}
+
+	/**
+	 * Appends the event to syslog.
+	 *
+	 * Log is opened and closed each time because if it is not closed, it
+	 * can cause the Apache httpd server to log to whatever ident/facility
+	 * was used in openlog().
+	 *
+	 * @see http://www.php.net/manual/en/function.syslog.php#97843
+	 */
+	public function append(LoggingEvent $event) {
+		$priority = $this->getSyslogPriority($event->getLevel());
+		$message = $this->layout->format($event);
+
+		openlog($this->ident, $this->intOption, $this->intFacility);
+		syslog($priority, $message);
+		closelog();
+	}
+
+	/** Determines which syslog priority to use based on the given level. */
+	private function getSyslogPriority(Level $level) {
+		if($this->overridePriority) {
+			return $this->intPriority;
+		}
+		return $level->getSyslogEquivalent();
+	}
+
+	/** Parses a syslog option string and returns the correspodning int value. */
+	private function parseOption() {
+		$value = 0;
+		$options = explode('|', $this->option);
+
+		foreach($options as $option) {
+			if (!empty($option)) {
+				$constant = "LOG_" . trim($option);
+				if (defined($constant)) {
+					$value |= constant($constant);
+				} else {
+					trigger_error("log4php: Invalid syslog option provided: $option. Whole option string: {$this->option}.", E_USER_WARNING);
+				}
+			}
+		}
+		return $value;
+	}
+
+	/** Parses the facility string and returns the corresponding int value. */
+	private function parseFacility() {
+		if (!empty($this->facility)) {
+			$constant = "LOG_" . trim($this->facility);
+			if (defined($constant)) {
+				return constant($constant);
+			} else {
+				trigger_error("log4php: Invalid syslog facility provided: {$this->facility}.", E_USER_WARNING);
+			}
+		}
+	}
+
+	/** Parses the priority string and returns the corresponding int value. */
+	private function parsePriority() {
+		if (!empty($this->priority)) {
+			$constant = "LOG_" . trim($this->priority);
+			if (defined($constant)) {
+				return constant($constant);
+			} else {
+				trigger_error("log4php: Invalid syslog priority provided: {$this->priority}.", E_USER_WARNING);
+			}
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/logging-log4php/blob/79ed2d0d/src/Autoloader.php
----------------------------------------------------------------------
diff --git a/src/Autoloader.php b/src/Autoloader.php
new file mode 100644
index 0000000..fe6b8d3
--- /dev/null
+++ b/src/Autoloader.php
@@ -0,0 +1,53 @@
+<?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.
+ */
+
+namespace Apache\Log4php;
+
+/**
+ * PSR-4 compliant autoloader implementation.
+ */
+class Autoloader
+{
+	const BASE_NAMESPACE = 'Apache\\Log4php\\';
+
+	public function autoload($class)
+	{
+	    // Base directory for the namespace prefix
+	    $baseDir = __DIR__ . '/../src/';
+
+	    // Skip classes which are not in base namespace
+	    $len = strlen(self::BASE_NAMESPACE);
+	    if (strncmp(self::BASE_NAMESPACE, $class, $len) !== 0) {
+	        return;
+	    }
+
+	    // Locate the class in base dir, based on namespace
+	    $classPath = str_replace('\\', '/', substr($class, $len));
+	    $file = $baseDir . $classPath . '.php';
+
+	    // If the file exists, require it
+	    if (file_exists($file)) {
+	        require $file;
+	    }
+	}
+
+	public function register()
+	{
+		spl_autoload_register(array($this, 'autoload'));
+	}
+}

http://git-wip-us.apache.org/repos/asf/logging-log4php/blob/79ed2d0d/src/Configurable.php
----------------------------------------------------------------------
diff --git a/src/Configurable.php b/src/Configurable.php
new file mode 100644
index 0000000..352aa23
--- /dev/null
+++ b/src/Configurable.php
@@ -0,0 +1,118 @@
+<?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.
+*/
+
+namespace Apache\Log4php;
+
+use Apache\Log4php\Helpers\OptionConverter;
+
+use Exception;
+
+/**
+ * A base class from which all classes which have configurable properties are
+ * extended. Provides a generic setter with integrated validation.
+ *
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
+ * @since 2.2
+ */
+abstract class Configurable {
+
+	/** Setter function for boolean type. */
+	protected function setBoolean($property, $value) {
+		try {
+			$this->$property = OptionConverter::toBooleanEx($value);
+		} catch (Exception $ex) {
+			$value = var_export($value, true);
+			$this->warn("Invalid value given for '$property' property: [$value]. Expected a boolean value. Property not changed.");
+		}
+	}
+
+	/** Setter function for integer type. */
+	protected function setInteger($property, $value) {
+		try {
+			$this->$property = OptionConverter::toIntegerEx($value);
+		} catch (Exception $ex) {
+			$value = var_export($value, true);
+			$this->warn("Invalid value given for '$property' property: [$value]. Expected an integer. Property not changed.");
+		}
+	}
+
+	/** Setter function for Level values. */
+	protected function setLevel($property, $value) {
+		try {
+			$this->$property = OptionConverter::toLevelEx($value);
+		} catch (Exception $ex) {
+			$value = var_export($value, true);
+			$this->warn("Invalid value given for '$property' property: [$value]. Expected a level value. Property not changed.");
+		}
+	}
+
+	/** Setter function for integer type. */
+	protected function setPositiveInteger($property, $value) {
+		try {
+			$this->$property = OptionConverter::toPositiveIntegerEx($value);
+		} catch (Exception $ex) {
+			$value = var_export($value, true);
+			$this->warn("Invalid value given for '$property' property: [$value]. Expected a positive integer. Property not changed.");
+		}
+	}
+
+	/** Setter for file size. */
+	protected function setFileSize($property, $value) {
+		try {
+			$this->$property = OptionConverter::toFileSizeEx($value);
+		} catch (Exception $ex) {
+			$value = var_export($value, true);
+			$this->warn("Invalid value given for '$property' property: [$value]. Expected a file size value.  Property not changed.");
+		}
+	}
+
+	/** Setter function for numeric type. */
+	protected function setNumeric($property, $value) {
+		try {
+			$this->$property = OptionConverter::toNumericEx($value);
+		} catch (Exception $ex) {
+			$value = var_export($value, true);
+			$this->warn("Invalid value given for '$property' property: [$value]. Expected a number. Property not changed.");
+		}
+	}
+
+	/** Setter function for string type. */
+	protected function setString($property, $value, $nullable = false) {
+		if ($value === null) {
+			if($nullable) {
+				$this->$property= null;
+			} else {
+				$this->warn("Null value given for '$property' property. Expected a string. Property not changed.");
+			}
+		} else {
+			try {
+				$value = OptionConverter::toStringEx($value);
+				$this->$property = OptionConverter::substConstants($value);
+			} catch (Exception $ex) {
+				$value = var_export($value, true);
+				$this->warn("Invalid value given for '$property' property: [$value]. Expected a string. Property not changed.");
+			}
+		}
+	}
+
+	/** Triggers a warning. */
+	protected function warn($message) {
+		$class = get_class($this);
+		trigger_error("log4php: $class: $message", E_USER_WARNING);
+	}
+}

http://git-wip-us.apache.org/repos/asf/logging-log4php/blob/79ed2d0d/src/Configuration/ConfiguratorInterface.php
----------------------------------------------------------------------
diff --git a/src/Configuration/ConfiguratorInterface.php b/src/Configuration/ConfiguratorInterface.php
new file mode 100644
index 0000000..a4a8e21
--- /dev/null
+++ b/src/Configuration/ConfiguratorInterface.php
@@ -0,0 +1,41 @@
+<?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.
+ */
+
+namespace Apache\Log4php\Configuration;
+
+use Apache\Log4php\Hierarchy;
+
+/**
+ * Interface for logger configurators.
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
+ * @since 2.2
+ */
+interface ConfiguratorInterface
+{
+	/**
+	 * Configures log4php based on the given configuration.
+	 *
+	 * All configurators implementations must implement this interface.
+	 *
+	 * @param Hierarchy $hierarchy The hierarchy on which to perform
+	 * 		the configuration.
+	 * @param mixed $input Either path to the config file or the
+	 * 		configuration as an array.
+	 */
+	public function configure(Hierarchy $hierarchy, $input = null);
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/logging-log4php/blob/79ed2d0d/src/Configuration/DefaultConfigurator.php
----------------------------------------------------------------------
diff --git a/src/Configuration/DefaultConfigurator.php b/src/Configuration/DefaultConfigurator.php
new file mode 100644
index 0000000..9e4e6bd
--- /dev/null
+++ b/src/Configuration/DefaultConfigurator.php
@@ -0,0 +1,512 @@
+<?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.
+ */
+
+namespace Apache\Log4php\Configuration;
+
+use Apache\Log4php\Appenders\AbstractAppender;
+use Apache\Log4php\Filters\AbstractFilter;
+use Apache\Log4php\Helpers\OptionConverter;
+use Apache\Log4php\Hierarchy;
+use Apache\Log4php\Layouts\AbstractLayout;
+use Apache\Log4php\Level;
+use Apache\Log4php\Logger;
+use Apache\Log4php\LoggerException;
+
+/**
+ * Default implementation of the logger configurator.
+ *
+ * Configures log4php based on a provided configuration file or array.
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
+ * @since 2.2
+ */
+class DefaultConfigurator implements ConfiguratorInterface
+{
+	/** 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 => 'XmlAdapter',
+		self::FORMAT_INI => 'IniAdapter',
+		self::FORMAT_PHP => 'PhpAdapter',
+	);
+
+	/** 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' => '\\Apache\\Log4php\\Appenders\\EchoAppender'
+            ),
+        ),
+	);
+
+	/** 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 Hierarchy $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(Hierarchy $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 = "Apache\\Log4php\\Configuration\\Adapters\\" . $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 Hierarchy $hierarchy
+	 * @param array $config
+	 */
+	private function doConfigure(Hierarchy $hierarchy, $config) {
+		if (isset($config['threshold'])) {
+			$threshold = Level::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);
+			}
+		}
+
+		if (isset($config['defaultRenderer'])) {
+			$this->configureDefaultRenderer($hierarchy, $config['defaultRenderer']);
+		}
+	}
+
+	private function configureRenderer(Hierarchy $hierarchy, $config) {
+		if (empty($config['renderingClass'])) {
+			$this->warn("Rendering class not specified. Skipping renderer definition.");
+			return;
+		}
+
+		if (empty($config['renderedClass'])) {
+			$this->warn("Rendered class not specified. Skipping renderer definition.");
+			return;
+		}
+
+		// Error handling performed by RendererMap
+		$hierarchy->getRendererMap()->addRenderer($config['renderedClass'], $config['renderingClass']);
+	}
+
+	private function configureDefaultRenderer(Hierarchy $hierarchy, $class) {
+		if (empty($class)) {
+			$this->warn("Rendering class not specified. Skipping default renderer definition.");
+			return;
+		}
+
+		// Error handling performed by RendererMap
+		$hierarchy->getRendererMap()->setDefaultRenderer($class);
+	}
+
+	/**
+	 * 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;
+		}
+
+		// Instantiate the appender
+		if (class_exists($class)) {
+			$appender = new $class($name);
+		} else {
+			// Try the default namespace
+			$nsClass = "\\Apache\\Log4php\\Appenders\\$class";
+			if (class_exists($nsClass)) {
+				$appender = new $nsClass($name);
+			}
+		}
+
+		if (!isset($appender)) {
+			$this->warn("Invalid class [$class] given for appender [$name]. Class does not exist. Skipping appender definition.");
+			return;
+		}
+
+		if (!($appender instanceof AbstractAppender)) {
+			$this->warn("Invalid class [$class] given for appender [$name]. Not a valid Appender class. Skipping appender definition.");
+			return;
+		}
+
+		// Parse the appender threshold
+		if (isset($config['threshold'])) {
+			$threshold = Level::toLevel($config['threshold']);
+			if ($threshold instanceof Level) {
+				$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 AbstractAppender $appender
+	 * @param array $config Layout configuration.
+	 */
+	private function createAppenderLayout(AbstractAppender $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)) {
+			$layout = new $class();
+		} else {
+			$nsClass = "Apache\\Log4php\\Layouts\\$class";
+			if (class_exists($nsClass)) {
+				$layout = new $nsClass();
+			}
+		}
+
+		if (!isset($layout)) {
+			$this->warn("Nonexistant layout class [$class] specified for appender [$name]. Reverting to default layout.");
+			return;
+		}
+
+
+		if (!($layout instanceof AbstractLayout)) {
+			$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 Appender $appender
+	 * @param array $config Filter configuration.
+	 */
+	private function createAppenderFilter(AbstractAppender $appender, $config) {
+		$name = $appender->getName();
+		$class = $config['class'];
+
+		if (class_exists($class)) {
+			$filter = new $class();
+		} else {
+			$nsClass = "Apache\\Log4php\\Filters\\$class";
+			if (class_exists($nsClass)) {
+				$filter = new $nsClass();
+			}
+		}
+
+		if (!isset($filter)) {
+			$this->warn("Nonexistant filter class [$class] specified on appender [$name]. Skipping filter definition.");
+			return;
+		}
+
+		if (!($filter instanceof AbstractFilter)) {
+			$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(Hierarchy $hierarchy, $config) {
+		$logger = $hierarchy->getRootLogger();
+		$this->configureLogger($logger, $config);
+	}
+
+	/**
+	 * Configures a logger which is not root.
+	 * @see configureLogger()
+	 */
+	private function configureOtherLogger(Hierarchy $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 = Level::toLevel($config['level']);
+			if (isset($level)) {
+				$logger->setLevel($level);
+			} else {
+				$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'])) {
+			try {
+				$additivity = OptionConverter::toBooleanEx($config['additivity'], null);
+				$logger->setAdditivity($additivity);
+			} catch (LoggerException $ex) {
+				$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);
+	}
+}

http://git-wip-us.apache.org/repos/asf/logging-log4php/blob/79ed2d0d/src/Configuration/adapters/AdapterInterface.php
----------------------------------------------------------------------
diff --git a/src/Configuration/adapters/AdapterInterface.php b/src/Configuration/adapters/AdapterInterface.php
new file mode 100644
index 0000000..f0fea69
--- /dev/null
+++ b/src/Configuration/adapters/AdapterInterface.php
@@ -0,0 +1,35 @@
+<?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.
+ */
+
+namespace Apache\Log4php\Configuration\Adapters;
+
+/**
+ * The interface for configurator adapters.
+ *
+ * Adapters convert configuration in several formats such as XML, ini and PHP
+ * file to a PHP array.
+ *
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
+ * @since 2.2
+ */
+interface AdapterInterface
+{
+	/** Converts the configuration file to PHP format usable by the configurator. */
+	public function convert($input);
+
+}

http://git-wip-us.apache.org/repos/asf/logging-log4php/blob/79ed2d0d/src/Configuration/adapters/IniAdapter.php
----------------------------------------------------------------------
diff --git a/src/Configuration/adapters/IniAdapter.php b/src/Configuration/adapters/IniAdapter.php
new file mode 100644
index 0000000..61c003b
--- /dev/null
+++ b/src/Configuration/adapters/IniAdapter.php
@@ -0,0 +1,294 @@
+<?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.
+ */
+
+namespace Apache\Log4php\Configuration\Adapters;
+
+use Apache\Log4php\LoggerException;
+
+/**
+ * Converts ini configuration files to a PHP array.
+ *
+ * These used to be called "properties" files (inherited from log4j), and that
+ * file extension is still supported.
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
+ * @since 2.2
+ */
+class IniAdapter implements AdapterInterface {
+
+	/** Name to assign to the root logger. */
+	const ROOT_LOGGER_NAME = "root";
+
+	/** Prefix used for defining logger additivity. */
+	const ADDITIVITY_PREFIX = "log4php.additivity.";
+
+	/** Prefix used for defining logger threshold. */
+	const THRESHOLD_PREFIX = "log4php.threshold";
+
+	/** Prefix used for defining the root logger. */
+	const ROOT_LOGGER_PREFIX = "log4php.rootLogger";
+
+	/** Prefix used for defining a logger. */
+	const LOGGER_PREFIX = "log4php.logger.";
+
+	/** Prefix used for defining an appender. */
+	const APPENDER_PREFIX = "log4php.appender.";
+
+	/** Prefix used for defining a renderer. */
+	const RENDERER_PREFIX = "log4php.renderer.";
+
+	/** Holds the configuration. */
+	private $config = array();
+
+	/**
+	 * Loads and parses the INI configuration file.
+	 *
+	 * @param string $url Path to the config file.
+	 * @throws LoggerException
+	 */
+	private function load($url) {
+		if (!file_exists($url)) {
+			throw new LoggerException("File [$url] does not exist.");
+		}
+
+		$properties = @parse_ini_file($url, true);
+		if ($properties === false) {
+			$error = error_get_last();
+			throw new LoggerException("Error parsing configuration file: {$error['message']}");
+		}
+
+		return $properties;
+	}
+
+	/**
+	* Converts the provided INI configuration file to a PHP array config.
+	*
+	* @param string $path Path to the config file.
+	* @throws LoggerException If the file cannot be loaded or parsed.
+	*/
+	public function convert($path) {
+		// Load the configuration
+		$properties = $this->load($path);
+
+		// Parse threshold
+		if (isset($properties[self::THRESHOLD_PREFIX])) {
+			$this->config['threshold'] = $properties[self::THRESHOLD_PREFIX];
+		}
+
+		// Parse root logger
+		if (isset($properties[self::ROOT_LOGGER_PREFIX])) {
+			$this->parseLogger($properties[self::ROOT_LOGGER_PREFIX], self::ROOT_LOGGER_NAME);
+		}
+
+		$appenders = array();
+
+		foreach($properties as $key => $value) {
+			// Parse loggers
+			if ($this->beginsWith($key, self::LOGGER_PREFIX)) {
+				$name = substr($key, strlen(self::LOGGER_PREFIX));
+				$this->parseLogger($value, $name);
+			}
+
+			// Parse additivity
+			if ($this->beginsWith($key, self::ADDITIVITY_PREFIX)) {
+				$name = substr($key, strlen(self::ADDITIVITY_PREFIX));
+				$this->config['loggers'][$name]['additivity'] = $value;
+			}
+
+			// Parse appenders
+			else if ($this->beginsWith($key, self::APPENDER_PREFIX)) {
+				$this->parseAppender($key, $value);
+			}
+
+			// Parse renderers
+			else if ($this->beginsWith($key, self::RENDERER_PREFIX)) {
+				$this->parseRenderer($key, $value);
+			}
+		}
+
+		return $this->config;
+	}
+
+
+	/**
+	 * Parses a logger definition.
+	 *
+	 * Loggers are defined in the following manner:
+	 * <pre>
+	 * log4php.logger.<name> = [<level>], [<appender-ref>, <appender-ref>, ...]
+	 * </pre>
+	 *
+	 * @param string $value The configuration value (level and appender-refs).
+	 * @param string $name Logger name.
+	 */
+	private function parseLogger($value, $name) {
+		// Value is divided by commas
+		$parts = explode(',', $value);
+		if (empty($value) || empty($parts)) {
+			return;
+		}
+
+		// The first value is the logger level
+		$level = array_shift($parts);
+
+		// The remaining values are appender references
+		$appenders = array();
+		while($appender = array_shift($parts)) {
+			$appender = trim($appender);
+			if (!empty($appender)) {
+				$appenders[] = trim($appender);
+			}
+		}
+
+		// Find the target configuration
+		if ($name == self::ROOT_LOGGER_NAME) {
+			$this->config['rootLogger']['level'] = trim($level);
+			$this->config['rootLogger']['appenders'] = $appenders;
+		} else {
+			$this->config['loggers'][$name]['level'] = trim($level);
+			$this->config['loggers'][$name]['appenders'] = $appenders;
+		}
+	}
+
+	/**
+	 * Parses an configuration line pertaining to an appender.
+	 *
+	 * Parses the following patterns:
+	 *
+	 * Appender class:
+	 * <pre>
+	 * log4php.appender.<name> = <class>
+	 * </pre>
+	 *
+	 * Appender parameter:
+	 * <pre>
+	 * log4php.appender.<name>.<param> = <value>
+	 * </pre>
+	 *
+ 	 * Appender threshold:
+	 * <pre>
+	 * log4php.appender.<name>.threshold = <level>
+	 * </pre>
+	 *
+ 	 * Appender layout:
+	 * <pre>
+	 * log4php.appender.<name>.layout = <layoutClass>
+	 * </pre>
+	 *
+	 * Layout parameter:
+	 * <pre>
+	 * log4php.appender.<name>.layout.<param> = <value>
+	 * </pre>
+	 *
+	 * For example, a full appender config might look like:
+	 * <pre>
+	 * log4php.appender.myAppender = ConsoleAppender
+	 * log4php.appender.myAppender.threshold = info
+	 * log4php.appender.myAppender.target = stdout
+	 * log4php.appender.myAppender.layout = PatternLayout
+	 * log4php.appender.myAppender.layout.conversionPattern = "%d %c: %m%n"
+	 * </pre>
+	 *
+	 * After parsing all these options, the following configuration can be
+	 * found under $this->config['appenders']['myAppender']:
+	 * <pre>
+	 * array(
+	 * 	'class' => ConsoleAppender,
+	 * 	'threshold' => info,
+	 * 	'params' => array(
+	 * 		'target' => 'stdout'
+	 * 	),
+	 * 	'layout' => array(
+	 * 		'class' => 'ConsoleAppender',
+	 * 		'params' => array(
+	 * 			'conversionPattern' => '%d %c: %m%n'
+	 * 		)
+	 * 	)
+	 * )
+	 * </pre>
+	 *
+	 * @param string $key
+	 * @param string $value
+	 */
+	private function parseAppender($key, $value) {
+
+		// Remove the appender prefix from key
+		$subKey = substr($key, strlen(self::APPENDER_PREFIX));
+
+		// Divide the string by dots
+		$parts = explode('.', $subKey);
+		$count = count($parts);
+
+		// The first part is always the appender name
+		$name = trim($parts[0]);
+
+		// Only one part - this line defines the appender class
+		if ($count == 1) {
+			$this->config['appenders'][$name]['class'] = $value;
+			return;
+		}
+
+		// Two parts - either a parameter, a threshold or layout class
+		else if ($count == 2) {
+
+			if ($parts[1] == 'layout') {
+				$this->config['appenders'][$name]['layout']['class'] = $value;
+				return;
+			} else if ($parts[1] == 'threshold') {
+				$this->config['appenders'][$name]['threshold'] = $value;
+				return;
+			} else {
+				$this->config['appenders'][$name]['params'][$parts[1]] = $value;
+				return;
+			}
+		}
+
+		// Three parts - this can only be a layout parameter
+		else if ($count == 3) {
+			if ($parts[1] == 'layout') {
+				$this->config['appenders'][$name]['layout']['params'][$parts[2]] = $value;
+				return;
+			}
+		}
+
+		trigger_error("log4php: Don't know how to parse the following line: \"$key = $value\". Skipping.");
+	}
+
+	/**
+	 * Parses a renderer definition.
+	 *
+	 * Renderers are defined as:
+	 * <pre>
+	 * log4php.renderer.<renderedClass> = <renderingClass>
+	 * </pre>
+	 *
+	 * @param string $key log4php.renderer.<renderedClass>
+	 * @param string $value <renderingClass>
+	 */
+	private function parseRenderer($key, $value) {
+		// Remove the appender prefix from key
+		$renderedClass = substr($key, strlen(self::APPENDER_PREFIX));
+		$renderingClass = $value;
+
+		$this->config['renderers'][] = compact('renderedClass', 'renderingClass');
+	}
+
+	/** Helper method. Returns true if $str begins with $sub. */
+	private function beginsWith($str, $sub) {
+		return (strncmp($str, $sub, strlen($sub)) == 0);
+	}
+}

http://git-wip-us.apache.org/repos/asf/logging-log4php/blob/79ed2d0d/src/Configuration/adapters/PhpAdapter.php
----------------------------------------------------------------------
diff --git a/src/Configuration/adapters/PhpAdapter.php b/src/Configuration/adapters/PhpAdapter.php
new file mode 100644
index 0000000..71d245b
--- /dev/null
+++ b/src/Configuration/adapters/PhpAdapter.php
@@ -0,0 +1,82 @@
+<?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.
+ */
+
+namespace Apache\Log4php\Configuration\Adapters;
+
+use Apache\Log4php\LoggerException;
+
+/**
+ * Converts PHP configuration files to a PHP array.
+ *
+ * The file should only hold the PHP config array preceded by "return".
+ *
+ * Example PHP config file:
+ * <code>
+ * <?php
+ * return array(
+ *   'rootLogger' => array(
+ *     'level' => 'info',
+ *     'appenders' => array('default')
+ *   ),
+ *   'appenders' => array(
+ *     'default' => array(
+ *       'class' => 'EchoAppender',
+ *       'layout' => array(
+ *       	'class' => 'SimpleLayout'
+ *        )
+ *     )
+ *   )
+ * )
+ * ?>
+ * </code>
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
+ * @since 2.2
+ */
+class PhpAdapter implements AdapterInterface
+{
+	public function convert($url) {
+		if (!file_exists($url)) {
+			throw new LoggerException("File [$url] does not exist.");
+		}
+
+		// Load the config file
+		$data = @file_get_contents($url);
+		if ($data === false) {
+			$error = error_get_last();
+			throw new LoggerException("Error loading config file: {$error['message']}");
+		}
+
+		$config = @eval('?>' . $data);
+
+		if ($config === false) {
+			$error = error_get_last();
+			throw new LoggerException("Error parsing configuration: " . $error['message']);
+		}
+
+		if (empty($config)) {
+			throw new LoggerException("Invalid configuration: empty configuration array.");
+		}
+
+		if (!is_array($config)) {
+			throw new LoggerException("Invalid configuration: not an array.");
+		}
+
+		return $config;
+	}
+}
+

http://git-wip-us.apache.org/repos/asf/logging-log4php/blob/79ed2d0d/src/Configuration/adapters/XmlAdapter.php
----------------------------------------------------------------------
diff --git a/src/Configuration/adapters/XmlAdapter.php b/src/Configuration/adapters/XmlAdapter.php
new file mode 100644
index 0000000..d7495b8
--- /dev/null
+++ b/src/Configuration/adapters/XmlAdapter.php
@@ -0,0 +1,277 @@
+<?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.
+ */
+
+namespace Apache\Log4php\Configuration\Adapters;
+
+use Apache\Log4php\LoggerException;
+
+/**
+ * Converts XML configuration files to a PHP array.
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
+ * @since 2.2
+ */
+class XmlAdapter implements AdapterInterface
+{
+	/** Path to the XML schema used for validation. */
+	const SCHEMA_PATH = '/../xml/log4php.xsd';
+
+	private $config = array(
+		'appenders' => array(),
+		'loggers' => array(),
+		'renderers' => array(),
+	);
+
+	public function convert($url) {
+		$xml = $this->loadXML($url);
+
+		$this->parseConfiguration($xml);
+
+		// Parse the <root> node
+		if (isset($xml->root)) {
+			$this->parseRootLogger($xml->root);
+		}
+
+		// Process <logger> nodes
+		foreach($xml->logger as $logger) {
+			$this->parseLogger($logger);
+		}
+
+		// Process <appender> nodes
+		foreach($xml->appender as $appender) {
+			$this->parseAppender($appender);
+		}
+
+		// Process <renderer> nodes
+		foreach($xml->renderer as $rendererNode) {
+			$this->parseRenderer($rendererNode);
+		}
+
+		// Process <defaultRenderer> node
+		foreach($xml->defaultRenderer as $rendererNode) {
+			$this->parseDefaultRenderer($rendererNode);
+		}
+
+		return $this->config;
+	}
+
+	/**
+	 * Loads and validates the XML.
+	 * @param string $url Input XML.
+	 */
+	private function loadXML($url) {
+		if (!file_exists($url)) {
+			throw new LoggerException("File [$url] does not exist.");
+		}
+
+		libxml_clear_errors();
+		$oldValue = libxml_use_internal_errors(true);
+
+		// Load XML
+		$xml = @simplexml_load_file($url);
+		if ($xml === false) {
+
+			$errorStr = "";
+			foreach(libxml_get_errors() as $error) {
+				$errorStr .= $error->message;
+			}
+
+			throw new LoggerException("Error loading configuration file: " . trim($errorStr));
+		}
+
+		libxml_clear_errors();
+		libxml_use_internal_errors($oldValue);
+
+		return $xml;
+	}
+
+	/**
+	 * Parses the <configuration> node.
+	 */
+	private function parseConfiguration(\SimpleXMLElement $xml) {
+		$attributes = $xml->attributes();
+		if (isset($attributes['threshold'])) {
+			$this->config['threshold'] = (string) $attributes['threshold'];
+		}
+	}
+
+	/** Parses an <appender> node. */
+	private function parseAppender(\SimpleXMLElement $node) {
+		$name = $this->getAttributeValue($node, 'name');
+		if (empty($name)) {
+			$this->warn("An <appender> node is missing the required 'name' attribute. Skipping appender definition.");
+			return;
+		}
+
+		$appender = array();
+		$appender['class'] = $this->getAttributeValue($node, 'class');
+
+		if (isset($node['threshold'])) {
+			$appender['threshold'] = $this->getAttributeValue($node, 'threshold');
+		}
+
+		if (isset($node->layout)) {
+			$appender['layout']= $this->parseLayout($node->layout, $name);
+		}
+
+		if (count($node->param) > 0) {
+			$appender['params'] = $this->parseParameters($node);
+		}
+
+		foreach($node->filter as $filterNode) {
+			$appender['filters'][] = $this->parseFilter($filterNode);
+		}
+
+		$this->config['appenders'][$name] = $appender;
+	}
+
+	/** Parses a <layout> node. */
+	private function parseLayout(\SimpleXMLElement $node, $appenderName) {
+		$layout = array();
+		$layout['class'] = $this->getAttributeValue($node, 'class');
+
+		if (count($node->param) > 0) {
+			$layout['params'] = $this->parseParameters($node);
+		}
+
+		return $layout;
+	}
+
+	/** Parses any <param> child nodes returning them in an array. */
+	private function parseParameters($paramsNode) {
+		$params = array();
+
+		foreach($paramsNode->param as $paramNode) {
+			if (empty($paramNode['name'])) {
+				$this->warn("A <param> node is missing the required 'name' attribute. Skipping parameter.");
+				continue;
+			}
+
+			$name = $this->getAttributeValue($paramNode, 'name');
+			$value = $this->getAttributeValue($paramNode, 'value');
+
+			$params[$name] = $value;
+		}
+
+		return $params;
+	}
+
+	/** Parses a <root> node. */
+	private function parseRootLogger(\SimpleXMLElement $node) {
+		$logger = array();
+
+		if (isset($node->level)) {
+			$logger['level'] = $this->getAttributeValue($node->level, 'value');
+		}
+
+		$logger['appenders'] = $this->parseAppenderReferences($node);
+
+		$this->config['rootLogger'] = $logger;
+	}
+
+	/** Parses a <logger> node. */
+	private function parseLogger(\SimpleXMLElement $node) {
+		$logger = array();
+
+		$name = $this->getAttributeValue($node, 'name');
+		if (empty($name)) {
+			$this->warn("A <logger> node is missing the required 'name' attribute. Skipping logger definition.");
+			return;
+		}
+
+		if (isset($node->level)) {
+			$logger['level'] = $this->getAttributeValue($node->level, 'value');
+		}
+
+		if (isset($node['additivity'])) {
+			$logger['additivity'] = $this->getAttributeValue($node, 'additivity');
+		}
+
+		$logger['appenders'] = $this->parseAppenderReferences($node);
+
+		// Check for duplicate loggers
+		if (isset($this->config['loggers'][$name])) {
+			$this->warn("Duplicate logger definition [$name]. Overwriting.");
+		}
+
+		$this->config['loggers'][$name] = $logger;
+	}
+
+	/**
+	 * Parses a <logger> node for appender references and returns them in an array.
+	 *
+	 * Previous versions supported appender-ref, as well as appender_ref so both
+	 * are parsed for backward compatibility.
+	 */
+	private function parseAppenderReferences(\SimpleXMLElement $node) {
+		$refs = array();
+		foreach($node->appender_ref as $ref) {
+			$refs[] = $this->getAttributeValue($ref, 'ref');
+		}
+
+		foreach($node->{'appender-ref'} as $ref) {
+			$refs[] = $this->getAttributeValue($ref, 'ref');
+		}
+
+		return $refs;
+	}
+
+	/** Parses a <filter> node. */
+	private function parseFilter($filterNode) {
+		$filter = array();
+		$filter['class'] = $this->getAttributeValue($filterNode, 'class');
+
+		if (count($filterNode->param) > 0) {
+			$filter['params'] = $this->parseParameters($filterNode);
+		}
+
+		return $filter;
+	}
+
+	/** Parses a <renderer> node. */
+	private function parseRenderer(\SimpleXMLElement $node) {
+		$renderedClass = $this->getAttributeValue($node, 'renderedClass');
+		$renderingClass = $this->getAttributeValue($node, 'renderingClass');
+
+		$this->config['renderers'][] = compact('renderedClass', 'renderingClass');
+	}
+
+	/** Parses a <defaultRenderer> node. */
+	private function parseDefaultRenderer(\SimpleXMLElement $node) {
+		$renderingClass = $this->getAttributeValue($node, 'renderingClass');
+
+		// Warn on duplicates
+		if(isset($this->config['defaultRenderer'])) {
+			$this->warn("Duplicate <defaultRenderer> node. Overwriting.");
+		}
+
+		$this->config['defaultRenderer'] = $renderingClass;
+	}
+
+	// ******************************************
+	// ** Helper methods                       **
+	// ******************************************
+
+	private function getAttributeValue(\SimpleXMLElement $node, $name) {
+		return isset($node[$name]) ? (string) $node[$name] : null;
+	}
+
+	private function warn($message) {
+		trigger_error("log4php: " . $message, E_USER_WARNING);
+	}
+}
+

http://git-wip-us.apache.org/repos/asf/logging-log4php/blob/79ed2d0d/src/Filters/AbstractFilter.php
----------------------------------------------------------------------
diff --git a/src/Filters/AbstractFilter.php b/src/Filters/AbstractFilter.php
new file mode 100644
index 0000000..81663e3
--- /dev/null
+++ b/src/Filters/AbstractFilter.php
@@ -0,0 +1,126 @@
+<?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.
+ */
+
+namespace Apache\Log4php\Filters;
+
+use Apache\Log4php\Configurable;
+use Apache\Log4php\LoggingEvent;
+
+/**
+ * Users should extend this class to implement customized logging
+ * event filtering. Note that {@link LoggerCategory} and {@link Appender},
+ * the parent class of all standard
+ * appenders, have built-in filtering rules. It is suggested that you
+ * first use and understand the built-in rules before rushing to write
+ * your own custom filters.
+ *
+ * <p>This abstract class assumes and also imposes that filters be
+ * organized in a linear chain. The {@link #decide
+ * decide(LoggingEvent)} method of each filter is called sequentially,
+ * in the order of their addition to the chain.
+ *
+ * <p>The {@link decide()} method must return one
+ * of the integer constants {@link AbstractFilter::DENY},
+ * {@link AbstractFilter::NEUTRAL} or {@link AbstractFilter::ACCEPT}.
+ *
+ * <p>If the value {@link AbstractFilter::DENY} is returned, then the log event is
+ * dropped immediately without consulting with the remaining
+ * filters.
+ *
+ * <p>If the value {@link AbstractFilter::NEUTRAL} is returned, then the next filter
+ * in the chain is consulted. If there are no more filters in the
+ * chain, then the log event is logged. Thus, in the presence of no
+ * filters, the default behaviour is to log all logging events.
+ *
+ * <p>If the value {@link AbstractFilter::ACCEPT} is returned, then the log
+ * event is logged without consulting the remaining filters.
+ *
+ * <p>The philosophy of log4php filters is largely inspired from the
+ * Linux ipchains.
+ */
+abstract class AbstractFilter extends Configurable {
+
+	/**
+	 * The log event must be logged immediately without consulting with
+	 * the remaining filters, if any, in the chain.
+	 */
+	const ACCEPT = 1;
+
+	/**
+	 * This filter is neutral with respect to the log event. The
+	 * remaining filters, if any, should be consulted for a final decision.
+	 */
+	const NEUTRAL = 0;
+
+	/**
+	 * The log event must be dropped immediately without consulting
+	 * with the remaining filters, if any, in the chain.
+	 */
+	const DENY = -1;
+
+	/**
+	 * @var AbstractFilter Points to the next {@link AbstractFilter} in the filter chain.
+	 */
+	protected $next;
+
+	/**
+	 * Usually filters options become active when set. We provide a
+	 * default do-nothing implementation for convenience.
+	*/
+	public function activateOptions() {
+	}
+
+	/**
+	 * Decide what to do.
+	 * <p>If the decision is {@link AbstractFilter::DENY}, then the event will be
+	 * dropped. If the decision is {@link AbstractFilter::NEUTRAL}, then the next
+	 * filter, if any, will be invoked. If the decision is {@link AbstractFilter::ACCEPT} then
+	 * the event will be logged without consulting with other filters in
+	 * the chain.
+	 *
+	 * @param LoggingEvent $event The {@link LoggingEvent} to decide upon.
+	 * @return integer {@link AbstractFilter::NEUTRAL} or {@link AbstractFilter::DENY}|{@link AbstractFilter::ACCEPT}
+	 */
+	public function decide(LoggingEvent $event) {
+		return self::NEUTRAL;
+	}
+
+	/**
+	 * Adds a new filter to the filter chain this filter is a part of.
+	 * If this filter has already and follow up filter, the param filter
+	 * is passed on until it is the last filter in chain.
+	 *
+	 * @param $filter - the filter to add to this chain
+	 */
+	public function addNext($filter) {
+		if($this->next !== null) {
+			$this->next->addNext($filter);
+		} else {
+			$this->next = $filter;
+		}
+	}
+
+	/**
+	 * Returns the next filter in this chain
+	 * @return the next filter
+	 */
+	public function getNext() {
+		return $this->next;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/logging-log4php/blob/79ed2d0d/src/Filters/DenyAllFilter.php
----------------------------------------------------------------------
diff --git a/src/Filters/DenyAllFilter.php b/src/Filters/DenyAllFilter.php
new file mode 100644
index 0000000..1d34357
--- /dev/null
+++ b/src/Filters/DenyAllFilter.php
@@ -0,0 +1,45 @@
+<?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.
+ */
+
+namespace Apache\Log4php\Filters;
+
+use Apache\Log4php\LoggingEvent;
+
+/**
+ * This filter drops all logging events.
+ *
+ * You can add this filter to the end of a filter chain to
+ * switch from the default "accept all unless instructed otherwise"
+ * filtering behaviour to a "deny all unless instructed otherwise"
+ * behaviour.
+ *
+ * @since 0.3
+ */
+class DenyAllFilter extends AbstractFilter {
+
+	/**
+	 * Always returns the integer constant {@link AbstractFilter::DENY}
+	 * regardless of the {@link LoggingEvent} parameter.
+	 *
+	 * @param LoggingEvent $event The {@link LoggingEvent} to filter.
+	 * @return AbstractFilter::DENY Always returns {@link AbstractFilter::DENY}
+	 */
+	public function decide(LoggingEvent $event) {
+		return AbstractFilter::DENY;
+	}
+}

http://git-wip-us.apache.org/repos/asf/logging-log4php/blob/79ed2d0d/src/Filters/LevelMatchFilter.php
----------------------------------------------------------------------
diff --git a/src/Filters/LevelMatchFilter.php b/src/Filters/LevelMatchFilter.php
new file mode 100644
index 0000000..4573c4c
--- /dev/null
+++ b/src/Filters/LevelMatchFilter.php
@@ -0,0 +1,98 @@
+<?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.
+ */
+
+namespace Apache\Log4php\Filters;
+
+use Apache\Log4php\LoggingEvent;
+
+/**
+ * This is a very simple filter based on level matching.
+ *
+ * <p>The filter admits two options <b><var>LevelToMatch</var></b> and
+ * <b><var>AcceptOnMatch</var></b>. If there is an exact match between the value
+ * of the <b><var>LevelToMatch</var></b> option and the level of the
+ * {@link LoggingEvent}, then the {@link decide()} method returns
+ * {@link AbstractFilter::ACCEPT} in case the <b><var>AcceptOnMatch</var></b>
+ * option value is set to <i>true</i>, if it is <i>false</i> then
+ * {@link AbstractFilter::DENY} is returned. If there is no match,
+ * {@link AbstractFilter::NEUTRAL} is returned.</p>
+ *
+ * <p>
+ * An example for this filter:
+ *
+ * {@example ../../examples/php/filter_levelmatch.php 19}
+ *
+ * <p>
+ * The corresponding XML file:
+ *
+ * {@example ../../examples/resources/filter_levelmatch.xml 18}
+ * @since 0.6
+ */
+class LevelMatchFilter extends AbstractFilter {
+
+	/**
+	 * Indicates if this event should be accepted or denied on match
+	 * @var boolean
+	 */
+	protected $acceptOnMatch = true;
+
+	/**
+	 * The level, when to match
+	 * @var Level
+	 */
+	protected $levelToMatch;
+
+	/**
+	 * @param boolean $acceptOnMatch
+	 */
+	public function setAcceptOnMatch($acceptOnMatch) {
+		$this->setBoolean('acceptOnMatch', $acceptOnMatch);
+	}
+
+	/**
+	 * @param string $l the level to match
+	 */
+	public function setLevelToMatch($level) {
+		$this->setLevel('levelToMatch', $level);
+	}
+
+	/**
+	 * Return the decision of this filter.
+	 *
+	 * Returns {@link AbstractFilter::NEUTRAL} if the <b><var>LevelToMatch</var></b>
+	 * option is not set or if there is not match.	Otherwise, if there is a
+	 * match, then the returned decision is {@link AbstractFilter::ACCEPT} if the
+	 * <b><var>AcceptOnMatch</var></b> property is set to <i>true</i>. The
+	 * returned decision is {@link AbstractFilter::DENY} if the
+	 * <b><var>AcceptOnMatch</var></b> property is set to <i>false</i>.
+	 *
+	 * @param LoggingEvent $event
+	 * @return integer
+	 */
+	public function decide(LoggingEvent $event) {
+		if($this->levelToMatch === null) {
+			return AbstractFilter::NEUTRAL;
+		}
+
+		if($this->levelToMatch->equals($event->getLevel())) {
+			return $this->acceptOnMatch ? AbstractFilter::ACCEPT : AbstractFilter::DENY;
+		} else {
+			return AbstractFilter::NEUTRAL;
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/logging-log4php/blob/79ed2d0d/src/Filters/LevelRangeFilter.php
----------------------------------------------------------------------
diff --git a/src/Filters/LevelRangeFilter.php b/src/Filters/LevelRangeFilter.php
new file mode 100644
index 0000000..d30f5ae
--- /dev/null
+++ b/src/Filters/LevelRangeFilter.php
@@ -0,0 +1,136 @@
+<?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.
+ */
+
+namespace Apache\Log4php\Filters;
+
+use Apache\Log4php\LoggingEvent;
+
+/**
+ * This is a very simple filter based on level matching, which can be
+ * used to reject messages with priorities outside a certain range.
+ *
+ * <p>The filter admits three options <b><var>LevelMin</var></b>, <b><var>LevelMax</var></b>
+ * and <b><var>AcceptOnMatch</var></b>.</p>
+ *
+ * <p>If the level of the {@link LoggingEvent} is not between Min and Max
+ * (inclusive), then {@link AbstractFilter::DENY} is returned.</p>
+ *
+ * <p>If the Logging event level is within the specified range, then if
+ * <b><var>AcceptOnMatch</var></b> is <i>true</i>,
+ * {@link AbstractFilter::ACCEPT} is returned, and if
+ * <b><var>AcceptOnMatch</var></b> is <i>false</i>,
+ * {@link AbstractFilter::NEUTRAL} is returned.</p>
+ *
+ * <p>If <b><var>LevelMin</var></b> is not defined, then there is no
+ * minimum acceptable level (i.e. a level is never rejected for
+ * being too "low"/unimportant).  If <b><var>LevelMax</var></b> is not
+ * defined, then there is no maximum acceptable level (ie a
+ * level is never rejected for being too "high"/important).</p>
+ *
+ * <p>Refer to the {@link Appender::setThreshold()} method
+ * available to <b>all</b> appenders extending {@link Appender}
+ * for a more convenient way to filter out events by level.</p>
+ *
+ * <p>
+ * An example for this filter:
+ *
+ * {@example ../../examples/php/filter_levelrange.php 19}
+ *
+ * <p>
+ * The corresponding XML file:
+ *
+ * {@example ../../examples/resources/filter_levelrange.xml 18}
+ *
+ * @author Simon Kitching
+ * @author based on the org.apache.log4j.varia.LevelRangeFilte Java code by Ceki G&uuml;lc&uuml;
+ * @since 0.6
+ */
+class LevelRangeFilter extends AbstractFilter {
+
+	/**
+	 * @var boolean
+	 */
+	protected $acceptOnMatch = true;
+
+	/**
+	 * @var Level
+	 */
+	protected $levelMin;
+
+	/**
+	 * @var Level
+	 */
+	protected $levelMax;
+
+	/**
+	 * @param boolean $acceptOnMatch
+	 */
+	public function setAcceptOnMatch($acceptOnMatch) {
+		$this->setBoolean('acceptOnMatch', $acceptOnMatch);
+	}
+
+	/**
+	 * @param string $l the level min to match
+	 */
+	public function setLevelMin($level) {
+		$this->setLevel('levelMin', $level);
+	}
+
+	/**
+	 * @param string $l the level max to match
+	 */
+	public function setLevelMax($level) {
+		$this->setLevel('levelMax', $level);
+	}
+
+	/**
+	 * Return the decision of this filter.
+	 *
+	 * @param LoggingEvent $event
+	 * @return integer
+	 */
+	public function decide(LoggingEvent $event) {
+		$level = $event->getLevel();
+
+		if($this->levelMin !== null) {
+			if($level->isGreaterOrEqual($this->levelMin) == false) {
+				// level of event is less than minimum
+				return AbstractFilter::DENY;
+			}
+		}
+
+		if($this->levelMax !== null) {
+			if($level->toInt() > $this->levelMax->toInt()) {
+				// level of event is greater than maximum
+				// Alas, there is no Level.isGreater method. and using
+				// a combo of isGreaterOrEqual && !Equal seems worse than
+				// checking the int values of the level objects..
+				return AbstractFilter::DENY;
+			}
+		}
+
+		if($this->acceptOnMatch) {
+			// this filter set up to bypass later filters and always return
+			// accept if level in range
+			return AbstractFilter::ACCEPT;
+		} else {
+			// event is ok for this filter; allow later filters to have a look..
+			return AbstractFilter::NEUTRAL;
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/logging-log4php/blob/79ed2d0d/src/Filters/StringMatchFilter.php
----------------------------------------------------------------------
diff --git a/src/Filters/StringMatchFilter.php b/src/Filters/StringMatchFilter.php
new file mode 100644
index 0000000..152c9c9
--- /dev/null
+++ b/src/Filters/StringMatchFilter.php
@@ -0,0 +1,87 @@
+<?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.
+ */
+
+namespace Apache\Log4php\Filters;
+
+use Apache\Log4php\LoggingEvent;
+
+/**
+ * This is a very simple filter based on string matching.
+ *
+ * <p>The filter admits two options {@link $stringToMatch} and
+ * {@link $acceptOnMatch}. If there is a match (using {@link PHP_MANUAL#strpos}
+ * between the value of the {@link $stringToMatch} option and the message
+ * of the {@link LoggingEvent},
+ * then the {@link decide()} method returns {@link AbstractFilter::ACCEPT} if
+ * the <b>AcceptOnMatch</b> option value is true, if it is false then
+ * {@link AbstractFilter::DENY} is returned. If there is no match, {@link AbstractFilter::NEUTRAL}
+ * is returned.</p>
+ *
+ * <p>
+ * An example for this filter:
+ *
+ * {@example ../../examples/php/filter_stringmatch.php 19}
+ *
+ * <p>
+ * The corresponding XML file:
+ *
+ * {@example ../../examples/resources/filter_stringmatch.xml 18}
+ * @since 0.3
+ */
+class StringMatchFilter extends AbstractFilter {
+
+	/**
+	 * @var boolean
+	 */
+	protected $acceptOnMatch = true;
+
+	/**
+	 * @var string
+	 */
+	protected $stringToMatch;
+
+	/**
+	 * @param mixed $acceptOnMatch a boolean or a string ('true' or 'false')
+	 */
+	public function setAcceptOnMatch($acceptOnMatch) {
+		$this->setBoolean('acceptOnMatch', $acceptOnMatch);
+	}
+
+	/**
+	 * @param string $s the string to match
+	 */
+	public function setStringToMatch($string) {
+		$this->setString('stringToMatch', $string);
+	}
+
+	/**
+	 * @return integer a {@link LOGGER_FILTER_NEUTRAL} is there is no string match.
+	 */
+	public function decide(LoggingEvent $event) {
+		$msg = $event->getRenderedMessage();
+
+		if($msg === null or $this->stringToMatch === null) {
+			return AbstractFilter::NEUTRAL;
+		}
+
+		if(strpos($msg, $this->stringToMatch) !== false ) {
+			return ($this->acceptOnMatch) ? AbstractFilter::ACCEPT : AbstractFilter::DENY;
+		}
+		return AbstractFilter::NEUTRAL;
+	}
+}