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:55 UTC

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

http://git-wip-us.apache.org/repos/asf/logging-log4php/blob/79ed2d0d/src/Helpers/FormattingInfo.php
----------------------------------------------------------------------
diff --git a/src/Helpers/FormattingInfo.php b/src/Helpers/FormattingInfo.php
new file mode 100644
index 0000000..9f2bb3b
--- /dev/null
+++ b/src/Helpers/FormattingInfo.php
@@ -0,0 +1,51 @@
+<?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\Helpers;
+
+/**
+ * This class encapsulates the information obtained when parsing
+ * formatting modifiers in conversion modifiers.
+ * @since 0.3
+ */
+class FormattingInfo {
+
+	/**
+	 * Minimal output length. If output is shorter than this value, it will be
+	 * padded with spaces.
+	 */
+	public $min = 0;
+
+	/**
+	 * Maximum output length. If output is longer than this value, it will be
+	 * trimmed.
+	 */
+	public $max = PHP_INT_MAX;
+
+	/**
+	 * Whether to pad the string from the left. If set to false, the string
+	 * will be padded from the right.
+	 */
+	public $padLeft = true;
+
+	/**
+	 * Whether to trim the string from the left. If set to false, the string
+	 * will be trimmed from the right.
+	 */
+	public $trimLeft = false;
+}

http://git-wip-us.apache.org/repos/asf/logging-log4php/blob/79ed2d0d/src/Helpers/OptionConverter.php
----------------------------------------------------------------------
diff --git a/src/Helpers/OptionConverter.php b/src/Helpers/OptionConverter.php
new file mode 100644
index 0000000..c496f02
--- /dev/null
+++ b/src/Helpers/OptionConverter.php
@@ -0,0 +1,225 @@
+<?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\Helpers;
+
+use Apache\Log4php\Level;
+use Apache\Log4php\LoggerException;
+
+/**
+ * A convenience class to convert property values to specific types.
+ * @since 0.5
+ */
+class OptionConverter {
+
+	/** String values which are converted to boolean TRUE. */
+	private static $trueValues = array('1', 'true', 'yes', 'on');
+
+	/**
+	 * String values which are converted to boolean FALSE.
+	 *
+	 * Note that an empty string must convert to false, because
+	 * parse_ini_file() which is used for parsing configuration
+	 * converts the value _false_ to an empty string.
+	 */
+	private static $falseValues = array('0', 'false', 'no', 'off', '');
+
+	/**
+	 * Read a predefined var.
+	 *
+	 * It returns a value referenced by <var>$key</var> using this search criteria:
+	 * - if <var>$key</var> is a constant then return it. Else
+	 * - if <var>$key</var> is set in <var>$_ENV</var> then return it. Else
+	 * - return <var>$def</var>.
+	 *
+	 * @param string $key The key to search for.
+	 * @param string $def The default value to return.
+	 * @return string	the string value of the system property, or the default
+	 *					value if there is no property with that key.
+	 */
+	public static function getSystemProperty($key, $def) {
+		if(defined($key)) {
+			return (string)constant($key);
+		} else if(isset($_SERVER[$key])) {
+			return (string)$_SERVER[$key];
+		} else if(isset($_ENV[$key])) {
+			return (string)$_ENV[$key];
+		} else {
+			return $def;
+		}
+	}
+
+	/** Converts $value to boolean, or throws an exception if not possible. */
+	public static function toBooleanEx($value) {
+		if (isset($value)) {
+			if (is_bool($value)) {
+				return $value;
+			}
+			$value = strtolower(trim($value));
+			if (in_array($value, self::$trueValues)) {
+				return true;
+			}
+			if (in_array($value, self::$falseValues)) {
+				return false;
+			}
+		}
+
+		throw new LoggerException("Given value [" . var_export($value, true) . "] cannot be converted to boolean.");
+	}
+
+	/**
+	 * Converts $value to integer, or throws an exception if not possible.
+	 * Floats cannot be converted to integer.
+	 */
+	public static function toIntegerEx($value) {
+		if (is_integer($value)) {
+			return $value;
+		}
+		if (is_numeric($value) && ($value == (integer) $value)) {
+			return (integer) $value;
+		}
+
+		throw new LoggerException("Given value [" . var_export($value, true) . "] cannot be converted to integer.");
+	}
+
+	/**
+	 * Converts $value to integer, or throws an exception if not possible.
+	 * Floats cannot be converted to integer.
+	 */
+	public static function toPositiveIntegerEx($value) {
+		if (is_integer($value) && $value > 0) {
+			return $value;
+		}
+		if (is_numeric($value) && ($value == (integer) $value) && $value > 0) {
+			return (integer) $value;
+		}
+
+		throw new LoggerException("Given value [" . var_export($value, true) . "] cannot be converted to a positive integer.");
+	}
+
+	/** Converts the value to a level. Throws an exception if not possible. */
+	public static function toLevelEx($value) {
+		if ($value instanceof Level) {
+			return $value;
+		}
+		$level = Level::toLevel($value);
+		if ($level === null) {
+			throw new LoggerException("Given value [" . var_export($value, true) . "] cannot be converted to a logger level.");
+		}
+		return $level;
+	}
+
+	/**
+	 * Converts a value to a valid file size (integer).
+	 *
+	 * Supports 'KB', 'MB' and 'GB' suffixes, where KB = 1024 B etc.
+	 *
+	 * The final value will be rounded to the nearest integer.
+	 *
+	 * Examples:
+	 * - '100' => 100
+	 * - '100.12' => 100
+	 * - '100KB' => 102400
+	 * - '1.5MB' => 1572864
+	 *
+	 * @param mixed $value File size (optionally with suffix).
+	 * @return integer Parsed file size.
+	 */
+	public static function toFileSizeEx($value) {
+
+		if (empty($value)) {
+			throw new LoggerException("Empty value cannot be converted to a file size.");
+		}
+
+		if (is_numeric($value)) {
+			return (integer) $value;
+		}
+
+		if (!is_string($value)) {
+			throw new LoggerException("Given value [" . var_export($value, true) . "] cannot be converted to a file size.");
+		}
+
+		$str = strtoupper(trim($value));
+		$count = preg_match('/^([0-9.]+)(KB|MB|GB)?$/', $str, $matches);
+
+		if ($count > 0) {
+			$size = $matches[1];
+			$unit = $matches[2];
+
+			switch($unit) {
+				case 'KB': $size *= pow(1024, 1); break;
+				case 'MB': $size *= pow(1024, 2); break;
+				case 'GB': $size *= pow(1024, 3); break;
+			}
+
+			return (integer) $size;
+		}
+
+		throw new LoggerException("Given value [$value] cannot be converted to a file size.");
+	}
+
+	/**
+	 * Converts a value to string, or throws an exception if not possible.
+	 *
+	 * Objects can be converted to string if they implement the magic
+	 * __toString() method.
+	 *
+	 */
+	public static function toStringEx($value) {
+		if (is_string($value)) {
+			return $value;
+		}
+		if (is_numeric($value)) {
+			return (string) $value;
+		}
+		if (is_object($value) && method_exists($value, '__toString')) {
+			return (string) $value;
+		}
+
+		throw new LoggerException("Given value [" . var_export($value, true) . "] cannot be converted to string.");
+	}
+
+	/**
+	 * Performs value substitution for string options.
+	 *
+	 * An option can contain PHP constants delimited by '${' and '}'.
+	 *
+	 * E.g. for input string "some ${FOO} value", the method will attempt
+	 * to substitute ${FOO} with the value of constant FOO if it exists.
+	 *
+	 * Therefore, if FOO is a constant, and it has value "bar", the resulting
+	 * string will be "some bar value".
+	 *
+	 * If the constant is not defined, it will be replaced by an empty string,
+	 * and the resulting string will be "some  value".
+	 *
+	 * @param string $string String on which to perform substitution.
+	 * @return string
+	 */
+	public static function substConstants($string) {
+		preg_match_all('/\${([^}]+)}/', $string, $matches);
+
+		foreach($matches[1] as $key => $match) {
+			$match = trim($match);
+			$search = $matches[0][$key];
+			$replacement = defined($match) ? constant($match) : '';
+			$string = str_replace($search, $replacement, $string);
+		}
+		return $string;
+	}
+}

