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