diff --git a/library/log4php/appenders/LoggerAppenderPDO.php b/library/log4php/appenders/LoggerAppenderPDO.php new file mode 100644 --- /dev/null +++ b/library/log4php/appenders/LoggerAppenderPDO.php @@ -0,0 +1,282 @@ +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); + } +}