http://git-wip-us.apache.org/repos/asf/logging-log4php/blob/79ed2d0d/src/Helpers/PatternParser.php
----------------------------------------------------------------------
diff --git a/src/Helpers/PatternParser.php b/src/Helpers/PatternParser.php
new file mode 100644
index 0000000..7e98a55
--- /dev/null
+++ b/src/Helpers/PatternParser.php
@@ -0,0 +1,245 @@
+<?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\Helpers;
+
+use Apache\Log4php\LoggerException;
+use Apache\Log4php\Pattern\AbstractConverter;
+use Apache\Log4php\Pattern\LiteralConverter;
+
+/**
+ * Most of the work of the {@link LoggerPatternLayout} class
+ * is delegated to the {@link PatternParser} class.
+ *
+ * <p>It is this class that parses conversion patterns and creates
+ * a chained list of {@link AbstractConverter} converters.</p>
+ *
+ * @since 0.3
+ */
+class PatternParser {
+
+	/** Escape character for conversion words in the conversion pattern. */
+	const ESCAPE_CHAR = '%';
+
+	/** Maps conversion words to relevant converters. */
+	private $converterMap;
+
+	/** Conversion pattern used in layout. */
+	private $pattern;
+
+	/** Regex pattern used for parsing the conversion pattern. */
+	private $regex;
+
+	/**
+	 * First converter in the chain.
+	 * @var AbstractConverter
+	 */
+	private $head;
+
+	/** Last converter in the chain. */
+	private $tail;
+
+	public function __construct($pattern, $converterMap) {
+		$this->pattern = $pattern;
+		$this->converterMap = $converterMap;
+
+		// Construct the regex pattern
+		$this->regex =
+			'/' .                       // Starting regex pattern delimiter
+			self::ESCAPE_CHAR .         // Character which marks the start of the conversion pattern
+			'(?P<modifiers>[0-9.-]*)' . // Format modifiers (optional)
+			'(?P<word>[a-zA-Z]+)' .     // The conversion word
+			'(?P<option>{[^}]*})?' .    // Conversion option in braces (optional)
+			'/';                        // Ending regex pattern delimiter
+	}
+
+	/**
+	 * Parses the conversion pattern string, converts it to a chain of pattern
+	 * converters and returns the first converter in the chain.
+	 *
+	 * @return AbstractConverter
+	 */
+	public function parse() {
+
+		// Skip parsing if the pattern is empty
+		if (empty($this->pattern)) {
+			$this->addLiteral('');
+			return $this->head;
+		}
+
+		// Find all conversion words in the conversion pattern
+		$count = preg_match_all($this->regex, $this->pattern, $matches, PREG_OFFSET_CAPTURE);
+		if ($count === false) {
+			$error = error_get_last();
+			throw new LoggerException("Failed parsing layotut pattern: {$error['message']}");
+		}
+
+		$prevEnd = 0;
+
+		foreach($matches[0] as $key => $item) {
+
+			// Locate where the conversion command starts and ends
+			$length = strlen($item[0]);
+			$start = $item[1];
+			$end = $item[1] + $length;
+
+			// Find any literal expressions between matched commands
+			if ($start > $prevEnd) {
+				$literal = substr($this->pattern, $prevEnd, $start - $prevEnd);
+				$this->addLiteral($literal);
+			}
+
+			// Extract the data from the matched command
+			$word = !empty($matches['word'][$key]) ? $matches['word'][$key][0] : null;
+			$modifiers = !empty($matches['modifiers'][$key]) ? $matches['modifiers'][$key][0] : null;
+			$option = !empty($matches['option'][$key]) ? $matches['option'][$key][0] : null;
+
+			// Create a converter and add it to the chain
+			$this->addConverter($word, $modifiers, $option);
+
+			$prevEnd = $end;
+		}
+
+		// Add any trailing literals
+		if ($end < strlen($this->pattern)) {
+			$literal = substr($this->pattern, $end);
+			$this->addLiteral($literal);
+		}
+
+		return $this->head;
+	}
+
+	/**
+	 * Adds a literal converter to the converter chain.
+	 * @param string $string The string for the literal converter.
+	 */
+	private function addLiteral($string) {
+		$converter = new LiteralConverter($string);
+		$this->addToChain($converter);
+	}
+
+	/**
+	 * Adds a non-literal converter to the converter chain.
+	 *
+	 * @param string $word The conversion word, used to determine which
+	 *  converter will be used.
+	 * @param string $modifiers Formatting modifiers.
+	 * @param string $option Option to pass to the converter.
+	 */
+	private function addConverter($word, $modifiers, $option) {
+ 		$formattingInfo = $this->parseModifiers($modifiers);
+		$option = trim($option, "{} ");
+
+		if (isset($this->converterMap[$word])) {
+			$converter = $this->getConverter($word, $formattingInfo, $option);
+			$this->addToChain($converter);
+		} else {
+			trigger_error("log4php: Invalid keyword '%$word' in converison pattern. Ignoring keyword.", E_USER_WARNING);
+		}
+	}
+
+	/**
+	 * Determines which converter to use based on the conversion word. Creates
+	 * an instance of the converter using the provided formatting info and
+	 * option and returns it.
+	 *
+	 * @param string $word The conversion word.
+	 * @param FormattingInfo $info Formatting info.
+	 * @param string $option Converter option.
+	 *
+	 * @throws LoggerException
+	 *
+	 * @return AbstractConverter
+	 */
+	private function getConverter($word, $info, $option) {
+		if (!isset($this->converterMap[$word])) {
+			throw new LoggerException("Invalid keyword '%$word' in converison pattern. Ignoring keyword.");
+		}
+
+		$class = $this->converterMap[$word];
+		if (class_exists($class)) {
+			$converter = new $class($info, $option);
+		} else {
+			$nsClass = "Apache\\Log4php\\Pattern\\$class";
+			if (class_exists($nsClass)) {
+				$converter = new $nsClass($info, $option);
+			}
+		}
+
+		if (!isset($converter)) {
+			throw new LoggerException("Class '$class' does not exist.");
+		}
+
+		if(!($converter instanceof AbstractConverter)) {
+			throw new LoggerException("Class '$class' is not an instance of AbstractConverter.");
+		}
+
+		return $converter;
+	}
+
+	/** Adds a converter to the chain and updates $head and $tail pointers. */
+	private function addToChain(AbstractConverter $converter) {
+		if (!isset($this->head)) {
+			$this->head = $converter;
+			$this->tail = $this->head;
+		} else {
+			$this->tail->next = $converter;
+			$this->tail = $this->tail->next;
+		}
+	}
+
+	/**
+	 * Parses the formatting modifiers and produces the corresponding
+	 * FormattingInfo object.
+	 *
+	 * @param string $modifier
+	 * @return FormattingInfo
+	 * @throws LoggerException
+	 */
+	private function parseModifiers($modifiers) {
+		$info = new FormattingInfo();
+
+		// If no modifiers are given, return default values
+		if (empty($modifiers)) {
+			return $info;
+		}
+
+		// Validate
+		$pattern = '/^(-?[0-9]+)?\.?-?[0-9]+$/';
+		if (!preg_match($pattern, $modifiers)) {
+			trigger_error("log4php: Invalid modifier in conversion pattern: [$modifiers]. Ignoring modifier.", E_USER_WARNING);
+			return $info;
+		}
+
+		$parts = explode('.', $modifiers);
+
+		if (!empty($parts[0])) {
+			$minPart = (integer) $parts[0];
+			$info->min = abs($minPart);
+			$info->padLeft = ($minPart > 0);
+		}
+
+		if (!empty($parts[1])) {
+			$maxPart = (integer) $parts[1];
+			$info->max = abs($maxPart);
+			$info->trimLeft = ($maxPart < 0);
+		}
+
+		return $info;
+	}
+}

http://git-wip-us.apache.org/repos/asf/logging-log4php/blob/79ed2d0d/src/Helpers/Utils.php
----------------------------------------------------------------------
diff --git a/src/Helpers/Utils.php b/src/Helpers/Utils.php
new file mode 100644
index 0000000..52be24d
--- /dev/null
+++ b/src/Helpers/Utils.php
@@ -0,0 +1,120 @@
+<?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\Helpers;
+
+/**
+ * Contains various helper methods.
+ * @since 2.3
+ */
+class Utils {
+
+	/**
+ 	 * Splits a fully qualified class name into fragments delimited by the
+ 	 * namespace separator (\).
+ 	 *
+ 	 * For backward compatibility, a dot (.) can be used as a delimiter as
+ 	 * well.
+	 *
+	 * @param string $name
+	 *
+	 * @return array Class name split into fragments.
+	 */
+	public static function tokenizeClassName($name) {
+		$name = str_replace('.', '\\', $name);
+		$name = trim($name, ' \\');
+		$fragments = explode('\\', $name);
+
+		foreach($fragments as $key => $fragment) {
+			if (trim($fragment) === '') {
+				unset($fragments[$key]);
+			}
+		}
+
+		return $fragments;
+	}
+
+	/**
+	 * Attempts to shorten the given class name to the desired length.
+	 *
+	 * This is done by separating the class name into fragments (delimited
+	 * by \ or .) and trimming individual fragments, starting with the left,
+	 * until desired length has been reached.
+	 *
+	 * The final fragment (i.e. class name) will never be shortened so the
+	 * result may still be longer than given length.
+	 *
+	 * @param string $name The (qualified) class name.
+	 * @param integer $length The length to shorten to. If null or 0 is given,
+	 * the name will be returned without shortening.
+	 */
+	public static function shortenClassName($name, $length) {
+		if ($length === null || $length < 0) {
+			return $name;
+		}
+
+		$name = str_replace('.', '\\', $name);
+		$name = trim($name, ' \\');
+
+		// Check if any shortening is required
+		$currentLength = strlen($name);
+		if ($currentLength <= $length) {
+			return $name;
+		}
+
+		// Split name into fragments
+		$fragments = explode('\\', $name);
+
+		// If zero length is specified, return only last fragment
+		if ($length == 0) {
+			return array_pop($fragments);
+		}
+
+		// If the name splits to only one fragment, then it cannot be shortened
+		$count = count($fragments);
+		if ($count == 1) {
+			return $name;
+		}
+
+		foreach($fragments as $key => &$fragment) {
+
+			// Never shorten last fragment
+			if ($key == $count - 1) {
+				break;
+			}
+
+			// Check for empty fragments (shouldn't happen but it's possible)
+			$fragLen = strlen($fragment);
+			if ($fragLen <= 1) {
+				continue;
+			}
+
+			// Shorten fragment to one character and check if total length satisfactory
+			$fragment = substr($fragment, 0, 1);
+			$currentLength = $currentLength - $fragLen + 1;
+
+			if ($currentLength <= $length) {
+				break;
+			}
+		}
+		unset($fragment);
+
+		return implode('\\', $fragments);
+	}
+}
+

