PluginProbe
DecaLog / 4.4.0
DecaLog v4.4.0
3.0.2 3.1.0 3.10.0 3.2.0 3.3.0 3.4.0 3.4.1 3.5.0 3.5.1 3.6.0 3.6.1 3.6.2 3.6.3 3.7.0 3.7.1 3.8.0 3.9.0 3.9.1 4.0.0 4.1.0 4.2.0 4.3.0 4.3.1 4.4.0 4.5.0 All 75 releases
decalog / includes / features / class-dlogger.php

class-dlogger.php in DecaLog 4.4.0, at includes/features/class-dlogger.php

475 lines 12.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * DecaLog logger definition.
4 *
5 * @package Features
6 * @author Pierre Lannoy <https://pierre.lannoy.fr/>.
7 * @since 1.0.0
8 */
9
10 namespace Decalog\Plugin\Feature;
11
12 use Decalog\Plugin\Feature\HandlerTypes;
13 use DLMonolog\Logger;
14 use Decalog\System\Environment;
15 use Decalog\System\Option;
16 use Decalog\System\Timezone;
17 use Decalog\Plugin\Feature\LoggerFactory;
18 use Decalog\Plugin\Feature\ClassTypes;
19 use Decalog\Plugin\Feature\ChannelTypes;
20 use Decalog\Plugin\Feature\HandlerDiagnosis;
21 use Decalog\System\UUID;
22 use Decalog\Plugin\Feature\DMonitor;
23
24 /**
25 * Main DecaLog logger class.
26 *
27 * This class defines all code necessary to log events with DecaLog.
28 *
29 * @package Features
30 * @author Pierre Lannoy <https://pierre.lannoy.fr/>.
31 * @since 1.0.0
32 */
33 class DLogger {
34
35 /**
36 * The class of the component.
37 *
38 * @since 1.0.0
39 * @var string $class Maintains the class of the component.
40 */
41 protected $class = 'unknwon';
42
43 /**
44 * The name of the component.
45 *
46 * @since 1.0.0
47 * @var string $class Maintains the name of the component.
48 */
49 protected $name = 'unknown';
50
51 /**
52 * The version of the component.
53 *
54 * @since 1.0.0
55 * @var string $version Maintains the version of the component.
56 */
57 protected $version = '-';
58
59 /**
60 * The monolog logger.
61 *
62 * @since 1.0.0
63 * @var object $logger Maintains the logger.
64 */
65 protected $logger = null;
66
67 /**
68 * Is the logger in test.
69 *
70 * @since 1.2.1
71 * @var boolean $in_test Maintains the test status of the logger.
72 */
73 protected $in_test = false;
74
75 /**
76 * Is this listener a PSR-3 logger.
77 *
78 * @since 1.3.0
79 * @var boolean $psr3 Maintains the psr3 status of the logger.
80 */
81 private $psr3 = false;
82
83 /**
84 * Is logger allowed to run.
85 *
86 * @since 1.3.0
87 * @var boolean $allowed Maintains the allowed status of the logger.
88 */
89 private $allowed = true;
90
91 /**
92 * Messages counter.
93 *
94 * @since 1.3.0
95 * @var array $counter Maintains the messages counter.
96 */
97 private static $counter = [];
98
99 /**
100 * Initialize the class and set its properties.
101 *
102 * @param string $class The class identifier, must be in self::$classes.
103 * @param string $name Optional. The name of the component.
104 * @param string $version Optional. The version of the component.
105 * @param string $test Optional. The handler to create if specified.
106 * @param boolean $psr3 Optional. True if this logger is a PSR-3 logger.
107 * @since 1.0.0
108 */
109 public function __construct( $class, $name = null, $version = null, $test = null, $psr3 = false ) {
110 if ( ! defined( 'DECALOG_TRACEID' ) ) {
111 define( 'DECALOG_TRACEID', UUID::generate_unique_id( 32 ) );
112 }
113 if ( in_array( $class, ClassTypes::$classes, true ) ) {
114 $this->class = $class;
115 }
116 if ( $name && is_string( $name ) ) {
117 $this->name = $name;
118 }
119 if ( $version && is_string( $version ) ) {
120 $this->version = $version;
121 }
122 $this->psr3 = $psr3;
123 $this->init( $test );
124 $this->debug( 'A new instance of DecaLog logger is initialized and operational.' );
125 }
126
127 /**
128 * Init the logger.
129 *
130 * @param string $test Optional. The handler to init if specified.
131 * @since 1.0.0
132 */
133 private function init( $test = null ) {
134 if ( $this->psr3 ) {
135 if ( ! Option::network_get( 'autolisteners' ) ) {
136 $this->allowed = in_array( 'psr3', Option::network_get( 'listeners' ), true );
137 }
138 }
139 $this->in_test = isset( $test );
140 $factory = new LoggerFactory();
141 $this->logger = new Logger( $this->current_channel_tag(), [], [], Timezone::network_get() );
142 $handlers = new HandlerTypes();
143 $diagnosis = new HandlerDiagnosis();
144 $unloadable = [];
145 $skipped = [];
146 foreach ( $this->loggers_check() as $key => $logger ) {
147 if ( $this->in_test && $key !== $test ) {
148 continue;
149 }
150 if ( '' === $key ) {
151 continue;
152 }
153 if ( ! is_array( $logger ) || ( is_array( $logger ) && !array_key_exists('handler', $logger) ) ) {
154 continue;
155 }
156 $handler_def = $handlers->get( $logger['handler'] );
157 $logger['uuid'] = $key;
158 if ( $handler_def && $diagnosis->check( $handler_def['id'] ) ) {
159 $handler = $factory->create_logger( $logger );
160 if ( $handler instanceof \DLMonolog\Handler\HandlerInterface ) {
161 $this->logger->pushHandler( $handler );
162 } elseif ( $logger['running'] ) {
163 $skipped[] = sprintf( 'Skipping loading of a %s logger.', $handler_def['name'] );
164 }
165 } else {
166 if ( $handler_def ) {
167 $unloadable[] = sprintf( 'Unable to load a %s logger. %s', $handler_def['name'], $diagnosis->error_string( $handler_def['id'] ) );
168 } else {
169 $unloadable[] = 'Unable to load a logger.';
170 }
171 }
172 }
173 if ( count( $unloadable ) > 0 ) {
174 foreach ( $unloadable as $item ) {
175 $this->error( $item, 666 );
176 }
177 }
178 if ( count( $skipped ) > 0 ) {
179 foreach ( $skipped as $item ) {
180 $this->debug( $item );
181 }
182 }
183 }
184
185 /**
186 * Check the loggers.
187 *
188 * @return array The logger list.
189 * @since 2.0.0
190 */
191 private function loggers_check() {
192 $loggers = Option::network_get( 'loggers', [] );
193 $persist_logger_update = false;
194 // Verify data structure and fix if required
195 if ( ! is_array( $loggers ) ) {
196 $persist_logger_update = true;
197 $loggers = [];
198 }
199 // Verify shared memory logger
200 if ( ! array_key_exists( DECALOG_SHM_ID, $loggers ) ) {
201 $persist_logger_update = true;
202 $shm = [];
203 $shm['name'] =decalog__( 'System events-logger', 'decalog' );
204 $shm['handler'] = 'SharedMemoryHandler';
205 $shm['running'] = Option::network_get( 'livelog' );
206 $shm['level'] = Logger::INFO;
207 $shm['privacy'] = [
208 'obfuscation' => 0,
209 'pseudonymization' => 0,
210 ];
211 $shm['processors'] = [ 'WordpressProcessor', 'IntrospectionProcessor', 'WWWProcessor' ];
212 $loggers[ DECALOG_SHM_ID ] = $shm;
213 }
214 // Setup loggers from wp-config
215 if ( defined( 'DECALOG_DEFAULT_LOGGERS' ) && is_array( DECALOG_DEFAULT_LOGGERS ) ) {
216 foreach ( DECALOG_DEFAULT_LOGGERS as $default_logger_id => $default_logger ) {
217 if ( ! array_key_exists( $default_logger_id, $loggers ) ) {
218 $persist_logger_update = true;
219 $loggers[ $default_logger_id ] = $default_logger;
220 }
221 }
222 }
223 if ( $persist_logger_update ) {
224 Option::network_set( 'loggers', $loggers );
225 }
226
227 return $loggers;
228 }
229
230 /**
231 * Get the current channel tag.
232 *
233 * @return string The current channel tag.
234 * @since 1.0.0
235 */
236 private function current_channel_tag() {
237 return $this->channel_tag( Environment::exec_mode() );
238 }
239
240 /**
241 * Get the channel tag.
242 *
243 * @param integer $id Optional. The channel id (execution mode).
244 * @return string The channel tag.
245 * @since 1.0.0
246 */
247 public function channel_tag( $id = 0 ) {
248 if ( $id >= count( ChannelTypes::$channels ) ) {
249 $id = 0;
250 }
251 return ChannelTypes::$channels[ $id ];
252 }
253
254 /**
255 * Verify if DEBUG is allowed.
256 *
257 * @return boolean True if DEBUG message are allowed, false otherwise.
258 * @since 1.0.0
259 */
260 private function is_debug_allowed() {
261 if ( ! Option::network_get( 'respect_wp_debug' ) ) {
262 return true;
263 }
264 if ( defined( 'WP_DEBUG' ) ) {
265 return WP_DEBUG;
266 }
267 return true;
268 }
269
270 /**
271 * Normalize a string.
272 *
273 * @param string $string The string.
274 * @return string The normalized string.
275 * @since 1.10.0+
276 */
277 private function normalize_string( $string ) {
278 $string = str_replace( '"', '“', $string );
279 $string = str_replace( '\'', '`', $string );
280 $string = str_replace( '>=', '≥', $string );
281 $string = str_replace( '<=', '≤', $string );
282 return decalog_filter_string( $string );
283 }
284
285 /**
286 * Normalize an array.
287 *
288 * @param mixed $array The array.
289 * @return mixed The normalized array.
290 * @since 1.10.0+
291 */
292 private function normalize_array( $array ) {
293 array_walk_recursive(
294 $array,
295 function ( &$item, $key ) {
296 if ( is_string( $item ) ) {
297 $item = $this->normalize_string( $item );
298 } }
299 );
300 return $array;
301 }
302
303 /**
304 * Normalize an array.
305 *
306 * @param mixed $level The log level.
307 * @return integer The counter for this level.
308 * @since 1.10.0+
309 */
310 public static function count( $level ) {
311 if ( is_string( $level ) ) {
312 $level = EventTypes::$levels[ strtolower( $level ) ];
313 }
314 if ( ! array_key_exists( $level, self::$counter ) ) {
315 return 0;
316 }
317 return self::$counter[ $level ];
318 }
319
320 /**
321 * Adds a log record at a specific level.
322 *
323 * @param mixed $level The log level.
324 * @param string $message The log message.
325 * @param integer $code Optional. The log code.
326 * @param string $phase Optional. The log phase.
327 * @param boolean $signal Optional. Add .
328 * @return boolean True if message was logged, false otherwise.
329 * @since 1.0.0
330 */
331 public function log( $level, $message, $code = 0, $phase = '', $signal = true ) {
332
333 if ( is_string( $level ) ) {
334 $level = EventTypes::$levels[ strtolower( $level ) ];
335 }
336 if ( ! array_key_exists( $level, self::$counter ) ) {
337 self::$counter[ $level ] = 0;
338 }
339 self::$counter[ $level ]++;
340 if ( ! $this->allowed ) {
341 return false;
342 }
343 $debug = ( Logger::DEBUG === $level || 100 === $level || 'DEBUG' === strtoupper( (string) $level ) );
344 if ( $debug && ! $this->is_debug_allowed() ) {
345 return false;
346 }
347 try {
348 $context = [
349 'class' => (string) $this->class,
350 'component' => (string) $this->name,
351 'version' => (string) $this->version,
352 'phase' => (string) $phase,
353 'code' => (int) $code,
354 'environment' => (string) Environment::stage(),
355 'traceID' => (string) DECALOG_TRACEID,
356 'instance' => (string) DECALOG_INSTANCE_NAME,
357 ];
358 $channel = $this->current_channel_tag();
359 if ( $this->logger->getName() !== $channel ) {
360 $this->logger = $this->logger->withName( $channel );
361 }
362 $result = true;
363 // phpcs:ignore
364 set_error_handler( function () use (&$result) {$result = false;} );
365 $this->logger->log( $level, $this->normalize_string( $message ), $this->normalize_array( $context ) );
366 // phpcs:ignore
367 restore_error_handler();
368 } catch ( \Throwable $t ) {
369 $result = false;
370 } finally {
371 if ( $signal && ! $result && ! $debug ) {
372 //$this->log( Logger::ALERT, 'error', 666, '', false );
373 }
374 return $result;
375 }
376 }
377
378 /**
379 * Adds a log record at the DEBUG level.
380 *
381 * @param string $message The log message.
382 * @param integer $code Optional. The log code.
383 * @return boolean True if message was logged, false otherwise.
384 * @since 1.0.0
385 */
386 public function debug( $message, $code = 0 ) {
387 return $this->log( Logger::DEBUG, $message, $code );
388 }
389
390 /**
391 * Adds a log record at the INFO level.
392 *
393 * @param string $message The log message.
394 * @param integer $code Optional. The log code.
395 * @return boolean True if message was logged, false otherwise.
396 * @since 1.0.0
397 */
398 public function info( $message, $code = 0 ) {
399 return $this->log( Logger::INFO, $message, $code );
400 }
401
402 /**
403 * Adds a log record at the NOTICE level.
404 *
405 * @param string $message The log message.
406 * @param integer $code Optional. The log code.
407 * @return boolean True if message was logged, false otherwise.
408 * @since 1.0.0
409 */
410 public function notice( $message, $code = 0 ) {
411 return $this->log( Logger::NOTICE, $message, $code );
412 }
413
414 /**
415 * Adds a log record at the WARNING level.
416 *
417 * @param string $message The log message.
418 * @param integer $code Optional. The log code.
419 * @return boolean True if message was logged, false otherwise.
420 * @since 1.0.0
421 */
422 public function warning( $message, $code = 0 ) {
423 return $this->log( Logger::WARNING, $message, $code );
424 }
425
426 /**
427 * Adds a log record at the ERROR level.
428 *
429 * @param string $message The log message.
430 * @param integer $code Optional. The log code.
431 * @return boolean True if message was logged, false otherwise.
432 * @since 1.0.0
433 */
434 public function error( $message, $code = 0 ) {
435 return $this->log( Logger::ERROR, $message, $code );
436 }
437
438 /**
439 * Adds a log record at the CRITICAL level.
440 *
441 * @param string $message The log message.
442 * @param integer $code Optional. The log code.
443 * @return boolean True if message was logged, false otherwise.
444 * @since 1.0.0
445 */
446 public function critical( $message, $code = 0 ) {
447 return $this->log( Logger::CRITICAL, $message, $code );
448 }
449
450 /**
451 * Adds a log record at the ALERT level.
452 *
453 * @param string $message The log message.
454 * @param integer $code Optional. The log code.
455 * @return boolean True if message was logged, false otherwise.
456 * @since 1.0.0
457 */
458 public function alert( $message, $code = 0 ) {
459 return $this->log( Logger::ALERT, $message, $code );
460 }
461
462 /**
463 * Adds a log record at the EMERGENCY level.
464 *
465 * @param string $message The log message.
466 * @param integer $code Optional. The log code.
467 * @return boolean True if message was logged, false otherwise.
468 * @since 1.0.0
469 */
470 public function emergency( $message, $code = 0 ) {
471 return $this->log( Logger::EMERGENCY, $message, $code );
472 }
473
474 }
475