new file mode 100644
--- /dev/null
+++ b/library/log4php/appenders/LoggerAppenderPDO.php
@@ -0,0 +1,282 @@
+<?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.
+ */
+
+/**
+ * LoggerAppenderPDO appender logs to a database using the PHP's PDO extension.
+ *
+ * ## Configurable parameters: ##
+ *
+ * - dsn - The Data Source Name (DSN) used to connect to the database.
+ * - user - Username used to connect to the database.
+ * - password - Password used to connect to the database.
+ * - table - Name of the table to which log entries are be inserted.
+ * - insertSQL - Sets the insert statement for a logging event. Defaults
+ * to the correct one - change only if you are sure what you are doing.
+ * - insertPattern - The conversion pattern to use in conjuction with insert
+ * SQL. Must contain the same number of comma separated
+ * conversion patterns as there are question marks in the
+ * insertSQL.
+ *
+ * @version $Revision: 1374546 $
+ * @package log4php
+ * @subpackage appenders
+ * @since 2.0
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
+ * @link http://logging.apache.org/log4php/docs/appenders/pdo.html Appender documentation
+ */
+class LoggerAppenderPDO extends LoggerAppender {
+
+ // ******************************************
+ // *** Configurable parameters ***
+ // ******************************************
+
+ /**
+ * DSN string used to connect to the database.
+ * @see http://www.php.net/manual/en/pdo.construct.php
+ */
+ protected $dsn;
+
+ /** Database user name. */
+ protected $user;
+
+ /** Database password. */
+ protected $password;
+
+ /**
+ * The insert query.
+ *
+ * The __TABLE__ placeholder will be replaced by the table name from
+ * {@link $table}.
+ *
+ * The questionmarks are part of the prepared statement, and they must
+ * match the number of conversion specifiers in {@link insertPattern}.
+ */
+ protected $insertSQL = "INSERT INTO __TABLE__ (timestamp, logger, level, message, thread, file, line) VALUES (?, ?, ?, ?, ?, ?, ?)";
+
+ /**
+ * A comma separated list of {@link LoggerPatternLayout} format strings
+ * which replace the "?" in {@link $insertSQL}.
+ *
+ * Must contain the same number of comma separated conversion patterns as
+ * there are question marks in {@link insertSQL}.
+ *
+ * @see LoggerPatternLayout For conversion patterns.
+ */
+ protected $insertPattern = "%date{Y-m-d H:i:s},%logger,%level,%message,%pid,%file,%line";
+
+ /** Name of the table to which to append log events. */
+ protected $table = 'log4php_log';
+
+ /** The number of recconect attempts to make on failed append. */
+ protected $reconnectAttempts = 3;
+
+
+ // ******************************************
+ // *** Private memebers ***
+ // ******************************************
+
+ /**
+ * The PDO instance.
+ * @var PDO
+ */
+ protected $db;
+
+ /**
+ * Prepared statement for the insert query.
+ * @var PDOStatement
+ */
+ protected $preparedInsert;
+
+ /** This appender does not require a layout. */
+ protected $requiresLayout = false;
+
+
+ // ******************************************
+ // *** Appender methods ***
+ // ******************************************
+
+ /**
+ * Acquires a database connection based on parameters.
+ * Parses the insert pattern to create a chain of converters which will be
+ * used in forming query parameters from logging events.
+ */
+ public function activateOptions() {
+ try {
+ $this->establishConnection();
+ } catch (PDOException $e) {
+ $this->warn("Failed connecting to database. Closing appender. Error: " . $e->getMessage());
+ $this->close();
+ return;
+ }
+
+ // Parse the insert patterns; pattern parts are comma delimited
+ $pieces = explode(',', $this->insertPattern);
+ $converterMap = LoggerLayoutPattern::getDefaultConverterMap();
+ foreach ($pieces as $pattern) {
+ $parser = new LoggerPatternParser($pattern, $converterMap);
+ $this->converters[] = $parser->parse();
+ }
+
+ $this->closed = false;
+ }
+
+ /**
+ * Connects to the database, and prepares the insert query.
+ * @throws PDOException If connect or prepare fails.
+ */
+ protected function establishConnection() {
+ // Acquire database connection
+ $this->db = new PDO($this->dsn, $this->user, $this->password);
+ $this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+
+ // Prepare the insert statement
+ $insertSQL = str_replace('__TABLE__', $this->table, $this->insertSQL);
+ $this->preparedInsert = $this->db->prepare($insertSQL);
+ }
+
+ /**
+ * Appends a new event to the database.
+ *
+ * If writing to database fails, it will retry by re-establishing the
+ * connection up to $reconnectAttempts times. If writing still fails,
+ * the appender will close.
+ */
+ public function append(LoggerLoggingEvent $event) {
+
+ for ($attempt = 1; $attempt <= $this->reconnectAttempts + 1; $attempt++) {
+ try {
+ // Attempt to write to database
+ $this->preparedInsert->execute($this->format($event));
+ $this->preparedInsert->closeCursor();
+ break;
+ } catch (PDOException $e) {
+ $this->warn("Failed writing to database: " . $e->getMessage());
+
+ // Close the appender if it's the last attempt
+ if ($attempt > $this->reconnectAttempts) {
+ $this->warn("Failed writing to database after {$this->reconnectAttempts} reconnect attempts. Closing appender.");
+ $this->close();
+ // Otherwise reconnect and try to write again
+ } else {
+ $this->warn("Attempting a reconnect (attempt $attempt of {$this->reconnectAttempts}).");
+ $this->establishConnection();
+ }
+ }
+ }
+ }
+
+ /**
+ * Converts the logging event to a series of database parameters by using
+ * the converter chain which was set up on activation.
+ */
+ protected function format(LoggerLoggingEvent $event) {
+ $params = array();
+ foreach ($this->converters as $converter) {
+ $buffer = '';
+ while ($converter !== null) {
+ $converter->format($buffer, $event);
+ $converter = $converter->next;
+ }
+ $params[] = $buffer;
+ }
+ return $params;
+ }
+
+ /**
+ * Closes the connection to the logging database
+ */
+ public function close() {
+ // Close the connection (if any)
+ $this->db = null;
+
+ // Close the appender
+ $this->closed = true;
+ }
+
+ // ******************************************
+ // *** Accessor methods ***
+ // ******************************************
+
+ /**
+ * Returns the active database handle or null if not established.
+ * @return PDO
+ */
+ public function getDatabaseHandle() {
+ return $this->db;
+ }
+
+ /** Sets the username. */
+ public function setUser($user) {
+ $this->setString('user', $user);
+ }
+
+ /** Returns the username. */
+ public function getUser($user) {
+ return $this->user;
+ }
+
+ /** Sets the password. */
+ public function setPassword($password) {
+ $this->setString('password', $password);
+ }
+
+ /** Returns the password. */
+ public function getPassword($password) {
+ return $this->password;
+ }
+
+ /** Sets the insert SQL. */
+ public function setInsertSQL($sql) {
+ $this->setString('insertSQL', $sql);
+ }
+
+ /** Returns the insert SQL. */
+ public function getInsertSQL($sql) {
+ return $this->insertSQL;
+ }
+
+ /** Sets the insert pattern. */
+ public function setInsertPattern($pattern) {
+ $this->setString('insertPattern', $pattern);
+ }
+
+ /** Returns the insert pattern. */
+ public function getInsertPattern($pattern) {
+ return $this->insertPattern;
+ }
+
+ /** Sets the table name. */
+ public function setTable($table) {
+ $this->setString('table', $table);
+ }
+
+ /** Returns the table name. */
+ public function getTable($table) {
+ return $this->table;
+ }
+
+ /** Sets the DSN string. */
+ public function setDSN($dsn) {
+ $this->setString('dsn', $dsn);
+ }
+
+ /** Returns the DSN string. */
+ public function getDSN($dsn) {
+ return $this->setString('dsn', $dsn);
+ }
+}