http://git-wip-us.apache.org/repos/asf/logging-log4php/blob/79ed2d0d/src/Hierarchy.php
----------------------------------------------------------------------
diff --git a/src/Hierarchy.php b/src/Hierarchy.php
new file mode 100644
index 0000000..7b28a6b
--- /dev/null
+++ b/src/Hierarchy.php
@@ -0,0 +1,256 @@
+<?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\Renderers\RendererMap;
+
+/**
+ * This class is specialized in retrieving loggers by name and also maintaining
+ * the logger hierarchy. The logger hierarchy is dealing with the several Log-Levels
+ * Logger can have. From log4j website:
+ *
+ * "A logger is said to be an ancestor of another logger if its name followed
+ * by a dot is a prefix of the descendant logger name. A logger is said to be
+ * a parent of a child logger if there are no ancestors between itself and the
+ * descendant logger."
+ *
+ * Child Loggers do inherit their Log-Levels from their Ancestors. They can
+ * increase their Log-Level compared to their Ancestors, but they cannot decrease it.
+ *
+ * <p>The casual user does not have to deal with this class directly.</p>
+ *
+ * <p>The structure of the logger hierarchy is maintained by the
+ * getLogger method. The hierarchy is such that children link
+ * to their parent but parents do not have any pointers to their
+ * children. Moreover, loggers can be instantiated in any order, in
+ * particular descendant before ancestor.</p>
+ *
+ * <p>In case a descendant is created before a particular ancestor,
+ * then it creates a provision node for the ancestor and adds itself
+ * to the provision node. Other descendants of the same ancestor add
+ * themselves to the previously created provision node.</p>
+ */
+class Hierarchy {
+
+	/** Array holding all Logger instances. */
+	protected $loggers = array();
+
+	/**
+	 * The root logger.
+	 * @var RootLogger
+	 */
+	protected $root;
+
+	/**
+	 * The logger renderer map.
+	 * @var RendererMap
+	 */
+	protected $rendererMap;
+
+	/**
+	 * Main level threshold. Events with lower level will not be logged by any
+	 * logger, regardless of it's configuration.
+	 * @var Level
+	 */
+	protected $threshold;
+
+	/**
+	 * Creates a new logger hierarchy.
+	 * @param RootLogger $root The root logger.
+	 */
+	public function __construct(RootLogger $root) {
+		$this->root = $root;
+		$this->setThreshold(Level::getLevelAll());
+		$this->rendererMap = new RendererMap();
+	}
+
+	/**
+	 * Clears all loggers.
+	 */
+	public function clear() {
+		$this->loggers = array();
+	}
+
+	/**
+	 * Check if the named logger exists in the hierarchy.
+	 * @param string $name
+	 * @return boolean
+	 */
+	public function exists($name) {
+		return isset($this->loggers[$name]);
+	}
+
+	/**
+	 * Returns all the currently defined loggers in this hierarchy as an array.
+	 * @return array
+	 */
+	public function getCurrentLoggers() {
+		return array_values($this->loggers);
+	}
+
+	/**
+	 * Returns a named logger instance logger. If it doesn't exist, one is created.
+	 *
+	 * @param string $name Logger name
+	 * @return Logger Logger instance.
+	 */
+	public function getLogger($name) {
+		if(!isset($this->loggers[$name])) {
+			$logger = new Logger($name);
+
+			$nodes = explode('.', $name);
+			$firstNode = array_shift($nodes);
+
+			// if name is not a first node but another first node is their
+			if($firstNode != $name and isset($this->loggers[$firstNode])) {
+				$logger->setParent($this->loggers[$firstNode]);
+			} else {
+				// if there is no father, set root logger as father
+				$logger->setParent($this->root);
+			}
+
+			// if there are more nodes than one
+			if(count($nodes) > 0) {
+				// find parent node
+				foreach($nodes as $node) {
+					$parentNode = "$firstNode.$node";
+					if(isset($this->loggers[$parentNode]) and $parentNode != $name) {
+						$logger->setParent($this->loggers[$parentNode]);
+					}
+					$firstNode .= ".$node";
+				}
+			}
+
+			$this->loggers[$name] = $logger;
+		}
+
+		return $this->loggers[$name];
+	}
+
+	/**
+	 * Returns the logger renderer map.
+	 * @return RendererMap
+	 */
+	public function getRendererMap() {
+		return $this->rendererMap;
+	}
+
+	/**
+	 * Returns the root logger.
+	 * @return RootLogger
+	 */
+	public function getRootLogger() {
+		return $this->root;
+	}
+
+	/**
+	 * Returns the main threshold level.
+	 * @return Level
+	 */
+	public function getThreshold() {
+		return $this->threshold;
+	}
+
+	/**
+	 * Returns true if the hierarchy is disabled for given log level and false
+	 * otherwise.
+	 * @return boolean
+	 */
+	public function isDisabled(Level $level) {
+		return ($this->threshold->toInt() > $level->toInt());
+	}
+
+	/**
+	 * Reset all values contained in this hierarchy instance to their
+	 * default.
+	 *
+	 * This removes all appenders from all loggers, sets
+	 * the level of all non-root loggers to <i>null</i>,
+	 * sets their additivity flag to <i>true</i> and sets the level
+	 * of the root logger to {@link LOGGER_LEVEL_DEBUG}.
+	 *
+	 * <p>Existing loggers are not removed. They are just reset.
+	 *
+	 * <p>This method should be used sparingly and with care as it will
+	 * block all logging until it is completed.</p>
+	 */
+	public function resetConfiguration() {
+		$root = $this->getRootLogger();
+
+		$root->setLevel(Level::getLevelDebug());
+		$this->setThreshold(Level::getLevelAll());
+		$this->shutDown();
+
+		foreach($this->loggers as $logger) {
+			$logger->setLevel(null);
+			$logger->setAdditivity(true);
+			$logger->removeAllAppenders();
+		}
+
+		$this->rendererMap->reset();
+		AppenderPool::clear();
+	}
+
+	/**
+	 * Sets the main threshold level.
+	 * @param Level $l
+	 */
+	public function setThreshold(Level $threshold) {
+		$this->threshold = $threshold;
+	}
+
+	/**
+	 * Shutting down a hierarchy will <i>safely</i> close and remove
+	 * all appenders in all loggers including the root logger.
+	 *
+	 * The shutdown method is careful to close nested
+	 * appenders before closing regular appenders. This is allows
+	 * configurations where a regular appender is attached to a logger
+	 * and again to a nested appender.
+	 *
+	 * @todo Check if the last paragraph is correct.
+	 */
+	public function shutdown() {
+		$this->root->removeAllAppenders();
+
+		foreach($this->loggers as $logger) {
+			$logger->removeAllAppenders();
+		}
+	}
+
+	/**
+	 * Prints the current Logger hierarchy tree. Useful for debugging.
+	 */
+	public function printHierarchy() {
+		$this->printHierarchyInner($this->getRootLogger(), 0);
+	}
+
+	private function printHierarchyInner(Logger $current, $level) {
+		for ($i = 0; $i < $level; $i++) {
+			echo ($i == $level - 1) ? "|--" : "|  ";
+		}
+		echo $current->getName() . "\n";
+
+		foreach($this->loggers as $logger) {
+			if ($logger->getParent() == $current) {
+				$this->printHierarchyInner($logger, $level + 1);
+			}
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/logging-log4php/blob/79ed2d0d/src/Layouts/AbstractLayout.php
----------------------------------------------------------------------
diff --git a/src/Layouts/AbstractLayout.php b/src/Layouts/AbstractLayout.php
new file mode 100644
index 0000000..b6a2d14
--- /dev/null
+++ b/src/Layouts/AbstractLayout.php
@@ -0,0 +1,74 @@
+<?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\Layouts;
+
+use Apache\Log4php\Configurable;
+use Apache\Log4php\LoggingEvent;
+
+/**
+ * Extend this abstract class to create your own log layout format.
+ */
+abstract class AbstractLayout extends Configurable {
+	/**
+	 * Activates options for this layout.
+	 * Override this method if you have options to be activated.
+	 */
+	public function activateOptions() {
+		return true;
+	}
+
+	/**
+	 * Override this method to create your own layout format.
+	 *
+	 * @param LoggingEvent
+	 * @return string
+	 */
+	public function format(LoggingEvent $event) {
+		return $event->getRenderedMessage();
+	}
+
+	/**
+	 * Returns the content type output by this layout.
+	 * @return string
+	 */
+	public function getContentType() {
+		return "text/plain";
+	}
+
+	/**
+	 * Returns the footer for the layout format.
+	 * @return string
+	 */
+	public function getFooter() {
+		return null;
+	}
+
+	/**
+	 * Returns the header for the layout format.
+	 * @return string
+	 */
+	public function getHeader() {
+		return null;
+	}
+
+	/** Triggers a warning for this layout with the given message. */
+	protected function warn($message) {
+		trigger_error("log4php: [" . get_class($this) . "]: $message", E_USER_WARNING);
+	}
+}

http://git-wip-us.apache.org/repos/asf/logging-log4php/blob/79ed2d0d/src/Layouts/HtmlLayout.php
----------------------------------------------------------------------
diff --git a/src/Layouts/HtmlLayout.php b/src/Layouts/HtmlLayout.php
new file mode 100644
index 0000000..af74e61
--- /dev/null
+++ b/src/Layouts/HtmlLayout.php
@@ -0,0 +1,197 @@
+<?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\Layouts;
+
+use Apache\Log4php\LoggingEvent;
+use Apache\Log4php\Level;
+
+/**
+ * This layout outputs events in a HTML table.
+ *
+ * Configurable parameters for this layout are:
+ *
+ * - title
+ * - locationInfo
+ */
+class HtmlLayout extends AbstractLayout {
+	/**
+	 * The <b>LocationInfo</b> option takes a boolean value. By
+	 * default, it is set to false which means there will be no location
+	 * information output by this layout. If the the option is set to
+	 * true, then the file name and line number of the statement
+	 * at the origin of the log statement will be output.
+	 *
+	 * <p>If you are embedding this layout within a {@link MailAppender}
+	 * or a {@link MailEventAppender} then make sure to set the
+	 * <b>LocationInfo</b> option of that appender as well.
+	 * @var boolean
+	 */
+	protected $locationInfo = false;
+
+	/**
+	 * The <b>Title</b> option takes a String value. This option sets the
+	 * document title of the generated HTML document.
+	 * Defaults to 'Log4php Log Messages'.
+	 * @var string
+	 */
+	protected $title = "Log4php Log Messages";
+
+	/**
+	 * The <b>LocationInfo</b> option takes a boolean value. By
+	 * default, it is set to false which means there will be no location
+	 * information output by this layout. If the the option is set to
+	 * true, then the file name and line number of the statement
+	 * at the origin of the log statement will be output.
+	 *
+	 * <p>If you are embedding this layout within a {@link MailAppender}
+	 * or a {@link MailEventAppender} then make sure to set the
+	 * <b>LocationInfo</b> option of that appender as well.
+	 */
+	public function setLocationInfo($flag) {
+		$this->setBoolean('locationInfo', $flag);
+	}
+
+	/**
+	 * Returns the current value of the <b>LocationInfo</b> option.
+	 */
+	public function getLocationInfo() {
+		return $this->locationInfo;
+	}
+
+	/**
+	 * The <b>Title</b> option takes a String value. This option sets the
+	 * document title of the generated HTML document.
+	 * Defaults to 'Log4php Log Messages'.
+	 */
+	public function setTitle($title) {
+		$this->setString('title', $title);
+	}
+
+	/**
+	 * @return string Returns the current value of the <b>Title</b> option.
+	 */
+	public function getTitle() {
+		return $this->title;
+	}
+
+	/**
+	 * @return string Returns the content type output by this layout, i.e "text/html".
+	 */
+	public function getContentType() {
+		return "text/html";
+	}
+
+	/**
+	 * @param LoggingEvent $event
+	 * @return string
+	 */
+	public function format(LoggingEvent $event) {
+		$sbuf = PHP_EOL . "<tr>" . PHP_EOL;
+
+		$sbuf .= "<td>";
+		$sbuf .= round(1000 * $event->getRelativeTime());
+		$sbuf .= "</td>" . PHP_EOL;
+
+		$sbuf .= "<td title=\"" . $event->getThreadName() . " thread\">";
+		$sbuf .= $event->getThreadName();
+		$sbuf .= "</td>" . PHP_EOL;
+
+		$sbuf .= "<td title=\"Level\">";
+
+		$level = $event->getLevel();
+
+		if ($level->equals(Level::getLevelDebug())) {
+			$sbuf .= "<font color=\"#339933\">$level</font>";
+		} else if ($level->equals(Level::getLevelWarn())) {
+			$sbuf .= "<font color=\"#993300\"><strong>$level</strong></font>";
+		} else {
+			$sbuf .= $level;
+		}
+		$sbuf .= "</td>" . PHP_EOL;
+
+		$sbuf .= "<td title=\"" . htmlentities($event->getLoggerName(), ENT_QUOTES) . " category\">";
+		$sbuf .= htmlentities($event->getLoggerName(), ENT_QUOTES);
+		$sbuf .= "</td>" . PHP_EOL;
+
+		if ($this->locationInfo) {
+			$locInfo = $event->getLocationInformation();
+			$sbuf .= "<td>";
+			$sbuf .= htmlentities($locInfo->getFileName(), ENT_QUOTES). ':' . $locInfo->getLineNumber();
+			$sbuf .= "</td>" . PHP_EOL;
+		}
+
+		$sbuf .= "<td title=\"Message\">";
+		$sbuf .= htmlentities($event->getRenderedMessage(), ENT_QUOTES);
+		$sbuf .= "</td>" . PHP_EOL;
+
+		$sbuf .= "</tr>" . PHP_EOL;
+
+		if ($event->getNDC() != null) {
+			$sbuf .= "<tr><td bgcolor=\"#EEEEEE\" style=\"font-size : xx-small;\" colspan=\"6\" title=\"Nested Diagnostic Context\">";
+			$sbuf .= "NDC: " . htmlentities($event->getNDC(), ENT_QUOTES);
+			$sbuf .= "</td></tr>" . PHP_EOL;
+		}
+		return $sbuf;
+	}
+
+	/**
+	 * @return string Returns appropriate HTML headers.
+	 */
+	public function getHeader() {
+		$sbuf = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">" . PHP_EOL;
+		$sbuf .= "<html>" . PHP_EOL;
+		$sbuf .= "<head>" . PHP_EOL;
+		$sbuf .= "<title>" . $this->title . "</title>" . PHP_EOL;
+		$sbuf .= "<style type=\"text/css\">" . PHP_EOL;
+		$sbuf .= "<!--" . PHP_EOL;
+		$sbuf .= "body, table {font-family: arial,sans-serif; font-size: x-small;}" . PHP_EOL;
+		$sbuf .= "th {background: #336699; color: #FFFFFF; text-align: left;}" . PHP_EOL;
+		$sbuf .= "-->" . PHP_EOL;
+		$sbuf .= "</style>" . PHP_EOL;
+		$sbuf .= "</head>" . PHP_EOL;
+		$sbuf .= "<body bgcolor=\"#FFFFFF\" topmargin=\"6\" leftmargin=\"6\">" . PHP_EOL;
+		$sbuf .= "<hr size=\"1\" noshade>" . PHP_EOL;
+		$sbuf .= "Log session start time " . strftime('%c', time()) . "<br>" . PHP_EOL;
+		$sbuf .= "<br>" . PHP_EOL;
+		$sbuf .= "<table cellspacing=\"0\" cellpadding=\"4\" border=\"1\" bordercolor=\"#224466\" width=\"100%\">" . PHP_EOL;
+		$sbuf .= "<tr>" . PHP_EOL;
+		$sbuf .= "<th>Time</th>" . PHP_EOL;
+		$sbuf .= "<th>Thread</th>" . PHP_EOL;
+		$sbuf .= "<th>Level</th>" . PHP_EOL;
+		$sbuf .= "<th>Category</th>" . PHP_EOL;
+		if ($this->locationInfo) {
+			$sbuf .= "<th>File:Line</th>" . PHP_EOL;
+		}
+		$sbuf .= "<th>Message</th>" . PHP_EOL;
+		$sbuf .= "</tr>" . PHP_EOL;
+
+		return $sbuf;
+	}
+
+	/**
+	 * @return string Returns the appropriate HTML footers.
+	 */
+	public function getFooter() {
+		$sbuf = "</table>" . PHP_EOL;
+		$sbuf .= "<br>" . PHP_EOL;
+		$sbuf .= "</body></html>";
+
+		return $sbuf;
+	}
+}

http://git-wip-us.apache.org/repos/asf/logging-log4php/blob/79ed2d0d/src/Layouts/PatternLayout.php
----------------------------------------------------------------------
diff --git a/src/Layouts/PatternLayout.php b/src/Layouts/PatternLayout.php
new file mode 100644
index 0000000..c34cbe2
--- /dev/null
+++ b/src/Layouts/PatternLayout.php
@@ -0,0 +1,171 @@
+<?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\Layouts;
+
+use Apache\Log4php\Helpers\PatternParser;
+use Apache\Log4php\LoggerException;
+use Apache\Log4php\LoggingEvent;
+
+/**
+ * A flexible layout configurable with a pattern string.
+ *
+ * Configurable parameters:
+ *
+ * * converionPattern - A string which controls the formatting of logging
+ *   events. See docs for full specification.
+ */
+class PatternLayout extends AbstractLayout {
+
+	/** Default conversion pattern */
+	const DEFAULT_CONVERSION_PATTERN = '%date %-5level %logger %message%newline';
+
+	/** Default conversion TTCC Pattern */
+	const TTCC_CONVERSION_PATTERN = '%d [%t] %p %c %x - %m%n';
+
+	/** The conversion pattern. */
+	protected $pattern = self::DEFAULT_CONVERSION_PATTERN;
+
+	/** Maps conversion keywords to the relevant converter (default implementation). */
+	protected static $defaultConverterMap = array(
+		'c' => 'LoggerConverter',
+		'lo' => 'LoggerConverter',
+		'logger' => 'LoggerConverter',
+
+		'C' => 'ClassConverter',
+		'class' => 'ClassConverter',
+
+		'cookie' => 'CookieConverter',
+
+		'd' => 'DateConverter',
+		'date' => 'DateConverter',
+
+		'e' => 'EnvironmentConverter',
+		'env' => 'EnvironmentConverter',
+
+		'ex' => 'ThrowableConverter',
+		'exception' => 'ThrowableConverter',
+		'throwable' => 'ThrowableConverter',
+
+		'F' => 'FileConverter',
+		'file' => 'FileConverter',
+
+		'l' => 'LocationConverter',
+		'location' => 'LocationConverter',
+
+		'L' => 'LineConverter',
+		'line' => 'LineConverter',
+
+		'm' => 'MessageConverter',
+		'msg' => 'MessageConverter',
+		'message' => 'MessageConverter',
+
+		'M' => 'MethodConverter',
+		'method' => 'MethodConverter',
+
+		'n' => 'NewLineConverter',
+		'newline' => 'NewLineConverter',
+
+		'p' => 'LevelConverter',
+		'le' => 'LevelConverter',
+		'level' => 'LevelConverter',
+
+		'r' => 'RelativeConverter',
+		'relative' => 'RelativeConverter',
+
+		'req' => 'RequestConverter',
+		'request' => 'RequestConverter',
+
+		's' => 'ServerConverter',
+		'server' => 'ServerConverter',
+
+		'ses' => 'SessionConverter',
+		'session' => 'SessionConverter',
+
+		'sid' => 'SessionIdConverter',
+		'sessionid' => 'SessionIdConverter',
+
+		't' => 'ProcessConverter',
+		'pid' => 'ProcessConverter',
+		'process' => 'ProcessConverter',
+
+		'x' => 'NdcConverter',
+		'ndc' => 'NdcConverter',
+
+		'X' => 'MdcConverter',
+		'mdc' => 'MdcConverter',
+	);
+
+	/** Maps conversion keywords to the relevant converter. */
+	protected $converterMap = array();
+
+	/**
+	 * Head of a chain of Converters.
+	 * @var AbstractConverter
+	 */
+	private $head;
+
+	/** Returns the default converter map. */
+	public static function getDefaultConverterMap() {
+		return self::$defaultConverterMap;
+	}
+
+	/** Constructor. Initializes the converter map. */
+	public function __construct() {
+		$this->converterMap = self::$defaultConverterMap;
+	}
+
+	/**
+	 * Sets the conversionPattern option. This is the string which
+	 * controls formatting and consists of a mix of literal content and
+	 * conversion specifiers.
+	 * @param array $conversionPattern
+	 */
+	public function setConversionPattern($conversionPattern) {
+		$this->pattern = $conversionPattern;
+	}
+
+	/**
+	 * Processes the conversion pattern and creates a corresponding chain of
+	 * pattern converters which will be used to format logging events.
+	 */
+	public function activateOptions() {
+		if (!isset($this->pattern)) {
+			throw new LoggerException("Mandatory parameter 'conversionPattern' is not set.");
+		}
+
+		$parser = new PatternParser($this->pattern, $this->converterMap);
+		$this->head = $parser->parse();
+	}
+
+	/**
+	 * Produces a formatted string as specified by the conversion pattern.
+	 *
+	 * @param LoggingEvent $event
+	 * @return string
+	 */
+	public function format(LoggingEvent $event) {
+		$sbuf = '';
+		$converter = $this->head;
+		while ($converter !== null) {
+			$converter->format($sbuf, $event);
+			$converter = $converter->next;
+		}
+		return $sbuf;
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/logging-log4php/blob/79ed2d0d/src/Layouts/SerializedLayout.php
----------------------------------------------------------------------
diff --git a/src/Layouts/SerializedLayout.php b/src/Layouts/SerializedLayout.php
new file mode 100644
index 0000000..5273c3b
--- /dev/null
+++ b/src/Layouts/SerializedLayout.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\Layouts;
+
+use Apache\Log4php\LoggingEvent;
+
+/**
+ * Layout which formats the events using PHP's serialize() function.
+ *
+ * Available options:
+ * - locationInfo - If set to true, the event's location information will also
+ *                  be serialized (slow, defaults to false).
+ * @since 2.2
+ */
+class SerializedLayout extends AbstractLayout {
+
+	/** Whether to include the event's location information (slow). */
+	protected $locationInfo = false;
+
+	/** Sets the location information flag. */
+	public function setLocationInfo($value) {
+		$this->setBoolean('locationInfo', $value);
+	}
+
+	/** Returns the location information flag. */
+	public function getLocationInfo() {
+		return $this->locationInfo;
+	}
+
+	public function format(LoggingEvent $event) {
+		// If required, initialize the location data
+		if($this->locationInfo) {
+			$event->getLocationInformation();
+		}
+		return serialize($event) . PHP_EOL;
+	}
+}

http://git-wip-us.apache.org/repos/asf/logging-log4php/blob/79ed2d0d/src/Layouts/SimpleLayout.php
----------------------------------------------------------------------
diff --git a/src/Layouts/SimpleLayout.php b/src/Layouts/SimpleLayout.php
new file mode 100644
index 0000000..16645f3
--- /dev/null
+++ b/src/Layouts/SimpleLayout.php
@@ -0,0 +1,44 @@
+<?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\Layouts;
+
+use Apache\Log4php\LoggingEvent;
+
+/**
+ * A simple layout.
+ *
+ * Returns the log statement in a format consisting of the
+ * <b>level</b>, followed by " - " and then the <b>message</b>.
+ */
+class SimpleLayout extends AbstractLayout {
+	/**
+	 * Returns the log statement in a format consisting of the
+	 * <b>level</b>, followed by " - " and then the
+	 * <b>message</b>. For example,
+	 * <samp> INFO - "A message" </samp>
+	 *
+	 * @param LoggingEvent $event
+	 * @return string
+	 */
+	public function format(LoggingEvent $event) {
+		$level = $event->getLevel();
+		$message = $event->getRenderedMessage();
+		return "$level - $message" . PHP_EOL;
+	}
+}

http://git-wip-us.apache.org/repos/asf/logging-log4php/blob/79ed2d0d/src/Layouts/XmlLayout.php
----------------------------------------------------------------------
diff --git a/src/Layouts/XmlLayout.php b/src/Layouts/XmlLayout.php
new file mode 100644
index 0000000..0ada386
--- /dev/null
+++ b/src/Layouts/XmlLayout.php
@@ -0,0 +1,192 @@
+<?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\Layouts;
+
+use Apache\Log4php\LoggingEvent;
+
+/**
+ * The output of the LoggerXmlLayout consists of a series of log4php:event elements.
+ *
+ * Configurable parameters:
+ * - {@link $locationInfo} - If set to true then the file name and line number
+ *   of the origin of the log statement will be included in output.
+ * - {@link $log4jNamespace} - If set to true then log4j namespace will be used
+ *   instead of log4php namespace. This can be usefull when using log viewers
+ *   which can only parse the log4j namespace such as Apache Chainsaw.
+ *
+ * <p>It does not output a complete well-formed XML file.
+ * The output is designed to be included as an external entity in a separate file to form
+ * a correct XML file.</p>
+ *
+ */
+class XmlLayout extends AbstractLayout {
+	const LOG4J_NS_PREFIX ='log4j';
+	const LOG4J_NS = 'http://jakarta.apache.org/log4j/';
+
+	const LOG4PHP_NS_PREFIX = 'log4php';
+	const LOG4PHP_NS = 'http://logging.apache.org/log4php/';
+
+	const CDATA_START = '<![CDATA[';
+	const CDATA_END = ']]>';
+	const CDATA_PSEUDO_END = ']]&gt;';
+	const CDATA_EMBEDDED_END = ']]>]]&gt;<![CDATA[';
+
+	/**
+	 * If set to true then the file name and line number of the origin of the
+	 * log statement will be output.
+	 * @var boolean
+	 */
+	protected $locationInfo = true;
+
+	/**
+	 * If set to true, log4j namespace will be used instead of the log4php
+	 * namespace.
+	 * @var boolean
+	 */
+	protected $log4jNamespace = false;
+
+	/** The namespace in use. */
+	protected $namespace = self::LOG4PHP_NS;
+
+	/** The namespace prefix in use */
+	protected $namespacePrefix = self::LOG4PHP_NS_PREFIX;
+
+	public function activateOptions() {
+		if ($this->getLog4jNamespace()) {
+			$this->namespace        = self::LOG4J_NS;
+			$this->namespacePrefix  = self::LOG4J_NS_PREFIX;
+		} else {
+			$this->namespace        = self::LOG4PHP_NS;
+			$this->namespacePrefix  = self::LOG4PHP_NS_PREFIX;
+		}
+	}
+
+	/**
+	 * @return string
+	 */
+	public function getHeader() {
+		return "<{$this->namespacePrefix}:eventSet ".
+			"xmlns:{$this->namespacePrefix}=\"{$this->namespace}\" ".
+			"version=\"0.3\" ".
+			"includesLocationInfo=\"".($this->getLocationInfo() ? "true" : "false")."\"".
+			">" . PHP_EOL;
+	}
+
+	/**
+	 * Formats a {@link LoggingEvent} in conformance with the log4php.dtd.
+	 *
+	 * @param LoggingEvent $event
+	 * @return string
+	 */
+	public function format(LoggingEvent $event) {
+		$ns = $this->namespacePrefix;
+
+		$loggerName = $event->getLoggerName();
+		$timeStamp = number_format((float)($event->getTimeStamp() * 1000), 0, '', '');
+		$thread = $event->getThreadName();
+		$level = $event->getLevel()->toString();
+
+		$buf  = "<$ns:event logger=\"{$loggerName}\" level=\"{$level}\" thread=\"{$thread}\" timestamp=\"{$timeStamp}\">".PHP_EOL;
+		$buf .= "<$ns:message>";
+		$buf .= $this->encodeCDATA($event->getRenderedMessage());
+		$buf .= "</$ns:message>".PHP_EOL;
+
+		$ndc = $event->getNDC();
+		if(!empty($ndc)) {
+			$buf .= "<$ns:NDC><![CDATA[";
+			$buf .= $this->encodeCDATA($ndc);
+			$buf .= "]]></$ns:NDC>".PHP_EOL;
+		}
+
+		$mdcMap = $event->getMDCMap();
+		if (!empty($mdcMap)) {
+			$buf .= "<$ns:properties>".PHP_EOL;
+			foreach ($mdcMap as $name=>$value) {
+				$buf .= "<$ns:data name=\"$name\" value=\"$value\" />".PHP_EOL;
+			}
+			$buf .= "</$ns:properties>".PHP_EOL;
+		}
+
+		if ($this->getLocationInfo()) {
+			$locationInfo = $event->getLocationInformation();
+			$buf .= "<$ns:locationInfo ".
+					"class=\"" . $locationInfo->getClassName() . "\" ".
+					"file=\"" .  htmlentities($locationInfo->getFileName(), ENT_QUOTES) . "\" ".
+					"line=\"" .  $locationInfo->getLineNumber() . "\" ".
+					"method=\"" . $locationInfo->getMethodName() . "\" ";
+			$buf .= "/>".PHP_EOL;
+		}
+
+		$buf .= "</$ns:event>".PHP_EOL;
+
+		return $buf;
+	}
+
+	/**
+	 * @return string
+	 */
+	public function getFooter() {
+		return "</{$this->namespacePrefix}:eventSet>" . PHP_EOL;
+	}
+
+
+	/**
+	 * Whether or not file name and line number will be included in the output.
+	 * @return boolean
+	 */
+	public function getLocationInfo() {
+		return $this->locationInfo;
+	}
+
+	/**
+	 * The {@link $locationInfo} option takes a boolean value. By default,
+	 * it is set to false which means there will be no location
+	 * information output by this layout. If the the option is set to
+	 * true, then the file name and line number of the statement at the
+	 * origin of the log statement will be output.
+	 */
+	public function setLocationInfo($flag) {
+		$this->setBoolean('locationInfo', $flag);
+	}
+
+	/**
+	 * @return boolean
+	 */
+	 public function getLog4jNamespace() {
+	 	return $this->log4jNamespace;
+	 }
+
+	/**
+	 * @param boolean
+	 */
+	public function setLog4jNamespace($flag) {
+		$this->setBoolean('log4jNamespace', $flag);
+	}
+
+	/**
+	 * Encases a string in CDATA tags, and escapes any existing CDATA end
+	 * tags already present in the string.
+	 * @param string $string
+	 */
+	private function encodeCDATA($string) {
+		$string = str_replace(self::CDATA_END, self::CDATA_EMBEDDED_END, $string);
+		return self::CDATA_START . $string . self::CDATA_END;
+	}
+}
+

http://git-wip-us.apache.org/repos/asf/logging-log4php/blob/79ed2d0d/src/Level.php
----------------------------------------------------------------------
diff --git a/src/Level.php b/src/Level.php
new file mode 100644
index 0000000..f17f0fc
--- /dev/null
+++ b/src/Level.php
@@ -0,0 +1,253 @@
+<?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;
+
+/**
+ * Defines the minimum set of levels recognized by the system, that is
+ * <i>OFF</i>, <i>FATAL</i>, <i>ERROR</i>,
+ * <i>WARN</i>, <i>INFO</i>, <i>DEBUG</i> and
+ * <i>ALL</i>.
+ *
+ * <p>The <i>Level</i> class may be subclassed to define a larger
+ * level set.</p>
+ * @since 0.5
+ */
+class Level {
+
+	const OFF = 2147483647;
+	const FATAL = 50000;
+	const ERROR = 40000;
+	const WARN = 30000;
+	const INFO = 20000;
+	const DEBUG = 10000;
+	const TRACE = 5000;
+	const ALL = -2147483647;
+
+	/** Integer level value. */
+	private $level;
+
+	/** Contains a list of instantiated levels. */
+	private static $levelMap;
+
+	/** String representation of the level. */
+	private $levelStr;
+
+	/**
+	 * Equivalent syslog level.
+	 * @var integer
+	 */
+	private $syslogEquivalent;
+
+	/**
+	 * Constructor
+	 *
+	 * @param integer $level
+	 * @param string $levelStr
+	 * @param integer $syslogEquivalent
+	 */
+	private function __construct($level, $levelStr, $syslogEquivalent) {
+		$this->level = $level;
+		$this->levelStr = $levelStr;
+		$this->syslogEquivalent = $syslogEquivalent;
+	}
+
+	/**
+	 * Compares two logger levels.
+	 *
+	 * @param Level $other
+	 * @return boolean
+	 */
+	public function equals($other) {
+		if($other instanceof Level) {
+			if($this->level == $other->level) {
+				return true;
+			}
+		} else {
+			return false;
+		}
+	}
+
+	/**
+	 * Returns an Off Level
+	 * @return Level
+	 */
+	public static function getLevelOff() {
+		if(!isset(self::$levelMap[Level::OFF])) {
+			self::$levelMap[Level::OFF] = new Level(Level::OFF, 'OFF', LOG_ALERT);
+		}
+		return self::$levelMap[Level::OFF];
+	}
+
+	/**
+	 * Returns a Fatal Level
+	 * @return Level
+	 */
+	public static function getLevelFatal() {
+		if(!isset(self::$levelMap[Level::FATAL])) {
+			self::$levelMap[Level::FATAL] = new Level(Level::FATAL, 'FATAL', LOG_ALERT);
+		}
+		return self::$levelMap[Level::FATAL];
+	}
+
+	/**
+	 * Returns an Error Level
+	 * @return Level
+	 */
+	public static function getLevelError() {
+		if(!isset(self::$levelMap[Level::ERROR])) {
+			self::$levelMap[Level::ERROR] = new Level(Level::ERROR, 'ERROR', LOG_ERR);
+		}
+		return self::$levelMap[Level::ERROR];
+	}
+
+	/**
+	 * Returns a Warn Level
+	 * @return Level
+	 */
+	public static function getLevelWarn() {
+		if(!isset(self::$levelMap[Level::WARN])) {
+			self::$levelMap[Level::WARN] = new Level(Level::WARN, 'WARN', LOG_WARNING);
+		}
+		return self::$levelMap[Level::WARN];
+	}
+
+	/**
+	 * Returns an Info Level
+	 * @return Level
+	 */
+	public static function getLevelInfo() {
+		if(!isset(self::$levelMap[Level::INFO])) {
+			self::$levelMap[Level::INFO] = new Level(Level::INFO, 'INFO', LOG_INFO);
+		}
+		return self::$levelMap[Level::INFO];
+	}
+
+	/**
+	 * Returns a Debug Level
+	 * @return Level
+	 */
+	public static function getLevelDebug() {
+		if(!isset(self::$levelMap[Level::DEBUG])) {
+			self::$levelMap[Level::DEBUG] = new Level(Level::DEBUG, 'DEBUG', LOG_DEBUG);
+		}
+		return self::$levelMap[Level::DEBUG];
+	}
+
+	/**
+	 * Returns a Trace Level
+	 * @return Level
+	 */
+	public static function getLevelTrace() {
+		if(!isset(self::$levelMap[Level::TRACE])) {
+			self::$levelMap[Level::TRACE] = new Level(Level::TRACE, 'TRACE', LOG_DEBUG);
+		}
+		return self::$levelMap[Level::TRACE];
+	}
+
+	/**
+	 * Returns an All Level
+	 * @return Level
+	 */
+	public static function getLevelAll() {
+		if(!isset(self::$levelMap[Level::ALL])) {
+			self::$levelMap[Level::ALL] = new Level(Level::ALL, 'ALL', LOG_DEBUG);
+		}
+		return self::$levelMap[Level::ALL];
+	}
+
+	/**
+	 * Return the syslog equivalent of this level as an integer.
+	 * @return integer
+	 */
+	public function getSyslogEquivalent() {
+		return $this->syslogEquivalent;
+	}
+
+	/**
+	 * Returns <i>true</i> if this level has a higher or equal
+	 * level than the level passed as argument, <i>false</i>
+	 * otherwise.
+	 *
+	 * @param Level $other
+	 * @return boolean
+	 */
+	public function isGreaterOrEqual($other) {
+		return $this->level >= $other->level;
+	}
+
+	/**
+	 * Returns the string representation of this level.
+	 * @return string
+	 */
+	public function toString() {
+		return $this->levelStr;
+	}
+
+	/**
+	 * Returns the string representation of this level.
+	 * @return string
+	 */
+	public function __toString() {
+		return $this->toString();
+	}
+
+	/**
+	 * Returns the integer representation of this level.
+	 * @return integer
+	 */
+	public function toInt() {
+		return $this->level;
+	}
+
+	/**
+	 * Convert the input argument to a level. If the conversion fails, then
+	 * this method returns the provided default level.
+	 *
+	 * @param mixed $arg The value to convert to level.
+	 * @param Level $default Value to return if conversion is not possible.
+	 * @return Level
+	 */
+	public static function toLevel($arg, $defaultLevel = null) {
+		if(is_int($arg)) {
+			switch($arg) {
+				case self::ALL:	return self::getLevelAll();
+				case self::TRACE: return self::getLevelTrace();
+				case self::DEBUG: return self::getLevelDebug();
+				case self::INFO: return self::getLevelInfo();
+				case self::WARN: return self::getLevelWarn();
+				case self::ERROR: return self::getLevelError();
+				case self::FATAL: return self::getLevelFatal();
+				case self::OFF:	return self::getLevelOff();
+				default: return $defaultLevel;
+			}
+		} else {
+			switch(strtoupper($arg)) {
+				case 'ALL':	return self::getLevelAll();
+				case 'TRACE': return self::getLevelTrace();
+				case 'DEBUG': return self::getLevelDebug();
+				case 'INFO': return self::getLevelInfo();
+				case 'WARN': return self::getLevelWarn();
+				case 'ERROR': return self::getLevelError();
+				case 'FATAL': return self::getLevelFatal();
+				case 'OFF':	return self::getLevelOff();
+				default: return $defaultLevel;
+			}
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/logging-log4php/blob/79ed2d0d/src/LocationInfo.php
----------------------------------------------------------------------
diff --git a/src/LocationInfo.php b/src/LocationInfo.php
new file mode 100644
index 0000000..4615962
--- /dev/null
+++ b/src/LocationInfo.php
@@ -0,0 +1,100 @@
+<?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;
+
+/**
+ * The internal representation of caller location information.
+ * @since 0.3
+ */
+class LocationInfo {
+
+	/** The value to return when the location information is not available. */
+	const LOCATION_INFO_NA = 'NA';
+
+	/**
+	 * Caller line number.
+	 * @var integer
+	 */
+	protected $lineNumber;
+
+	/**
+	 * Caller file name.
+	 * @var string
+	 */
+	protected $fileName;
+
+	/**
+	 * Caller class name.
+	 * @var string
+	 */
+	protected $className;
+
+	/**
+	 * Caller method name.
+	 * @var string
+	 */
+	protected $methodName;
+
+	/**
+	 * All the information combined.
+	 * @var string
+	 */
+	protected $fullInfo;
+
+	/**
+	 * Instantiate location information based on a {@link PHP_MANUAL#debug_backtrace}.
+	 *
+	 * @param array $trace
+	 * @param mixed $caller
+	 */
+	public function __construct($trace, $fqcn = null) {
+		$this->lineNumber = isset($trace['line']) ? $trace['line'] : null;
+		$this->fileName = isset($trace['file']) ? $trace['file'] : null;
+		$this->className = isset($trace['class']) ? $trace['class'] : null;
+		$this->methodName = isset($trace['function']) ? $trace['function'] : null;
+		$this->fullInfo = $this->getClassName() . '.' . $this->getMethodName() .
+			'(' . $this->getFileName() . ':' . $this->getLineNumber() . ')';
+	}
+
+	/** Returns the caller class name. */
+	public function getClassName() {
+		return ($this->className === null) ? self::LOCATION_INFO_NA : $this->className;
+	}
+
+	/** Returns the caller file name. */
+	public function getFileName() {
+		return ($this->fileName === null) ? self::LOCATION_INFO_NA : $this->fileName;
+	}
+
+	/** Returns the caller line number. */
+	public function getLineNumber() {
+		return ($this->lineNumber === null) ? self::LOCATION_INFO_NA : $this->lineNumber;
+	}
+
+	/** Returns the caller method name. */
+	public function getMethodName() {
+		return ($this->methodName === null) ? self::LOCATION_INFO_NA : $this->methodName;
+	}
+
+	/** Returns the full information of the caller. */
+	public function getFullInfo() {
+		return ($this->fullInfo === null) ? self::LOCATION_INFO_NA : $this->fullInfo;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/logging-log4php/blob/79ed2d0d/src/Logger.php
----------------------------------------------------------------------
diff --git a/src/Logger.php b/src/Logger.php
new file mode 100644
index 0000000..27eca1c
--- /dev/null
+++ b/src/Logger.php
@@ -0,0 +1,592 @@
+<?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;
+
+/**
+ * This is the central class in the log4php package. All logging operations
+ * are done through this class.
+ *
+ * The main logging methods are:
+ * 	<ul>
+ * 		<li>{@link trace()}</li>
+ * 		<li>{@link debug()}</li>
+ * 		<li>{@link info()}</li>
+ * 		<li>{@link warn()}</li>
+ * 		<li>{@link error()}</li>
+ * 		<li>{@link fatal()}</li>
+ * 	</ul>
+ *
+ * @license	http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
+ * @link http://logging.apache.org/log4php
+ */
+class Logger {
+
+	/**
+	 * Logger additivity. If set to true then child loggers will inherit
+	 * the appenders of their ancestors by default.
+	 * @var boolean
+	 */
+	private $additive = true;
+
+	/**
+	 * The Logger's fully qualified class name.
+	 * TODO: Determine if this is useful.
+	 */
+	private $fqcn = 'Logger';
+
+	/** The assigned Logger level. */
+	private $level;
+
+	/** The name of this Logger instance. */
+	private $name;
+
+	/** The parent logger. Set to null if this is the root logger. */
+	private $parent;
+
+	/** A collection of appenders linked to this logger. */
+	private $appenders = array();
+
+	/**
+	 * Constructor.
+	 * @param string $name Name of the logger.
+	 */
+	public function __construct($name) {
+		$this->name = $name;
+	}
+
+	/**
+	 * Returns the logger name.
+	 * @return string
+	 */
+	public function getName() {
+		return $this->name;
+	}
+
+	/**
+	 * Returns the parent Logger. Can be null if this is the root logger.
+	 * @return Logger
+	 */
+	public function getParent() {
+		return $this->parent;
+	}
+
+	// ******************************************
+	// *** Logging methods                    ***
+	// ******************************************
+
+	/**
+	 * Log a message object with the TRACE level.
+	 *
+	 * @param mixed $message message
+ 	 * @param Exception $throwable Optional throwable information to include
+	 *   in the logging event.
+	 */
+	public function trace($message, $throwable = null) {
+		$this->log(Level::getLevelTrace(), $message, $throwable);
+	}
+
+	/**
+	 * Log a message object with the DEBUG level.
+	 *
+	 * @param mixed $message message
+ 	 * @param Exception $throwable Optional throwable information to include
+	 *   in the logging event.
+	 */
+	public function debug($message, $throwable = null) {
+		$this->log(Level::getLevelDebug(), $message, $throwable);
+	}
+
+	/**
+	 * Log a message object with the INFO Level.
+	 *
+	 * @param mixed $message message
+ 	 * @param Exception $throwable Optional throwable information to include
+	 *   in the logging event.
+	 */
+	public function info($message, $throwable = null) {
+		$this->log(Level::getLevelInfo(), $message, $throwable);
+	}
+
+	/**
+	 * Log a message with the WARN level.
+	 *
+	 * @param mixed $message message
+  	 * @param Exception $throwable Optional throwable information to include
+	 *   in the logging event.
+	 */
+	public function warn($message, $throwable = null) {
+		$this->log(Level::getLevelWarn(), $message, $throwable);
+	}
+
+	/**
+	 * Log a message object with the ERROR level.
+	 *
+	 * @param mixed $message message
+	 * @param Exception $throwable Optional throwable information to include
+	 *   in the logging event.
+	 */
+	public function error($message, $throwable = null) {
+		$this->log(Level::getLevelError(), $message, $throwable);
+	}
+
+	/**
+	 * Log a message object with the FATAL level.
+	 *
+	 * @param mixed $message message
+	 * @param Exception $throwable Optional throwable information to include
+	 *   in the logging event.
+	 */
+	public function fatal($message, $throwable = null) {
+		$this->log(Level::getLevelFatal(), $message, $throwable);
+	}
+
+	/**
+	 * Log a message using the provided logging level.
+	 *
+	 * @param Level $level The logging level.
+	 * @param mixed $message Message to log.
+ 	 * @param Exception $throwable Optional throwable information to include
+	 *   in the logging event.
+	 */
+	public function log(Level $level, $message, $throwable = null) {
+		if($this->isEnabledFor($level)) {
+			$event = new LoggingEvent($this->fqcn, $this, $level, $message, null, $throwable);
+			$this->callAppenders($event);
+		}
+
+		// Forward the event upstream if additivity is turned on
+		if(isset($this->parent) && $this->getAdditivity()) {
+
+			// Use the event if already created
+			if (isset($event)) {
+				$this->parent->logEvent($event);
+			} else {
+				$this->parent->log($level, $message, $throwable);
+			}
+		}
+	}
+
+	/**
+	 * Logs an already prepared logging event object.
+	 * @param LoggingEvent $event
+	 */
+	public function logEvent(LoggingEvent $event) {
+		if($this->isEnabledFor($event->getLevel())) {
+			$this->callAppenders($event);
+		}
+
+		// Forward the event upstream if additivity is turned on
+		if(isset($this->parent) && $this->getAdditivity()) {
+			$this->parent->logEvent($event);
+		}
+	}
+
+	/**
+	 * If assertion parameter evaluates as false, then logs the message
+	 * using the ERROR level.
+	 *
+	 * @param bool $assertion
+	 * @param string $msg message to log
+	 */
+	public function assertLog($assertion = true, $msg = '') {
+		if($assertion == false) {
+			$this->error($msg);
+		}
+	}
+
+	/**
+	 * This method creates a new logging event and logs the event without
+	 * further checks.
+	 *
+	 * It should not be called directly. Use {@link trace()}, {@link debug()},
+	 * {@link info()}, {@link warn()}, {@link error()} and {@link fatal()}
+	 * wrappers.
+	 *
+	 * @param string $fqcn Fully qualified class name of the Logger
+	 * @param Exception $throwable Optional throwable information to include
+	 *   in the logging event.
+	 * @param Level $level log level
+	 * @param mixed $message message to log
+	 */
+	public function forcedLog($fqcn, $throwable, Level $level, $message) {
+		$event = new LoggingEvent($fqcn, $this, $level, $message, null, $throwable);
+		$this->callAppenders($event);
+
+		// Forward the event upstream if additivity is turned on
+		if(isset($this->parent) && $this->getAdditivity()) {
+			$this->parent->logEvent($event);
+		}
+	}
+
+	/**
+	 * Forwards the given logging event to all linked appenders.
+	 * @param LoggingEvent $event
+	 */
+	public function callAppenders($event) {
+		foreach($this->appenders as $appender) {
+			$appender->doAppend($event);
+		}
+	}
+
+	// ******************************************
+	// *** Checker methods                    ***
+	// ******************************************
+
+	/**
+	 * Check whether this Logger is enabled for a given Level passed as parameter.
+	 *
+	 * @param Level level
+	 * @return boolean
+	 */
+	public function isEnabledFor(Level $level) {
+		return $level->isGreaterOrEqual($this->getEffectiveLevel());
+	}
+
+	/**
+	 * Check whether this Logger is enabled for the TRACE Level.
+	 * @return boolean
+	 */
+	public function isTraceEnabled() {
+		return $this->isEnabledFor(Level::getLevelTrace());
+	}
+
+	/**
+	 * Check whether this Logger is enabled for the DEBUG Level.
+	 * @return boolean
+	 */
+	public function isDebugEnabled() {
+		return $this->isEnabledFor(Level::getLevelDebug());
+	}
+
+	/**
+	 * Check whether this Logger is enabled for the INFO Level.
+	 * @return boolean
+	 */
+	public function isInfoEnabled() {
+		return $this->isEnabledFor(Level::getLevelInfo());
+	}
+
+	/**
+	 * Check whether this Logger is enabled for the WARN Level.
+	 * @return boolean
+	 */
+	public function isWarnEnabled() {
+		return $this->isEnabledFor(Level::getLevelWarn());
+	}
+
+	/**
+	 * Check whether this Logger is enabled for the ERROR Level.
+	 * @return boolean
+	 */
+	public function isErrorEnabled() {
+		return $this->isEnabledFor(Level::getLevelError());
+	}
+
+	/**
+	 * Check whether this Logger is enabled for the FATAL Level.
+	 * @return boolean
+	 */
+	public function isFatalEnabled() {
+		return $this->isEnabledFor(Level::getLevelFatal());
+	}
+
+	// ******************************************
+	// *** Configuration methods              ***
+	// ******************************************
+
+	/**
+	 * Adds a new appender to the Logger.
+	 * @param Appender $appender The appender to add.
+	 */
+	public function addAppender($appender) {
+		$appenderName = $appender->getName();
+		$this->appenders[$appenderName] = $appender;
+	}
+
+	/** Removes all appenders from the Logger. */
+	public function removeAllAppenders() {
+		foreach($this->appenders as $name => $appender) {
+			$this->removeAppender($name);
+		}
+	}
+
+	/**
+	 * Remove the appender passed as parameter form the Logger.
+	 * @param mixed $appender an appender name or a {@link Appender} instance.
+	 */
+	public function removeAppender($appender) {
+		if($appender instanceof Appender) {
+			$appender->close();
+			unset($this->appenders[$appender->getName()]);
+		} else if (is_string($appender) and isset($this->appenders[$appender])) {
+			$this->appenders[$appender]->close();
+			unset($this->appenders[$appender]);
+		}
+	}
+
+	/**
+	 * Returns the appenders linked to this logger as an array.
+	 * @return array collection of appender names
+	 */
+	public function getAllAppenders() {
+		return $this->appenders;
+	}
+
+	/**
+	 * Returns a linked appender by name.
+	 * @return Appender
+	 */
+	public function getAppender($name) {
+		return $this->appenders[$name];
+	}
+
+	/**
+	 * Sets the additivity flag.
+	 * @param boolean $additive
+	 */
+	public function setAdditivity($additive) {
+		$this->additive = (bool)$additive;
+	}
+
+	/**
+	 * Returns the additivity flag.
+	 * @return boolean
+	 */
+	public function getAdditivity() {
+		return $this->additive;
+	}
+
+	/**
+	 * Starting from this Logger, search the Logger hierarchy for a non-null level and return it.
+	 * @see Level
+	 * @return Level or null
+	 */
+	public function getEffectiveLevel() {
+		for($logger = $this; $logger !== null; $logger = $logger->getParent()) {
+			if($logger->getLevel() !== null) {
+				return $logger->getLevel();
+			}
+		}
+	}
+
+	/**
+	 * Get the assigned Logger level.
+	 * @return Level The assigned level or null if none is assigned.
+	 */
+	public function getLevel() {
+		return $this->level;
+	}
+
+	/**
+	 * Set the Logger level.
+	 *
+	 * Use Level::getLevelXXX() methods to get a Level object, e.g.
+	 * <code>$logger->setLevel(Level::getLevelInfo());</code>
+	 *
+	 * @param Level $level The level to set, or NULL to clear the logger level.
+	 */
+	public function setLevel(Level $level = null) {
+		$this->level = $level;
+	}
+
+	/**
+	 * Checks whether an appender is attached to this logger instance.
+	 *
+	 * @param Appender $appender
+	 * @return boolean
+	 */
+	public function isAttached(Appender $appender) {
+		return isset($this->appenders[$appender->getName()]);
+	}
+
+	/**
+	 * Sets the parent logger.
+	 * @param Logger $logger
+	 */
+	public function setParent(Logger $logger) {
+		$this->parent = $logger;
+	}
+
+	// ******************************************
+	// *** Static methods and properties      ***
+	// ******************************************
+
+	/** The logger hierarchy used by log4php. */
+	private static $hierarchy;
+
+	/** Inidicates if log4php has been initialized */
+	private static $initialized = false;
+
+	/**
+	 * Returns the hierarchy used by this Logger.
+	 *
+	 * Caution: do not use this hierarchy unless you have called initialize().
+	 * To get Loggers, use the Logger::getLogger and Logger::getRootLogger
+	 * methods instead of operating on on the hierarchy directly.
+	 *
+	 * @return Hierarchy
+	 */
+	public static function getHierarchy() {
+		if(!isset(self::$hierarchy)) {
+			self::$hierarchy = new Hierarchy(new RootLogger());
+		}
+		return self::$hierarchy;
+	}
+
+	/**
+	 * Returns a Logger by name. If it does not exist, it will be created.
+	 *
+	 * @param string $name The logger name
+	 * @return Logger
+	 */
+	public static function getLogger($name) {
+		if(!self::isInitialized()) {
+			self::configure();
+		}
+		return self::getHierarchy()->getLogger($name);
+	}
+
+	/**
+	 * Returns the Root Logger.
+	 * @return RootLogger
+	 */
+	public static function getRootLogger() {
+		if(!self::isInitialized()) {
+			self::configure();
+		}
+		return self::getHierarchy()->getRootLogger();
+	}
+
+	/**
+	 * Clears all Logger definitions from the logger hierarchy.
+	 * @return boolean
+	 */
+	public static function clear() {
+		return self::getHierarchy()->clear();
+	}
+
+	/**
+	 * Destroy configurations for logger definitions
+	 */
+	public static function resetConfiguration() {
+		self::getHierarchy()->resetConfiguration();
+		self::getHierarchy()->clear(); // TODO: clear or not?
+		self::$initialized = false;
+	}
+
+	/**
+	 * Safely close all appenders.
+	 * @deprecated This is no longer necessary due the appenders shutdown via
+	 * destructors.
+	 */
+	public static function shutdown() {
+		return self::getHierarchy()->shutdown();
+	}
+
+	/**
+	 * check if a given logger exists.
+	 *
+	 * @param string $name logger name
+	 * @return boolean
+	 */
+	public static function exists($name) {
+		return self::getHierarchy()->exists($name);
+	}
+
+	/**
+	 * Returns an array this whole Logger instances.
+	 * @see Logger
+	 * @return array
+	 */
+	public static function getCurrentLoggers() {
+		return self::getHierarchy()->getCurrentLoggers();
+	}
+
+	/**
+	 * Configures log4php.
+	 *
+	 * This method needs to be called before the first logging event has
+	 * occured. If this method is not called before then the default
+	 * configuration will be used.
+	 *
+	 * @param string|array $configuration Either a path to the configuration
+	 *   file, or a configuration array.
+	 *
+	 * @param string|Configurator $configurator A custom
+	 * configurator class: either a class name (string), or an object which
+	 * implements the Configurator interface. If left empty, the default
+	 * configurator implementation will be used.
+	 */
+	public static function configure($configuration = null, $configurator = null) {
+		self::resetConfiguration();
+		$configurator = self::getConfigurator($configurator);
+		$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|Configurator $configurator The configurator class
+	 * or Configurator instance.
+	 */
+	private static function getConfigurator($configurator = null) {
+		if ($configurator === null) {
+			return new Configuration\DefaultConfigurator();
+		}
+
+		if (is_object($configurator)) {
+			if ($configurator instanceof Configurator) {
+				return $configurator;
+			} else {
+				trigger_error("log4php: Given configurator object [$configurator] does not implement the Configurator interface. Reverting to default configurator.", E_USER_WARNING);
+				return new Configuration\DefaultConfigurator();
+			}
+		}
+
+		if (is_string($configurator)) {
+			if (!class_exists($configurator)) {
+				trigger_error("log4php: Specified configurator class [$configurator] does not exist. Reverting to default configurator.", E_USER_WARNING);
+				return new Configuration\DefaultConfigurator();
+			}
+
+			$instance = new $configurator();
+
+			if (!($instance instanceof Configurator)) {
+				trigger_error("log4php: Specified configurator class [$configurator] does not implement the Configurator interface. Reverting to default configurator.", E_USER_WARNING);
+				return new Configuration\DefaultConfigurator();
+			}
+
+			return $instance;
+		}
+
+		trigger_error("log4php: Invalid configurator specified. Expected either a string or a Configurator instance. Reverting to default configurator.", E_USER_WARNING);
+		return new Configuration\DefaultConfigurator();
+	}
+
+	/**
+	 * Returns true if the log4php framework has been initialized.
+	 * @return boolean
+	 */
+	private static function isInitialized() {
+		return self::$initialized;
+	}
+}

http://git-wip-us.apache.org/repos/asf/logging-log4php/blob/79ed2d0d/src/LoggerException.php
----------------------------------------------------------------------
diff --git a/src/LoggerException.php b/src/LoggerException.php
new file mode 100644
index 0000000..103c532
--- /dev/null
+++ b/src/LoggerException.php
@@ -0,0 +1,25 @@
+<?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;
+
+/**
+ * LoggerException class
+ */
+class LoggerException extends \Exception {
+}