LoggerAppenderPDO.php
9.75 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
<?php
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @package log4php
*/
/**
* Appends log events to a db table using PDO.
*
* Configurable parameters of this appender are:
*
* - user - Sets the user of this database connection
* - password - Sets the password of this database connection
* - createTable - true, if the table should be created if necessary. false otherwise
* - table - Sets the table name (default: log4php_log)
* - sql - Sets the insert statement for a logging event. Defaults
* to the correct one - change only if you are sure what you are doing.
* - dsn - Sets the DSN string for this connection
*
* If $sql is set then $table and $sql are used, else $table, $insertSql and $insertPattern.
*
* An example:
*
* {@example ../../examples/php/appender_pdo.php 19}
*
* {@example ../../examples/resources/appender_pdo.properties 18}
*
* @version $Revision: 806678 $
* @package log4php
* @subpackage appenders
* @since 2.0
*/
class LoggerAppenderPDO extends LoggerAppender {
/** Create the log table if it does not exists (optional).
* @var string */
private $createTable = true;
/** Database user name.
* @var string */
private $user = '';
/** Database password
* @var string */
private $password = '';
/** DSN string for enabling a connection.
* @var string */
private $dsn;
/** A {@link LoggerPatternLayout} string used to format a valid insert query.
* @deprecated Use {@link $insertSql} and {@link $insertPattern} which properly handle quotes in the messages!
* @var string */
private $sql;
/** Can be set to a complete insert statement with ? that are replaced using {@link insertPattern}.
* @var string */
private $insertSql = "INSERT INTO __TABLE__ (timestamp, logger, level, message, thread, file, line) VALUES (?,?,?,?,?,?,?)";
/** A comma separated list of {@link LoggerPatternLayout} format strings that replace the "?" in {@link $sql}.
* @var string */
private $insertPattern = "%d,%c,%p,%m,%t,%F,%L";
/** Table name to write events. Used only for CREATE TABLE if {@link $createTable} is true.
* @var string */
private $table = 'log4php_log';
/** The PDO instance.
* @var PDO */
private $db = null;
/** Prepared statement for the INSERT INTO query.
* @var PDOStatement */
private $preparedInsert;
/** Set in activateOptions() and later used in append() to check if all conditions to append are true.
* @var boolean */
private $canAppend = true;
/**
* Constructor.
* This apender doesn't require a layout.
* @param string $name appender name
*/
public function __construct($name = '') {
parent::__construct($name);
$this->requiresLayout = false;
}
public function __destruct() {
$this->close();
}
/**
* 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() {
try {
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);
}
$this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// test if log table exists
try {
$result = $this->db->query('SELECT * FROM ' . $this->table . ' WHERE 1 = 0');
} catch (PDOException $e) {
// It could be something else but a "no such table" is the most likely
$result = false;
}
// create table if necessary
if ($result == false and $this->createTable) {
// The syntax should at least be compatible with MySQL, PostgreSQL, SQLite and Oracle.
$query = "CREATE TABLE {$this->table} (".
"timestamp varchar(32)," .
"logger varchar(64)," .
"level varchar(32)," .
"message varchar(9999)," .
"thread varchar(32)," .
"file varchar(255)," .
"line varchar(6))";
$result = $this->db->query($query);
}
} catch (PDOException $e) {
$this->canAppend = false;
throw new LoggerException($e);
}
$this->layout = new LoggerLayoutPattern();
//
// Keep compatibility to legacy option $sql which already included the format patterns!
//
if (empty($this->sql)) {
// new style with prepared Statment and $insertSql and $insertPattern
// Maybe the tablename has to be substituted.
$this->insertSql = preg_replace('/__TABLE__/', $this->table, $this->insertSql);
$this->preparedInsert = $this->db->prepare($this->insertSql);
$this->layout->setConversionPattern($this->insertPattern);
} else {
// Old style with format strings in the $sql query should be used.
$this->layout->setConversionPattern($this->sql);
}
$this->canAppend = true;
return true;
}
/**
* Appends a new event to the database.
*
* @throws LoggerException If the pattern conversion or the INSERT statement fails.
*/
public function append(LoggerLoggingEvent $event) {
// TODO: Can't activateOptions() simply throw an Exception if it encounters problems?
if ( ! $this->canAppend) return;
try {
if (empty($this->sql)) {
// new style with prepared statement
$params = $this->layout->formatToArray($event);
$this->preparedInsert->execute($params);
} else {
// old style
$query = $this->layout->format($event);
$this->db->exec($query);
}
} catch (Exception $e) {
throw new LoggerException($e);
}
}
/**
* Closes the connection to the logging database
*/
public function close() {
if($this->closed != true) {
if ($this->db !== null) {
$db = null;
}
$this->closed = true;
}
}
/**
* Sets the username for this connection.
* Defaults to ''
*/
public function setUser($user) {
$this->user = $user;
}
/**
* Sets the password for this connection.
* Defaults to ''
*/
public function setPassword($password) {
$this->password = $password;
}
/**
* 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 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'
*
* @deprecated See {@link setInsertSql} and {@link setInsertPattern}.
*/
public function setSql($sql) {
$this->sql = $sql;
}
/**
* Sets the SQL INSERT string to use with {@link $insertPattern}.
*
* @param $sql A complete INSERT INTO query with "?" that gets replaced.
*/
public function setInsertSql($sql) {
$this->insertSql = $sql;
}
/**
* Sets the {@link LoggerLayoutPattern} format strings for {@link $insertSql}.
*
* It's not necessary to change this except you have customized logging.
*
* @param $pattern Comma separated format strings like "%p,%m,%C"
*/
public function setInsertPattern($pattern) {
$this->insertPattern = $pattern;
}
/**
* 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.
*
* @return PDO
*/
public function getDatabaseHandle() {
return $this->db;
}
}