This appender uses a table in a database to log events.
*Parameters are {@link $host}, {@link $user}, {@link $password}, * {@link $database}, {@link $createTable}, {@link $table} and {@link $sql}.
* * @package log4php * @subpackage appenders * @since 2.0 */ class LoggerAppenderPDO extends LoggerAppender { /** Create the log table if it does not exists (optional). */ private $createTable = true; /** Database user name */ private $user = ''; /** Database password */ private $password = ''; /** DSN string for enabling a connection */ private $dsn; /** A {@link LoggerPatternLayout} string used to format a valid insert query (mandatory) */ private $sql; /** Table name to write events. Used only if {@link $createTable} is true. */ private $table = 'log4php_log'; /** The instance */ private $db = null; /** boolean used to check if all conditions to append are true */ private $canAppend = true; /** * Constructor. * This apender doesn't require a layout. * @param string $name appender name */ function __construct($name) { parent::__construct($name); $this->requiresLayout = false; } /** * Setup db connection. * Based on defined options, this method connects to db defined in {@link $dsn} * and creates a {@link $table} table if {@link $createTable} is true. * @return boolean true if all ok. * @throws a PDOException if the attempt to connect to the requested database fails. */ public function activateOptions() { if($this->user === null) { $this->db = new PDO($this->dsn); } else if($this->password === null) { $this->db = new PDO($this->dsn, $this->user); } else { $this->db = new PDO($this->dsn,$this->user,$this->password); } // test if log table exists $result = $this->db->query('select * from ' . $this->table . ' where 1 = 0'); if ($result == false and $this->createTable) { // TODO mysql syntax? $query = "CREATE TABLE {$this->table} ( timestamp varchar(32)," . "logger varchar(32)," . "level varchar(32)," . "message varchar(64)," . "thread varchar(32)," . "file varchar(64)," . "line varchar(4) );"; $result = $this->db->query($query); if (!$result) { $this->canAppend = false; return; // TODO throw exception? } } if($this->sql == '' || $this->sql == null) { $this->sql = "INSERT INTO $this->table ( timestamp, " . "logger, " . "level, " . "message, " . "thread, " . "file, " . "line" . ") VALUES ('%d','%c','%p','%m','%t','%F','%L')"; } $this->layout = LoggerReflectionUtils::createObject('LoggerLayoutPattern'); $this->layout->setConversionPattern($this->sql); $this->canAppend = true; return true; } /** * Appends a new event to the database using the sql format. */ // TODO:should work with prepared statement public function append($event) { if ($this->canAppend) { $query = $this->layout->format($event); $this->db->exec($query); } } /** * Closes the connection to the logging database */ public function close() { if ($this->db !== null) { $db = null; } $this->closed = true; } /** * Indicator if the logging table should be created on startup, * if its not existing. */ public function setCreateTable($flag) { $this->createTable = LoggerOptionConverter::toBoolean($flag, true); } /** * Sets the username for this connection. * Defaults to '' */ function setUser($user) { $this->user = $user; } /** * Sets the password for this connection. * Defaults to '' */ public function setPassword($password) { $this->password = $password; } /** * Sets the SQL string into which the event should be transformed. * Defaults to: * * INSERT INTO $this->table * ( timestamp, logger, level, message, thread, file, line) * VALUES * ('%d','%c','%p','%m','%t','%F','%L') * * It's not necessary to change this except you have customized logging' */ public function setSql($sql) { $this->sql = $sql; } /** * Sets the tablename to which this appender should log. * Defaults to log4php_log */ public function setTable($table) { $this->table = $table; } /** * Sets the DSN string for this connection. In case of * SQLite it could look like this: 'sqlite:appenders/pdotest.sqlite' */ public function setDSN($dsn) { $this->dsn = $dsn; } /** * Sometimes databases allow only one connection to themselves in one thread. * SQLite has this behaviour. In that case this handle is needed if the database * must be checked for events */ public function getDatabaseHandle() { return $this->db; } }