| Code Coverage
| Classes and Traits | Functions and Methods | Lines
Total |  | 0.00% | 0 / 1 |  | 38.89% | 7 / 18 | CRAP |  | 64.06% | 41 / 64
LoggerAppenderPDO |  | 0.00% | 0 / 1 |  | 38.89% | 7 / 18 | 54.01 |  | 64.06% | 41 / 64
activateOptions() |  | 0.00% | 0 / 1 | 3.03 |  | 84.62% | 11 / 13
establishConnection() |  | 100.00% | 1 / 1 | 1 |  | 100.00% | 5 / 5
append(LoggerLoggingEvent $event) |  | 0.00% | 0 / 1 | 8.25 |  | 35.71% | 5 / 14
format(LoggerLoggingEvent $event) |  | 100.00% | 1 / 1 | 3 |  | 100.00% | 10 / 10
close() |  | 100.00% | 1 / 1 | 1 |  | 100.00% | 3 / 3
getDatabaseHandle() |  | 100.00% | 1 / 1 | 1 |  | 100.00% | 1 / 1
setUser($user) |  | 0.00% | 0 / 1 | 2 |  | 0.00% | 0 / 2
getUser($user) |  | 0.00% | 0 / 1 | 2 |  | 0.00% | 0 / 1
setPassword($password) |  | 0.00% | 0 / 1 | 2 |  | 0.00% | 0 / 2
getPassword($password) |  | 0.00% | 0 / 1 | 2 |  | 0.00% | 0 / 1
setInsertSQL($sql) |  | 100.00% | 1 / 1 | 1 |  | 100.00% | 2 / 2
getInsertSQL($sql) |  | 0.00% | 0 / 1 | 2 |  | 0.00% | 0 / 1
setInsertPattern($pattern) |  | 100.00% | 1 / 1 | 1 |  | 100.00% | 2 / 2
getInsertPattern($pattern) |  | 0.00% | 0 / 1 | 2 |  | 0.00% | 0 / 1
setTable($table) |  | 0.00% | 0 / 1 | 2 |  | 0.00% | 0 / 2
getTable($table) |  | 0.00% | 0 / 1 | 2 |  | 0.00% | 0 / 1
setDSN($dsn) |  | 100.00% | 1 / 1 | 1 |  | 100.00% | 2 / 2
getDSN($dsn) |  | 0.00% | 0 / 1 | 2 |  | 0.00% | 0 / 1

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 | }