library/log4php/appenders/LoggerAppenderPDO.php
changeset 0 4869aea77e21
equal deleted inserted replaced
-1:000000000000 0:4869aea77e21
       
     1 <?php
       
     2 /**
       
     3  * Licensed to the Apache Software Foundation (ASF) under one or more
       
     4  * contributor license agreements.  See the NOTICE file distributed with
       
     5  * this work for additional information regarding copyright ownership.
       
     6  * The ASF licenses this file to You under the Apache License, Version 2.0
       
     7  * (the "License"); you may not use this file except in compliance with
       
     8  * the License.  You may obtain a copy of the License at
       
     9  *
       
    10  *     http://www.apache.org/licenses/LICENSE-2.0
       
    11  *
       
    12  * Unless required by applicable law or agreed to in writing, software
       
    13  * distributed under the License is distributed on an "AS IS" BASIS,
       
    14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
       
    15  * See the License for the specific language governing permissions and
       
    16  * limitations under the License.
       
    17  */
       
    18 
       
    19 /**
       
    20  * LoggerAppenderPDO appender logs to a database using the PHP's PDO extension.
       
    21  *
       
    22  * ## Configurable parameters: ##
       
    23  *
       
    24  * - dsn             - The Data Source Name (DSN) used to connect to the database.
       
    25  * - user            - Username used to connect to the database.
       
    26  * - password        - Password used to connect to the database.
       
    27  * - table           - Name of the table to which log entries are be inserted.
       
    28  * - insertSQL       - Sets the insert statement for a logging event. Defaults
       
    29  *                     to the correct one - change only if you are sure what you are doing.
       
    30  * - insertPattern   - The conversion pattern to use in conjuction with insert
       
    31  *                     SQL. Must contain the same number of comma separated
       
    32  *                     conversion patterns as there are question marks in the
       
    33  *                     insertSQL.
       
    34  *
       
    35  * @version $Revision: 1374546 $
       
    36  * @package log4php
       
    37  * @subpackage appenders
       
    38  * @since 2.0
       
    39  * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
       
    40  * @link http://logging.apache.org/log4php/docs/appenders/pdo.html Appender documentation
       
    41  */
       
    42 class LoggerAppenderPDO extends LoggerAppender {
       
    43 
       
    44     // ******************************************
       
    45     // *** Configurable parameters            ***
       
    46     // ******************************************
       
    47 
       
    48     /**
       
    49      * DSN string used to connect to the database.
       
    50      * @see http://www.php.net/manual/en/pdo.construct.php
       
    51      */
       
    52     protected $dsn;
       
    53 
       
    54     /** Database user name. */
       
    55     protected $user;
       
    56 
       
    57     /** Database password. */
       
    58     protected $password;
       
    59 
       
    60     /**
       
    61      * The insert query.
       
    62      *
       
    63      * The __TABLE__ placeholder will be replaced by the table name from
       
    64      * {@link $table}.
       
    65      *
       
    66      * The questionmarks are part of the prepared statement, and they must
       
    67      * match the number of conversion specifiers in {@link insertPattern}.
       
    68      */
       
    69     protected $insertSQL = "INSERT INTO __TABLE__ (timestamp, logger, level, message, thread, file, line) VALUES (?, ?, ?, ?, ?, ?, ?)";
       
    70 
       
    71     /**
       
    72      * A comma separated list of {@link LoggerPatternLayout} format strings
       
    73      * which replace the "?" in {@link $insertSQL}.
       
    74      *
       
    75      * Must contain the same number of comma separated conversion patterns as
       
    76      * there are question marks in {@link insertSQL}.
       
    77      *
       
    78      * @see LoggerPatternLayout For conversion patterns.
       
    79      */
       
    80     protected $insertPattern = "%date{Y-m-d H:i:s},%logger,%level,%message,%pid,%file,%line";
       
    81 
       
    82     /** Name of the table to which to append log events. */
       
    83     protected $table = 'log4php_log';
       
    84 
       
    85     /** The number of recconect attempts to make on failed append. */
       
    86     protected $reconnectAttempts = 3;
       
    87 
       
    88 
       
    89     // ******************************************
       
    90     // *** Private memebers                   ***
       
    91     // ******************************************
       
    92 
       
    93     /**
       
    94      * The PDO instance.
       
    95      * @var PDO
       
    96      */
       
    97     protected $db;
       
    98 
       
    99     /**
       
   100      * Prepared statement for the insert query.
       
   101      * @var PDOStatement
       
   102      */
       
   103     protected $preparedInsert;
       
   104 
       
   105     /** This appender does not require a layout. */
       
   106     protected $requiresLayout = false;
       
   107 
       
   108 
       
   109     // ******************************************
       
   110     // *** Appender methods                   ***
       
   111     // ******************************************
       
   112 
       
   113     /**
       
   114      * Acquires a database connection based on parameters.
       
   115      * Parses the insert pattern to create a chain of converters which will be
       
   116      * used in forming query parameters from logging events.
       
   117      */
       
   118     public function activateOptions() {
       
   119         try {
       
   120             $this->establishConnection();
       
   121         } catch (PDOException $e) {
       
   122             $this->warn("Failed connecting to database. Closing appender. Error: " . $e->getMessage());
       
   123             $this->close();
       
   124             return;
       
   125         }
       
   126 
       
   127         // Parse the insert patterns; pattern parts are comma delimited
       
   128         $pieces = explode(',', $this->insertPattern);
       
   129         $converterMap = LoggerLayoutPattern::getDefaultConverterMap();
       
   130         foreach ($pieces as $pattern) {
       
   131             $parser = new LoggerPatternParser($pattern, $converterMap);
       
   132             $this->converters[] = $parser->parse();
       
   133         }
       
   134 
       
   135         $this->closed = false;
       
   136     }
       
   137 
       
   138     /**
       
   139      * Connects to the database, and prepares the insert query.
       
   140      * @throws PDOException If connect or prepare fails.
       
   141      */
       
   142     protected function establishConnection() {
       
   143         // Acquire database connection
       
   144         $this->db = new PDO($this->dsn, $this->user, $this->password);
       
   145         $this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
       
   146 
       
   147         // Prepare the insert statement
       
   148         $insertSQL = str_replace('__TABLE__', $this->table, $this->insertSQL);
       
   149         $this->preparedInsert = $this->db->prepare($insertSQL);
       
   150     }
       
   151 
       
   152     /**
       
   153      * Appends a new event to the database.
       
   154      *
       
   155      * If writing to database fails, it will retry by re-establishing the
       
   156      * connection up to $reconnectAttempts times. If writing still fails,
       
   157      * the appender will close.
       
   158      */
       
   159     public function append(LoggerLoggingEvent $event) {
       
   160 
       
   161         for ($attempt = 1; $attempt <= $this->reconnectAttempts + 1; $attempt++) {
       
   162             try {
       
   163                 // Attempt to write to database
       
   164                 $this->preparedInsert->execute($this->format($event));
       
   165                 $this->preparedInsert->closeCursor();
       
   166                 break;
       
   167             } catch (PDOException $e) {
       
   168                 $this->warn("Failed writing to database: " . $e->getMessage());
       
   169 
       
   170                 // Close the appender if it's the last attempt
       
   171                 if ($attempt > $this->reconnectAttempts) {
       
   172                     $this->warn("Failed writing to database after {$this->reconnectAttempts} reconnect attempts. Closing appender.");
       
   173                     $this->close();
       
   174                     // Otherwise reconnect and try to write again
       
   175                 } else {
       
   176                     $this->warn("Attempting a reconnect (attempt $attempt of {$this->reconnectAttempts}).");
       
   177                     $this->establishConnection();
       
   178                 }
       
   179             }
       
   180         }
       
   181     }
       
   182 
       
   183     /**
       
   184      * Converts the logging event to a series of database parameters by using
       
   185      * the converter chain which was set up on activation.
       
   186      */
       
   187     protected function format(LoggerLoggingEvent $event) {
       
   188         $params = array();
       
   189         foreach ($this->converters as $converter) {
       
   190             $buffer = '';
       
   191             while ($converter !== null) {
       
   192                 $converter->format($buffer, $event);
       
   193                 $converter = $converter->next;
       
   194             }
       
   195             $params[] = $buffer;
       
   196         }
       
   197         return $params;
       
   198     }
       
   199 
       
   200     /**
       
   201      * Closes the connection to the logging database
       
   202      */
       
   203     public function close() {
       
   204         // Close the connection (if any)
       
   205         $this->db = null;
       
   206 
       
   207         // Close the appender
       
   208         $this->closed = true;
       
   209     }
       
   210 
       
   211     // ******************************************
       
   212     // *** Accessor methods                   ***
       
   213     // ******************************************
       
   214 
       
   215     /**
       
   216      * Returns the active database handle or null if not established.
       
   217      * @return PDO
       
   218      */
       
   219     public function getDatabaseHandle() {
       
   220         return $this->db;
       
   221     }
       
   222 
       
   223     /** Sets the username. */
       
   224     public function setUser($user) {
       
   225         $this->setString('user', $user);
       
   226     }
       
   227 
       
   228     /** Returns the username. */
       
   229     public function getUser($user) {
       
   230         return $this->user;
       
   231     }
       
   232 
       
   233     /** Sets the password. */
       
   234     public function setPassword($password) {
       
   235         $this->setString('password', $password);
       
   236     }
       
   237 
       
   238     /** Returns the password. */
       
   239     public function getPassword($password) {
       
   240         return $this->password;
       
   241     }
       
   242 
       
   243     /** Sets the insert SQL. */
       
   244     public function setInsertSQL($sql) {
       
   245         $this->setString('insertSQL', $sql);
       
   246     }
       
   247 
       
   248     /** Returns the insert SQL. */
       
   249     public function getInsertSQL($sql) {
       
   250         return $this->insertSQL;
       
   251     }
       
   252 
       
   253     /** Sets the insert pattern. */
       
   254     public function setInsertPattern($pattern) {
       
   255         $this->setString('insertPattern', $pattern);
       
   256     }
       
   257 
       
   258     /** Returns the insert pattern. */
       
   259     public function getInsertPattern($pattern) {
       
   260         return $this->insertPattern;
       
   261     }
       
   262 
       
   263     /** Sets the table name. */
       
   264     public function setTable($table) {
       
   265         $this->setString('table', $table);
       
   266     }
       
   267 
       
   268     /** Returns the table name. */
       
   269     public function getTable($table) {
       
   270         return $this->table;
       
   271     }
       
   272 
       
   273     /** Sets the DSN string. */
       
   274     public function setDSN($dsn) {
       
   275         $this->setString('dsn', $dsn);
       
   276     }
       
   277 
       
   278     /** Returns the DSN string. */
       
   279     public function getDSN($dsn) {
       
   280         return $this->setString('dsn', $dsn);
       
   281     }
       
   282 }