1
|
|
|
<?php |
2
|
|
|
/* |
3
|
|
|
You may not change or alter any portion of this comment or credits |
4
|
|
|
of supporting developers from this source code or any supporting source code |
5
|
|
|
which is considered copyrighted (c) material of the original comment or credit authors. |
6
|
|
|
|
7
|
|
|
This program is distributed in the hope that it will be useful, |
8
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
9
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
10
|
|
|
*/ |
11
|
|
|
|
12
|
|
|
namespace Xoops\Core; |
13
|
|
|
|
14
|
|
|
use Psr\Log\LogLevel; |
15
|
|
|
use Psr\Log\LoggerInterface; |
16
|
|
|
|
17
|
|
|
/** |
18
|
|
|
* Xoops\Core\Logger - dispatch log requests to any registered loggers. |
19
|
|
|
* |
20
|
|
|
* No logging is done in this class, but any logger, implemented as a |
21
|
|
|
* module or extension, can register as a logger using the addLogger() |
22
|
|
|
* method. Multiple loggers can be registered, and each will be |
23
|
|
|
* invoked in turn for each log() call. |
24
|
|
|
* |
25
|
|
|
* Such loggers are expected to implement the PSR-3 LoggerInterface. |
26
|
|
|
* In addition, any logger that generates output as part of the XOOPS |
27
|
|
|
* delivered page should implement the quiet() method, to disable output. |
28
|
|
|
* |
29
|
|
|
* Loggers are managed this way so that any routine may easily add a |
30
|
|
|
* log entry without needing to know any details of the implementation. |
31
|
|
|
* |
32
|
|
|
* Not all events are published through this mechanism, only specific requests |
33
|
|
|
* to log() or related methods. Individual loggers may listen for events (i.e. |
34
|
|
|
* preloads) or other sources and gain access to detailed debugging information. |
35
|
|
|
* |
36
|
|
|
* @category Xoops\Core\Logger |
37
|
|
|
* @package Logger |
38
|
|
|
* @author Richard Griffith <[email protected]> |
39
|
|
|
* @copyright 2013 XOOPS Project (http://xoops.org) |
40
|
|
|
* @license GNU GPL 2 or later (http://www.gnu.org/licenses/gpl-2.0.html) |
41
|
|
|
* @version Release: 2.6.0 |
42
|
|
|
* @link http://xoops.org |
43
|
|
|
* @since 2.6.0 |
44
|
|
|
*/ |
45
|
|
|
class Logger implements LoggerInterface |
46
|
|
|
{ |
47
|
|
|
/** |
48
|
|
|
* @var LoggerInterface[] chain of PSR-3 compatible loggers to call |
49
|
|
|
*/ |
50
|
|
|
private $loggers = array(); |
51
|
|
|
|
52
|
|
|
/** |
53
|
|
|
* @var boolean do we have active loggers? |
54
|
|
|
*/ |
55
|
|
|
private $logging_active = false; |
56
|
|
|
|
57
|
|
|
/** |
58
|
|
|
* @var boolean just to prevent fatal legacy errors. Does nothing. Stop it! |
59
|
|
|
*/ |
60
|
|
|
//public $activated = false; |
61
|
|
|
|
62
|
|
|
/** |
63
|
|
|
* Get the Xoops\Core\Logger instance |
64
|
|
|
* |
65
|
|
|
* @return Logger object |
66
|
|
|
*/ |
67
|
21 |
|
public static function getInstance() |
68
|
|
|
{ |
69
|
21 |
|
static $instance; |
70
|
21 |
|
if (!isset($instance)) { |
71
|
|
|
$class = __CLASS__; |
72
|
|
|
$instance = new $class(); |
73
|
|
|
// Always catch errors, for security reasons |
74
|
|
|
set_error_handler(array($instance, 'handleError')); |
75
|
|
|
// grab any uncaught exception |
76
|
|
|
set_exception_handler(array($instance, 'handleException')); |
77
|
|
|
} |
78
|
|
|
|
79
|
21 |
|
return $instance; |
80
|
|
|
} |
81
|
|
|
|
82
|
|
|
/** |
83
|
|
|
* Error handling callback. |
84
|
|
|
* |
85
|
|
|
* This will |
86
|
|
|
* |
87
|
|
|
* @param integer $errorNumber error number |
88
|
|
|
* @param string $errorString error message |
89
|
|
|
* @param string $errorFile file |
90
|
|
|
* @param integer $errorLine line number |
91
|
|
|
* |
92
|
|
|
* @return void |
93
|
|
|
*/ |
94
|
91 |
|
public function handleError($errorNumber, $errorString, $errorFile, $errorLine) |
95
|
|
|
{ |
96
|
91 |
|
if ($this->logging_active && ($errorNumber & error_reporting())) { |
97
|
|
|
|
98
|
|
|
// if an error occurs before a locale is established, |
99
|
|
|
// we still need messages, so check and deal with it |
100
|
|
|
|
101
|
5 |
|
$msg = ': ' . sprintf( |
102
|
5 |
|
(class_exists('\XoopsLocale', false) ? \XoopsLocale::EF_LOGGER_FILELINE : "%s in file %s line %s"), |
103
|
5 |
|
$this->sanitizePath($errorString), |
104
|
5 |
|
$this->sanitizePath($errorFile), |
105
|
5 |
|
$errorLine |
106
|
|
|
); |
107
|
|
|
|
108
|
|
|
switch ($errorNumber) { |
109
|
5 |
|
case E_USER_NOTICE: |
110
|
1 |
|
$msg = (class_exists('\XoopsLocale', false) ? \XoopsLocale::E_LOGGER_ERROR : '*Error:') . $msg; |
111
|
1 |
|
$this->log(LogLevel::NOTICE, $msg); |
112
|
1 |
|
break; |
113
|
4 |
|
case E_NOTICE: |
114
|
1 |
|
$msg = (class_exists('\XoopsLocale', false) ? \XoopsLocale::E_LOGGER_NOTICE : '*Notice:') . $msg; |
115
|
1 |
|
$this->log(LogLevel::NOTICE, $msg); |
116
|
1 |
|
break; |
117
|
3 |
|
case E_WARNING: |
118
|
1 |
|
$msg = (class_exists('\XoopsLocale', false) ? \XoopsLocale::E_LOGGER_WARNING : '*Warning:') . $msg; |
119
|
1 |
|
$this->log(LogLevel::WARNING, $msg); |
120
|
1 |
|
break; |
121
|
2 |
|
case E_STRICT: |
122
|
1 |
|
$msg = (class_exists('\XoopsLocale', false) ? \XoopsLocale::E_LOGGER_STRICT : '*Strict:') . $msg; |
123
|
1 |
|
$this->log(LogLevel::WARNING, $msg); |
124
|
1 |
|
break; |
125
|
1 |
|
case E_USER_ERROR: |
126
|
|
|
$msg = (class_exists('\XoopsLocale', false) ? \XoopsLocale::E_LOGGER_ERROR : '*Error:') . $msg; |
127
|
|
|
@$this->log(LogLevel::CRITICAL, $msg); |
|
|
|
|
128
|
|
|
break; |
129
|
|
|
default: |
130
|
1 |
|
$msg = (class_exists('\XoopsLocale', false) ? \XoopsLocale::E_LOGGER_UNKNOWN : '*Unknown:') . $msg; |
131
|
1 |
|
$this->log(LogLevel::ERROR, $msg); |
132
|
1 |
|
break; |
133
|
|
|
} |
134
|
|
|
} |
135
|
|
|
|
136
|
91 |
|
if ($errorNumber == E_USER_ERROR) { |
137
|
|
|
$trace = true; |
138
|
|
|
if (substr($errorString, 0, '8') === 'notrace:') { |
139
|
|
|
$trace = false; |
140
|
|
|
$errorString = substr($errorString, 8); |
141
|
|
|
} |
142
|
|
|
$this->reportFatalError($errorString); |
143
|
|
|
if ($trace) { |
144
|
|
|
$trace = debug_backtrace(); |
145
|
|
|
array_shift($trace); |
146
|
|
|
if ('cli' === php_sapi_name()) { |
147
|
|
|
foreach ($trace as $step) { |
148
|
|
|
if (isset($step['file'])) { |
149
|
|
|
fprintf(STDERR, "%s (%d)\n", $this->sanitizePath($step['file']), $step['line']); |
150
|
|
|
} |
151
|
|
|
} |
152
|
|
|
} else { |
153
|
|
|
echo "<div style='color:#f0f0f0;background-color:#f0f0f0'>" . _XOOPS_FATAL_BACKTRACE . ":<br />"; |
154
|
|
|
foreach ($trace as $step) { |
155
|
|
|
if (isset($step['file'])) { |
156
|
|
|
printf("%s (%d)\n<br />", $this->sanitizePath($step['file']), $step['line']); |
157
|
|
|
} |
158
|
|
|
} |
159
|
|
|
echo '</div>'; |
160
|
|
|
} |
161
|
|
|
} |
162
|
|
|
exit(); |
|
|
|
|
163
|
|
|
} |
164
|
91 |
|
} |
165
|
|
|
|
166
|
|
|
/** |
167
|
|
|
* Exception handling callback. |
168
|
|
|
* |
169
|
|
|
* @param \Exception|\Throwable $e uncaught Exception or Error |
170
|
|
|
* |
171
|
|
|
* @return void |
172
|
|
|
*/ |
173
|
|
|
public function handleException($e) |
174
|
|
|
{ |
175
|
|
|
if ($this->isThrowable($e)) { |
176
|
|
|
$msg = $e->getMessage(); |
177
|
|
|
$this->reportFatalError($msg); |
178
|
|
|
} |
179
|
|
|
} |
180
|
|
|
|
181
|
|
|
/** |
182
|
|
|
* Determine if an object implements Throwable (or is an Exception that would under PHP 7.) |
183
|
|
|
* |
184
|
|
|
* @param mixed $e Expected to be an object related to Exception or Throwable |
185
|
|
|
* |
186
|
|
|
* @return bool true if related to Throwable or Exception, otherwise false |
187
|
|
|
*/ |
188
|
|
|
protected function isThrowable($e) |
189
|
|
|
{ |
190
|
|
|
$type = interface_exists('\Throwable', false) ? '\Throwable' : '\Exception'; |
191
|
|
|
return $e instanceof $type; |
192
|
|
|
} |
193
|
|
|
|
194
|
|
|
/** |
195
|
|
|
* Announce fatal error, attempt to log |
196
|
|
|
* |
197
|
|
|
* @param string $msg error message to report |
198
|
|
|
* |
199
|
|
|
* @return void |
200
|
|
|
*/ |
201
|
|
|
private function reportFatalError($msg) |
202
|
|
|
{ |
203
|
|
|
$msg=$this->sanitizePath($msg); |
204
|
|
|
if ('cli' === php_sapi_name()) { |
205
|
|
|
fprintf(STDERR, "\nError : %s\n", $msg); |
206
|
|
|
} else { |
207
|
|
|
if (defined('_XOOPS_FATAL_MESSAGE')) { |
208
|
|
|
printf(_XOOPS_FATAL_MESSAGE, XOOPS_URL, $msg); |
209
|
|
|
} else { |
210
|
|
|
printf("\nError : %s\n", $msg); |
211
|
|
|
} |
212
|
|
|
} |
213
|
|
|
@$this->log(LogLevel::CRITICAL, $msg); |
|
|
|
|
214
|
|
|
} |
215
|
|
|
|
216
|
|
|
/** |
217
|
|
|
* clean a path to remove sensitive details |
218
|
|
|
* |
219
|
|
|
* @param string $message text to sanitize |
220
|
|
|
* |
221
|
|
|
* @return string sanitized message |
222
|
|
|
*/ |
223
|
23 |
|
public function sanitizePath($message) |
224
|
|
|
{ |
225
|
|
|
$cleaners = [ |
226
|
23 |
|
['\\', '/',], |
227
|
23 |
|
[\XoopsBaseConfig::get('var-path'), 'VAR',], |
228
|
23 |
|
[str_replace('\\', '/', realpath(\XoopsBaseConfig::get('var-path'))), 'VAR',], |
229
|
23 |
|
[\XoopsBaseConfig::get('lib-path'), 'LIB',], |
230
|
23 |
|
[str_replace('\\', '/', realpath(\XoopsBaseConfig::get('lib-path'))), 'LIB',], |
231
|
23 |
|
[\XoopsBaseConfig::get('root-path'), 'ROOT',], |
232
|
23 |
|
[str_replace('\\', '/', realpath(\XoopsBaseConfig::get('root-path'))), 'ROOT',], |
233
|
23 |
|
[\XoopsBaseConfig::get('db-name') . '.', '',], |
234
|
23 |
|
[\XoopsBaseConfig::get('db-name'), '',], |
235
|
23 |
|
[\XoopsBaseConfig::get('db-prefix') . '_', '',], |
236
|
23 |
|
[\XoopsBaseConfig::get('db-user'), '***',], |
237
|
23 |
|
[\XoopsBaseConfig::get('db-pass'), '***',], |
238
|
|
|
]; |
239
|
23 |
|
$stringsToClean = array_column($cleaners, 0); |
240
|
23 |
|
$replacementStings = array_column($cleaners, 1); |
241
|
|
|
|
242
|
23 |
|
$message = str_replace($stringsToClean, $replacementStings, $message); |
243
|
|
|
|
244
|
23 |
|
return $message; |
245
|
|
|
} |
246
|
|
|
|
247
|
|
|
/** |
248
|
|
|
* add a PSR-3 compatible logger to the chain |
249
|
|
|
* |
250
|
|
|
* @param object $logger a PSR-3 compatible logger object |
251
|
|
|
* |
252
|
|
|
* @return void |
253
|
|
|
*/ |
254
|
17 |
|
public function addLogger($logger) |
255
|
|
|
{ |
256
|
17 |
|
if (is_object($logger) && method_exists($logger, 'log')) { |
257
|
17 |
|
$this->loggers[] = $logger; |
258
|
17 |
|
$this->logging_active = true; |
259
|
|
|
} |
260
|
17 |
|
} |
261
|
|
|
|
262
|
|
|
/** |
263
|
|
|
* System is unusable. |
264
|
|
|
* |
265
|
|
|
* @param string $message message |
266
|
|
|
* @param array $context array of context data for this log entry |
267
|
|
|
* |
268
|
|
|
* @return void |
269
|
|
|
*/ |
270
|
1 |
|
public function emergency($message, array $context = array()) |
271
|
|
|
{ |
272
|
1 |
|
$this->log(LogLevel::EMERGENCY, $message, $context); |
273
|
1 |
|
} |
274
|
|
|
|
275
|
|
|
/** |
276
|
|
|
* Action must be taken immediately. |
277
|
|
|
* |
278
|
|
|
* Example: Entire website down, database unavailable, etc. This should |
279
|
|
|
* trigger the SMS alerts and wake you up. |
280
|
|
|
* |
281
|
|
|
* @param string $message message |
282
|
|
|
* @param array $context array of context data for this log entry |
283
|
|
|
* |
284
|
|
|
* @return void |
285
|
|
|
*/ |
286
|
1 |
|
public function alert($message, array $context = array()) |
287
|
|
|
{ |
288
|
1 |
|
$this->log(LogLevel::ALERT, $message, $context); |
289
|
1 |
|
} |
290
|
|
|
|
291
|
|
|
/** |
292
|
|
|
* Critical conditions. |
293
|
|
|
* |
294
|
|
|
* Example: Application component unavailable, unexpected exception. |
295
|
|
|
* |
296
|
|
|
* @param string $message message |
297
|
|
|
* @param array $context array of context data for this log entry |
298
|
|
|
* |
299
|
|
|
* @return void |
300
|
|
|
*/ |
301
|
1 |
|
public function critical($message, array $context = array()) |
302
|
|
|
{ |
303
|
1 |
|
$this->log(LogLevel::CRITICAL, $message, $context); |
304
|
1 |
|
} |
305
|
|
|
|
306
|
|
|
/** |
307
|
|
|
* Runtime errors that do not require immediate action but should typically |
308
|
|
|
* be logged and monitored. |
309
|
|
|
* |
310
|
|
|
* @param string $message message |
311
|
|
|
* @param array $context array of context data for this log entry |
312
|
|
|
* |
313
|
|
|
* @return void |
314
|
|
|
*/ |
315
|
1 |
|
public function error($message, array $context = array()) |
316
|
|
|
{ |
317
|
1 |
|
$this->log(LogLevel::ERROR, $message, $context); |
318
|
1 |
|
} |
319
|
|
|
|
320
|
|
|
/** |
321
|
|
|
* Exceptional occurrences that are not errors. |
322
|
|
|
* |
323
|
|
|
* Example: Use of deprecated APIs, poor use of an API, undesirable things |
324
|
|
|
* that are not necessarily wrong. |
325
|
|
|
* |
326
|
|
|
* @param string $message message |
327
|
|
|
* @param array $context array of context data for this log entry |
328
|
|
|
* |
329
|
|
|
* @return void |
330
|
|
|
*/ |
331
|
1 |
|
public function warning($message, array $context = array()) |
332
|
|
|
{ |
333
|
1 |
|
$this->log(LogLevel::WARNING, $message, $context); |
334
|
1 |
|
} |
335
|
|
|
|
336
|
|
|
/** |
337
|
|
|
* Normal but significant events. |
338
|
|
|
* |
339
|
|
|
* @param string $message message |
340
|
|
|
* @param array $context array of context data for this log entry |
341
|
|
|
* |
342
|
|
|
* @return void |
343
|
|
|
*/ |
344
|
1 |
|
public function notice($message, array $context = array()) |
345
|
|
|
{ |
346
|
1 |
|
$this->log(LogLevel::NOTICE, $message, $context); |
347
|
1 |
|
} |
348
|
|
|
|
349
|
|
|
/** |
350
|
|
|
* Interesting events. |
351
|
|
|
* |
352
|
|
|
* Example: User logs in, SQL logs. |
353
|
|
|
* |
354
|
|
|
* @param string $message message |
355
|
|
|
* @param array $context array of context data for this log entry |
356
|
|
|
* |
357
|
|
|
* @return void |
358
|
|
|
*/ |
359
|
1 |
|
public function info($message, array $context = array()) |
360
|
|
|
{ |
361
|
1 |
|
$this->log(LogLevel::INFO, $message, $context); |
362
|
1 |
|
} |
363
|
|
|
|
364
|
|
|
/** |
365
|
|
|
* Detailed debug information. |
366
|
|
|
* |
367
|
|
|
* @param string $message message |
368
|
|
|
* @param array $context array of context data for this log entry |
369
|
|
|
* |
370
|
|
|
* @return void |
371
|
|
|
*/ |
372
|
1 |
|
public function debug($message, array $context = array()) |
373
|
|
|
{ |
374
|
1 |
|
$this->log(LogLevel::DEBUG, $message, $context); |
375
|
1 |
|
} |
376
|
|
|
|
377
|
|
|
/** |
378
|
|
|
* Logs with an arbitrary level. |
379
|
|
|
* |
380
|
|
|
* @param mixed $level PSR-3 LogLevel constant |
381
|
|
|
* @param string $message message |
382
|
|
|
* @param array $context array of context data for this log entry |
383
|
|
|
* |
384
|
|
|
* @return void |
385
|
|
|
*/ |
386
|
14 |
|
public function log($level, $message, array $context = array()) |
387
|
|
|
{ |
388
|
14 |
|
if (!empty($this->loggers)) { |
389
|
13 |
|
foreach ($this->loggers as $logger) { |
390
|
13 |
|
if (is_object($logger)) { |
391
|
|
|
try { |
392
|
13 |
|
$logger->log($level, $message, $context); |
393
|
13 |
|
} catch (\Exception $e) { |
394
|
|
|
// just ignore, as we can't do anything, not even log it. |
395
|
|
|
} |
396
|
|
|
} |
397
|
|
|
} |
398
|
|
|
} |
399
|
14 |
|
} |
400
|
|
|
|
401
|
|
|
/** |
402
|
|
|
* quiet - turn off output if output is rendered in XOOPS page output. |
403
|
|
|
* This is intended to assist ajax code that may fail with any extra |
404
|
|
|
* content the logger may introduce. |
405
|
|
|
* |
406
|
|
|
* It should have no effect on loggers using other methods, such a write |
407
|
|
|
* to file. |
408
|
|
|
* |
409
|
|
|
* @return void |
410
|
|
|
*/ |
411
|
2 |
|
public function quiet() |
412
|
|
|
{ |
413
|
2 |
|
if (!empty($this->loggers)) { |
414
|
2 |
|
foreach ($this->loggers as $logger) { |
415
|
2 |
|
if (is_object($logger) && method_exists($logger, 'quiet')) { |
416
|
|
|
try { |
417
|
2 |
|
$logger->quiet(); |
418
|
2 |
|
} catch (\Exception $e) { |
419
|
|
|
// just ignore, as we can't do anything, not even log it. |
420
|
|
|
} |
421
|
|
|
} |
422
|
|
|
} |
423
|
|
|
} |
424
|
2 |
|
} |
425
|
|
|
|
426
|
|
|
// Deprecated uses |
427
|
|
|
|
428
|
|
|
/** |
429
|
|
|
* Keep deprecated calls from failing |
430
|
|
|
* |
431
|
|
|
* @param string $var property |
432
|
|
|
* @param string $val value |
433
|
|
|
* |
434
|
|
|
* @return void |
435
|
|
|
* |
436
|
|
|
* @deprecated |
437
|
|
|
*/ |
438
|
1 |
|
public function __set($var, $val) |
439
|
|
|
{ |
440
|
1 |
|
$this->deprecatedMessage(); |
441
|
|
|
// legacy compatibility: turn off logger display for $xoopsLogger->activated = false; usage |
442
|
1 |
|
if ($var==='activated' && !$val) { |
443
|
1 |
|
$this->quiet(); |
444
|
|
|
} |
445
|
|
|
|
446
|
1 |
|
} |
447
|
|
|
|
448
|
|
|
/** |
449
|
|
|
* Keep deprecated calls from failing |
450
|
|
|
* |
451
|
|
|
* @param string $var property |
452
|
|
|
* |
453
|
|
|
* @return void |
454
|
|
|
* |
455
|
|
|
* @deprecated |
456
|
|
|
*/ |
457
|
|
|
public function __get($var) |
458
|
|
|
{ |
459
|
|
|
$this->deprecatedMessage(); |
460
|
|
|
} |
461
|
|
|
|
462
|
|
|
/** |
463
|
|
|
* Keep deprecated calls from failing |
464
|
|
|
* |
465
|
|
|
* @param string $method method |
466
|
|
|
* @param string $args arguments |
467
|
|
|
* |
468
|
|
|
* @return void |
469
|
|
|
* |
470
|
|
|
* @deprecated |
471
|
|
|
*/ |
472
|
|
|
public function __call($method, $args) |
473
|
|
|
{ |
474
|
|
|
$this->deprecatedMessage(); |
475
|
|
|
} |
476
|
|
|
|
477
|
|
|
/** |
478
|
|
|
* issue a deprecated warning |
479
|
|
|
* |
480
|
|
|
* @return void |
481
|
|
|
*/ |
482
|
1 |
|
private function deprecatedMessage() |
483
|
|
|
{ |
484
|
1 |
|
$xoops = \Xoops::getInstance(); |
485
|
1 |
|
$xoops->deprecated('This use of XoopsLogger is deprecated since 2.6.0.'); |
486
|
1 |
|
} |
487
|
|
|
} |
488
|
|
|
|
This check looks for function or method calls that always return null and whose return value is used.
The method
getObject()
can return nothing but null, so it makes no sense to use the return value.The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.