PluginProbe
MainWP Dashboard: Self-hosted WordPress Management for Agencies / 6.1.4
MainWP Dashboard: Self-hosted WordPress Management for Agencies v6.1.4
6.2 6.1.8 6.1.7 6.1.6 6.1.5 6.1.4 6.1.3 6.1.2 6.1.1 6.1 6.0.12 6.0.11 4.6.0.1 5.0 5.0.1 5.0.2 5.0.3 5.0.3.1 5.0.3.2 5.1 5.1.1 5.2 5.2.1 5.2.2 5.3 All 153 releases
mainwp / class / class-mainwp-logger.php

class-mainwp-logger.php in MainWP Dashboard: Self-hosted WordPress Management for Agencies 6.1.4, at class/class-mainwp-logger.php

1,225 lines 41.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * MainWP Logger
4 *
5 * For custom read/write logging file.
6 *
7 * @package MainWP/Dashboard
8 */
9
10 namespace MainWP\Dashboard;
11
12 // Exit if accessed directly.
13 if ( ! defined( 'ABSPATH' ) ) {
14 exit;
15 }
16
17 /**
18 * Class MainWP_Logger
19 *
20 * @package MainWP\Dashboard
21 */
22 class MainWP_Logger { // phpcs:ignore Generic.Classes.OpeningBraceSameLine.ContentAfterBrace -- NOSONAR.
23
24 // phpcs:disable WordPress.WP.AlternativeFunctions -- for custom read/write logging file.
25
26 const UPDATE_CHECK_LOG_PRIORITY = 10;
27 const EXECUTION_TIME_LOG_PRIORITY = 15;
28 const LOGS_AUTO_PURGE_LOG_PRIORITY = 16;
29 const LOGS_REGULAR_SCHEDULE = 18;
30 const DEBUG_UPDATES_SCHEDULE = 19;
31 const COST_TRACKER_LOG_PRIORITY = 20230112;
32 const API_BACKUPS_LOG_PRIORITY = 20240130;
33 const CONNECT_LOG_PRIORITY = 20241001;
34 const UPTIME_CHECK_LOG_PRIORITY = 20241017;
35 const UPTIME_NOTICE_LOG_PRIORITY = 20241106;
36 const SITES_CHANGES_LOG_PRIORITY = 20250417;
37 const CACHE_METRICS_LOG_PRIORITY = 20250814;
38 const DB_QUERIES_LOG_PRIORITY = 20250827;
39 const UNHOOKS_LOG_PRIORITY = 20250901;
40 const WARM_CACHE_LOG_PRIORITY = 20250915;
41 const EXTENSION_UPDATES_CHECK_LOG_PRIORITY = 20260306;
42 const EXECUTION_SYNC_LOG_PRIORITY = 20260316;
43 const EXECUTION_SYNC_DETAILS_LOG_PRIORITY = 20260323;
44
45 const DISABLED = - 1;
46 const LOG = 0;
47 const WARNING = 1;
48 const INFO = 2;
49 const DEBUG = 3;
50 const NOTICE = 4;
51
52 const LOG_COLOR = '#999999';
53 const DEBUG_COLOR = '#666666';
54 const INFO_COLOR = '#276f86';
55 const NOTICE_COLOR = '#eb7609';
56 const WARNING_COLOR = '#9f3a38';
57
58 /**
59 * Private variable to hold time start.
60 *
61 * @var int
62 */
63 private static $time_start = null;
64
65 /**
66 * Private varibale to hold the log file prefix.
67 *
68 * @var string Default 'mainwp'
69 */
70 private $logFileNamePrefix = 'mainwp';
71
72 /**
73 * Private varibale to hold the log file suffix.
74 *
75 * @var string Default '.log'
76 */
77 private $logFileNameSuffix = '.log';
78
79 /**
80 * Private varibale to hold the log file max size.
81 *
82 * @var int Default 0.5
83 */
84 private $logMaxMB = 0.5;
85
86 /**
87 * Private varibale to hold the log file date format.
88 *
89 * @var string Default 'Y-m-d H:i:s'
90 */
91 private $logDateFormat = 'Y-m-d H:i:s';
92
93 /**
94 * Private varibale to hold the log file output directory.
95 *
96 * @var mixed Default null
97 */
98 private $logDirectory = null;
99
100 /**
101 * Private varibale to hold the log file priotrity.
102 *
103 * @var string Disabled
104 */
105 private $logPriority = self::DISABLED;
106
107 /**
108 * Private varibale to hold the log Specific priotrity.
109 *
110 * @var string Disabled
111 */
112 private $logSpecific = 0;
113
114 /**
115 * Private varibale to hold the auto enable logging actions.
116 *
117 * @var array Auto enable logging.
118 */
119 private $autoEnableLoggingActions = array();
120
121
122 /**
123 * Private static varibale to hold the instance.
124 *
125 * @var mixed Default null
126 */
127 private static $instance = null;
128
129 /**
130 * Method instance()
131 *
132 * Returns new MainWP_Logger instance.
133 *
134 * @return self MainWP_Logger
135 *
136 * @uses \MainWP\Dashboard\MainWP_Logger
137 */
138 public static function instance() {
139 if ( null === static::$instance ) {
140 static::$instance = new self();
141 }
142 return static::$instance;
143 }
144
145 /**
146 * MainWP_Logger constructor.
147 *
148 * Run each time the class is called.
149 *
150 * @uses \MainWP\Dashboard\MainWP_System_Utility::get_mainwp_dir()
151 */
152 private function __construct() {
153
154 add_action( 'init', array( $this, 'init' ) ); // Fix the issue where database tables do not exist.
155
156 $enabled = $this->get_log_status();
157 $specific = $this->get_log_specific();
158
159 $enabled = apply_filters( 'mainwp_log_status', $enabled );
160 $specific = apply_filters( 'mainwp_log_specific', $specific );
161
162 $this->set_log_priority( $enabled, $specific );
163
164 $this->autoEnableLoggingActions = array(
165 static::CONNECT_LOG_PRIORITY,
166 );
167 add_action( 'mainwp_module_log_record_inserted', array( $this, 'hook_module_log_record_inserted' ), 10, 2 );
168 add_filter( 'mainwp_custom_log_enabled_log_priority', array( $this, 'hook_is_enabled_log_priority' ), 10, 1 );
169 }
170
171 /**
172 * Method init.
173 */
174 public function init() {
175 $enabled = get_option( 'mainwp_actionlogs' );
176 if ( false === $enabled && ! get_transient( 'mainwp_transient_action_logs' ) ) {
177 $sites_count = MainWP_DB::instance()->get_websites_count();
178 if ( empty( $sites_count ) ) {
179 set_transient( 'mainwp_transient_action_logs', true, 2 * WEEK_IN_SECONDS );
180 }
181 }
182 }
183
184 /**
185 * Method set_log_priority()
186 *
187 * Sets the log priority.
188 *
189 * @param mixed $logPriority Log priority value.
190 * @param mixed $spec_log Specific log.
191 */
192 public function set_log_priority( $logPriority, $spec_log = 0 ) {
193 $this->logPriority = (int) $logPriority;
194 $this->logSpecific = (int) $spec_log; // 1 - specific log, 0 - not specific log.
195 }
196
197
198 /**
199 * Method enable_log_priority()
200 *
201 * Sets the log priority.
202 *
203 * @param mixed $logPriority Log priority value.
204 * @param mixed $spec_log Specific log.
205 */
206 public function enable_log_priority( $logPriority, $spec_log = 1 ) {
207 $spec_log = $spec_log ? 1 : 0;
208 $logPriority = intval( $logPriority );
209 MainWP_Utility::update_option( 'mainwp_specific_logs', $spec_log );
210 MainWP_Utility::update_option( 'mainwp_actionlogs', $logPriority );
211 MainWP_Utility::update_option( 'mainwp_actionlogs_enabled_timestamp', time() );
212 $this->log_action( 'Action logs set to: ' . $this->get_log_text( $logPriority ), ( $spec_log ? $logPriority : static::LOG ), 2, true );
213 $this->set_log_priority( $logPriority, $spec_log );
214 }
215
216
217 /**
218 * Method get_log_status()
219 *
220 * Get log status.
221 *
222 * @return mixed $enabled log status.
223 */
224 public function get_log_status() {
225 $enabled = get_option( 'mainwp_actionlogs' );
226 if ( false === $enabled ) {
227 if ( get_transient( 'mainwp_transient_action_logs' ) ) {
228 $enabled = self::DEBUG;
229 } else {
230 $enabled = static::DISABLED;
231 }
232 }
233 return $enabled;
234 }
235
236
237 /**
238 * Method get_log_specific()
239 *
240 * Get log specific status.
241 *
242 * @return mixed $enabled log status.
243 */
244 public function get_log_specific() {
245 return get_option( 'mainwp_specific_logs', 0 );
246 }
247
248 /**
249 * Method get_log_type_info()
250 *
251 * Get log type info.
252 *
253 * @param int $type Log type value.
254 * @param int $logcolor Log color value.
255 *
256 * @return array $currentColor log color code.
257 */
258 public function get_log_type_info( $type, $logcolor ) {
259 $currentColor = '';
260 $prefix = '';
261 if ( static::DEBUG === $type || static::DEBUG === $logcolor ) {
262 $currentColor = static::DEBUG_COLOR;
263 $prefix = '[DEBUG]';
264 } elseif ( static::INFO === $type || static::INFO === $logcolor ) {
265 $currentColor = static::INFO_COLOR;
266 $prefix = '[INFO]';
267 } elseif ( static::WARNING === $type || static::WARNING === $logcolor ) {
268 $currentColor = static::WARNING_COLOR;
269 $prefix = '[WARNING]';
270 } elseif ( static::LOG === $type || static::LOG === $logcolor ) {
271 $currentColor = static::LOG_COLOR;
272 $prefix = '[LOG]';
273 } elseif ( static::NOTICE === $type || static::NOTICE === $logcolor ) {
274 $currentColor = static::NOTICE_COLOR;
275 $prefix = '[NOTICE]';
276 }
277 return array(
278 'log_color' => $currentColor,
279 'log_prefix' => $prefix,
280 );
281 }
282
283 /**
284 * Method debug()
285 *
286 * Grab debug.
287 *
288 * @param string $text Debug message text.
289 *
290 * @return string Log debug message.
291 */
292 public function debug( $text ) {
293 return $this->log( $text, static::DEBUG );
294 }
295
296 /**
297 * Method info()
298 *
299 * Grab info.
300 *
301 * @param string $text Info message text.
302 *
303 * @return string Log info message.
304 */
305 public function info( $text ) {
306 return $this->log( $text, static::INFO );
307 }
308
309 /**
310 * Method warning()
311 *
312 * Grab warning information.
313 *
314 * @param string $text Warning message text.
315 *
316 * @return string Log warning message.
317 */
318 public function warning( $text ) {
319 return $this->log( $text, static::WARNING );
320 }
321
322 /**
323 * Method actions()
324 *
325 * Grab actions information.
326 *
327 * @param string $text Warning message text.
328 * @param int $priority priority message.
329 * @param int $log_color Set color: 0 - LOG, 1 - WARNING, 2 - INFO, 3- DEBUG.
330 * @param bool $forced forced logging.
331 * @param int $log_type Log type.
332 *
333 * @return string Log warning message.
334 */
335 public function log_action( $text, $priority, $log_color = 0, $forced = false, $log_type = 0 ) {
336 return $this->log( $text, $priority, $log_color, $forced, false, $log_type );
337 }
338
339
340 /**
341 * Method log_events().
342 *
343 * @param string $event_name Event name.
344 * @param string $text Log update check.
345 * @param mixed $color Log color.
346 * @param int $log_type Log type.
347 */
348 public function log_events( $event_name, $text = '', $color = false, $log_type = false ) {
349 $events = is_array( $event_name ) ? $event_name : explode( '|', $event_name );
350 if ( is_array( $events ) ) {
351 foreach ( $events as $event ) {
352 switch ( $event ) {
353 case 'update-check':
354 $this->log_action( '[Update Checks] :: ' . $text, static::UPDATE_CHECK_LOG_PRIORITY );
355 break;
356 case 'regular-schedule':
357 $this->log_action( '[Regular Schedule] :: ' . $text, static::LOGS_REGULAR_SCHEDULE );
358 break;
359 case 'debug-updates-crons':
360 $this->log_action( '[Debug updates crons] :: ' . $text, static::DEBUG_UPDATES_SCHEDULE );
361 break;
362 case 'sites-changes':
363 $this->log_action( '[Sites Changes] :: ' . $text, static::SITES_CHANGES_LOG_PRIORITY );
364 break;
365 case 'cache-metrics':
366 $this->log_action( '[MainWP Cache] :: ' . $text, static::CACHE_METRICS_LOG_PRIORITY );
367 break;
368 case 'db-queries':
369 $this->log_action( '[DB Queries] :: ' . $text, static::DB_QUERIES_LOG_PRIORITY );
370 break;
371 case 'unhooks':
372 $this->log_action( '[Unhooks infor] :: ' . $text, static::UNHOOKS_LOG_PRIORITY, $color );
373 break;
374 case 'execution-time':
375 $this->log_action( '[Execution time] :: ' . $text, static::EXECUTION_TIME_LOG_PRIORITY, $color );
376 break;
377 case 'execution-sync':
378 $this->log_action( '[Execution Sync] :: ' . $text, static::EXECUTION_SYNC_LOG_PRIORITY, $color, false, $log_type );
379 break;
380 case 'execution-sync-details':
381 $this->log_action( '[Execution Sync] :: ' . $text, static::EXECUTION_SYNC_DETAILS_LOG_PRIORITY, $color, false, $log_type );
382 break;
383 case 'warm-cache':
384 $this->log_action( '[Warm cache] :: ' . $text, static::WARM_CACHE_LOG_PRIORITY, $color );
385 break;
386 case 'extension-updates-check':
387 $this->log_action( '[Extension Updates Check] :: ' . $text, static::EXTENSION_UPDATES_CHECK_LOG_PRIORITY, $color );
388 break;
389 default:
390 break;
391 }
392 }
393 }
394 }
395
396 /**
397 * Method log_update_check().
398 *
399 * @param string $text Log update check.
400 */
401 public function log_update_check( $text = '' ) {
402 $this->log_action( $text, static::UPDATE_CHECK_LOG_PRIORITY );
403 }
404
405 /**
406 * Method log_update_check().
407 *
408 * @param string $text Log update check.
409 */
410 public function log_uptime_check( $text = '' ) {
411 $this->log_action( $text, static::UPTIME_CHECK_LOG_PRIORITY );
412 }
413
414 /**
415 * Method log_uptime_notice().
416 *
417 * @param string $text Log update check.
418 */
419 public function log_uptime_notice( $text = '' ) {
420 $this->log_action( $text, static::UPTIME_NOTICE_LOG_PRIORITY );
421 }
422
423 /**
424 * Method debug_for_website()
425 *
426 * Grab website debug and info.
427 *
428 * @param object $website Child site object.
429 * @param string $action Performed action.
430 * @param string $message Debug message.
431 *
432 * @return mixed Website debug info.
433 *
434 * @uses \MainWP\Dashboard\MainWP_Utility::get_nice_url()
435 */
436 public function debug_for_website( $website, $action, $message ) {
437 if ( empty( $website ) ) {
438 return $this->log( '[-] [-] ::' . $action . ':: ' . $message, static::DEBUG );
439 }
440
441 return $this->log( '[' . $website->name . '] [' . MainWP_Utility::get_nice_url( $website->url ) . '] ::' . $action . ':: ' . $message, static::DEBUG, 0, false, $website );
442 }
443
444 /**
445 * Method info_for_website()
446 *
447 * Grab Website Info.
448 *
449 * @param object $website Child site object.
450 * @param string $action Performed action.
451 * @param string $message Info message.
452 *
453 * @return mixed Website Info.
454 *
455 * @uses \MainWP\Dashboard\MainWP_Utility::get_nice_url()
456 */
457 public function info_for_website( $website, $action, $message ) {
458 if ( empty( $website ) ) {
459 return $this->log( '[-] [-] ::' . $action . ':: ' . $message, static::INFO );
460 }
461
462 return $this->log( '[' . $website->name . '] [' . MainWP_Utility::get_nice_url( $website->url ) . '] ::' . $action . ':: ' . $message, static::INFO );
463 }
464
465 /**
466 * Method warning_for_website()
467 *
468 * Grab Website Warnings.
469 *
470 * @param object $website Child site object.
471 * @param string $action Performed action.
472 * @param string $message Warning message.
473 * @param bool $addStackTrace Add or Don't add stack trace.
474 *
475 * @return string Website warnings.
476 */
477 public function warning_for_website( $website, $action, $message, $addStackTrace = true ) {
478 $stackTrace = '';
479 if ( $addStackTrace ) {
480 ob_start();
481 // phpcs:ignore -- for debugging.
482 debug_print_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS );
483 $stackTrace = "\n" . ob_get_clean();
484 }
485 if ( empty( $website ) ) {
486 return $this->log( '[-] [-] :: ' . $action . ' :: ' . $message . $stackTrace, static::WARNING );
487 }
488
489 return $this->log( '[' . $website->name . '] [' . MainWP_Utility::get_nice_url( $website->url ) . '] ::' . $action . ':: ' . $message . $stackTrace, static::WARNING );
490 }
491
492 /**
493 * Method log_to_db()
494 *
495 * Log to database.
496 *
497 * @param string $text Log record text.
498 * @param int $priority Set priority.
499 * @param int $log_color Set color.
500 * @param bool $forced forced logging.
501 * @param mixed $website website object.
502 * @param int $log_type Log type.
503 *
504 * @return bool true|false Default is False.
505 */
506 private function log_to_db( $text, $priority, $log_color = 0, $forced = false, $website = false, $log_type = false ) {
507
508 if ( static::DISABLED === $this->logPriority ) {
509 return false;
510 }
511
512 $priority = (int) apply_filters( 'mainwp_log_to_db_priority', $priority, $website );
513
514 $do_log = false;
515
516 if ( 1 === $this->logSpecific ) { // 1 - specific log, 0 - not specific log.
517 if ( $this->logPriority === $priority ) { // specific priority number saved setting.
518 $do_log = true;
519 }
520 } elseif ( $this->logPriority >= $priority ) {
521 $do_log = true;
522 }
523
524 $do_log = apply_filters( 'mainwp_log_do_to_db', $do_log, $website );
525
526 if ( ! $forced && ! $do_log ) {
527 return false;
528 }
529
530 $text = $this->prepare_log_info( $text );
531
532 do_action( 'mainwp_before_log_data', $text, $priority, $log_color );
533
534 if ( defined( 'DOING_CRON' ) && DOING_CRON && 'CRON' !== strtoupper( substr( $text, 0, 4 ) ) ) {
535 $text = 'CRON :: ' . $text;
536 }
537
538 /**
539 * Current user global.
540 *
541 * @global string
542 */
543 global $current_user;
544
545 $user = '';
546 if ( ! empty( $current_user ) && ! empty( $current_user->user_login ) ) {
547 $user = $current_user->user_login;
548 } elseif ( defined( 'WP_CLI' ) ) {
549 $user = 'WP_CLI';
550 } elseif ( defined( 'DOING_CRON' ) ) {
551 $user = 'DOING_CRON';
552 }
553
554 $data = array();
555
556 $type = false !== $log_type ? (int) $log_type : (int) $priority;
557
558 $data['log_content'] = $text;
559 $data['log_user'] = $user;
560 $data['log_type'] = $type;
561
562 if ( is_int( $log_color ) || ctype_digit( (string) $log_color ) ) {
563 $data['log_color'] = (int) $log_color;
564 } else {
565 $hex = sanitize_hex_color( $log_color );
566 $data['log_color'] = $hex ? $hex : '';
567 }
568
569 $data['log_timestamp'] = time();
570
571 $data = apply_filters( 'mainwp_log_to_db_data', $data );
572
573 MainWP_DB_Common::instance()->insert_action_log( $data );
574
575 return true;
576 }
577
578 /**
579 * Method hook_is_enabled_log_priority().
580 *
581 * @param int $priority Log priority.
582 *
583 * @return bool true|false True if enabled log for priority.
584 */
585 public function hook_is_enabled_log_priority( $priority ) {
586 return $this->enabled_log_priority( $priority );
587 }
588
589 /**
590 * Method enabled_log_priority()
591 *
592 * @param int $priority Set priority.
593 *
594 * @return bool true|false Default is False.
595 */
596 public function enabled_log_priority( $priority ) {
597
598 if ( static::DISABLED === $this->logPriority ) {
599 return false;
600 }
601
602 $priority = (int) apply_filters( 'mainwp_log_to_db_priority', $priority );
603 $do_log = false;
604 if ( 1 === $this->logSpecific ) { // 1 - specific log, 0 - not specific log.
605 if ( $this->logPriority === $priority ) { // specific priority number saved setting.
606 $do_log = true;
607 }
608 } elseif ( $this->logPriority >= $priority ) {
609 $do_log = true;
610 }
611 return $do_log;
612 }
613
614 /**
615 * Method log()
616 *
617 * Create Log File.
618 *
619 * @param string $text Log record text.
620 * @param int $priority Set priority.
621 * @param int $log_color Set color.
622 * @param bool $forced forced logging.
623 * @param mixed $website Site object.
624 * @param int $log_type Log type.
625 *
626 * @return bool true|false Default is False.
627 */
628 private function log( $text, $priority, $log_color = 0, $forced = false, $website = false, $log_type = false ) { // phpcs:ignore -- NOSONAR - complex function.
629
630 if ( in_array( $priority, $this->autoEnableLoggingActions ) && (int) $this->logPriority !== (int) $priority ) {
631 $this->enable_log_priority( $priority, 1 );
632 }
633
634 if ( static::DISABLED === $this->logPriority ) {
635 return false;
636 }
637
638 $log_to_db = apply_filters( 'mainwp_logger_to_db', true, $website );
639
640 if ( $log_to_db ) {
641 return $this->log_to_db( $text, $priority, $log_color, $forced, $website, $log_type );
642 }
643
644 $text = $this->prepare_log_info( $text );
645
646 $do_log = false;
647 if ( 1 === $this->logSpecific ) { // 1 - specific log, 0 - not specific log.
648 if ( $this->logPriority === $priority ) { // specific priority number saved setting.
649 $do_log = true;
650 }
651 } elseif ( $this->logPriority >= $priority ) {
652 $do_log = true;
653 }
654
655 if ( $do_log ) {
656 $this->logCurrentFile = $this->get_log_file();
657 $logCurrentHandle = fopen( $this->logCurrentFile, 'a+' );
658
659 if ( $logCurrentHandle ) {
660 $time = gmdate( $this->logDateFormat );
661 $prefix = '[' . $this->get_log_text( $priority ) . ']';
662
663 /**
664 * Current user global.
665 *
666 * @global string
667 */
668 global $current_user;
669
670 if ( ! empty( $current_user ) && ! empty( $current_user->user_login ) ) {
671 $prefix .= ' [administrator]';
672 }
673
674 fwrite( $logCurrentHandle, $time . ' ' . $prefix . ' ' . $text . "\n" );
675 fclose( $logCurrentHandle );
676 }
677
678 if ( filesize( $this->logCurrentFile ) > ( $this->logMaxMB * 1048576 ) ) {
679 $logCurrentHandle = fopen( $this->logCurrentFile, 'a+' );
680 if ( $logCurrentHandle ) {
681 fseek( $logCurrentHandle, 0 );
682 $newLogFile = $this->logCurrentFile . '.tmp';
683 $newLogHandle = false;
684 $chunkSize = filesize( $this->logCurrentFile ) - ( $this->logMaxMB * 1048576 );
685 while ( is_resource( $logCurrentHandle ) && ! feof( $logCurrentHandle ) && ( $chunkSize > 0 ) ) {
686 $content = fread( $logCurrentHandle, $chunkSize );
687 if ( false === $content ) {
688 break;
689 }
690 $pos = strrpos( $content, "\n" );
691 if ( $newLogHandle ) {
692 fwrite( $newLogHandle, $content );
693 } elseif ( $pos ) {
694 if ( ! $newLogHandle ) {
695 $newLogHandle = fopen( $newLogFile, 'w+' );
696 }
697 fwrite( $newLogHandle, substr( $content, $pos + 1 ) );
698 }
699 }
700
701 if ( is_resource( $logCurrentHandle ) ) {
702 fclose( $logCurrentHandle );
703 }
704
705 if ( $newLogHandle ) {
706 fclose( $newLogHandle );
707 wp_delete_file( $this->logCurrentFile );
708 if ( file_exists( $newLogFile ) ) {
709 rename( $newLogFile, $this->logCurrentFile );
710 }
711 }
712 }
713 }
714
715 return true;
716 }
717
718 return false;
719 }
720
721
722 /**
723 * Method prepare_log_info()
724 *
725 * Prepare log data.
726 *
727 * @param mixed $data Log data.
728 *
729 * @return mixed $data filtered data.
730 */
731 public function prepare_log_info( $data ) {
732 $patterns[0] = '/user=([^\&]+)\&/';
733 $replacement[0] = 'user=xxxxxx&';
734 $patterns[1] = '/alt_user=([^\&]+)\&/';
735 $replacement[1] = 'alt_user=xxxxxx&';
736 $patterns[2] = '/\&server=([^\&]+)\&/';
737 $replacement[2] = '&server=xxxxxx&';
738 $data = preg_replace( $patterns, $replacement, $data );
739 return $data;
740 }
741
742 /**
743 * Method prepend()
744 *
745 * Prepend content to log file.
746 *
747 * @param mixed $str Custom string.
748 * @param mixed $filename Filename.
749 */
750 public function prepend( $str, $filename ) {
751 $context = stream_context_create();
752 $fp = fopen( $filename, 'r', 1, $context );
753 $tmpname = md5( $str ); // NOSONAR - safe for salt file name.
754 file_put_contents( $tmpname, $str );
755 file_put_contents( $tmpname, $fp, FILE_APPEND );
756 fclose( $fp );
757 wp_delete_file( $filename );
758 rename( $tmpname, $filename );
759 }
760
761 /**
762 * Method init_execution_time().
763 *
764 * @param string $time_index index for timer.
765 *
766 * Init execution time start value.
767 */
768 public function init_execution_time( $time_index = '' ) {
769 if ( null === static::$time_start || ! is_array( static::$time_start ) ) {
770 static::$time_start = array( 'start' => microtime( true ) );
771 $this->log_action( 'execution time :: init :: [start]', static::EXECUTION_TIME_LOG_PRIORITY );
772 }
773
774 if ( ! empty( $time_index ) && is_string( $time_index ) && 'start' !== $time_index ) {
775 static::$time_start[ $time_index ] = microtime( true );
776 $this->log_action( 'execution time :: init :: [' . $time_index . ']', static::EXECUTION_TIME_LOG_PRIORITY );
777 }
778 }
779
780 /**
781 * Method log_execution_time().
782 *
783 * @param string $text Log record text.
784 *
785 * Log the execution time value.
786 */
787 public function log_execution_time( $text = '' ) {
788 $exec_time = $this->get_execution_time();
789 $this->log_action( 'execution time :: ' . ( ! empty( $text ) ? (string) $text . ' :: ' : '' ) . ' [time=' . round( $exec_time, 4 ) . '](seconds)', static::EXECUTION_TIME_LOG_PRIORITY );
790 }
791
792 /**
793 * Method log_execution_sync().
794 *
795 * @param string $progress Log progress value.
796 * @param string $text Log record text.
797 * @param mixed $website Website data, default false.
798 *
799 * Log the execution sync time value.
800 */
801 public function log_execution_sync( $progress = '', $text = '', $website = false ) { // phpcs:ignore -- NOSONAR -complex.
802
803 if ( 1 !== $this->logSpecific || ( static::EXECUTION_SYNC_LOG_PRIORITY !== $this->logPriority && static::EXECUTION_SYNC_DETAILS_LOG_PRIORITY !== $this->logPriority ) ) { // 1 - specific log, 0 - not specific log.
804 return;
805 }
806
807 static $initialized = false;
808
809 if ( 'init' === $progress && ! $initialized ) {
810 $initialized = true;
811 MainWP_Execution_Helper::init_http_call_track();
812 }
813
814 // If the process has ended and the sync event log is not initialized, do not log.
815 if ( 'end' === $progress && ! $initialized ) {
816 return;
817 }
818
819 $text = 'init' === $progress && empty( $text ) ? 'Init' : $text;
820 $text = 'end' === $progress && empty( $text ) ? 'End' : $text;
821
822 $exec_time = $this->get_execution_time();
823 $exec_time = round( $exec_time, 4 );
824
825 if ( ! empty( $text ) ) {
826 $text = $text . ' :: ';
827 }
828
829 $id = '';
830
831 if ( ! empty( $website ) ) {
832
833 if ( is_int( $website ) || ctype_digit( $website ) ) {
834 $id = (int) $website;
835 } elseif ( is_array( $website ) && ! empty( $website['id'] ) ) {
836 $id = $website['id'];
837 } elseif ( is_object( $website ) && property_exists( $website, 'id' ) ) {
838 $id = $website->id;
839 }
840 } else {
841 $id = MainWP_System_Utility::get_current_wpid();
842 }
843
844 if ( ! empty( $id ) ) {
845 $text .= '[siteid=' . $id . '] :: ';
846 }
847
848 $log = $text . '[total runtime=' . $exec_time . '](sec)';
849
850 $mem = '';
851
852 if ( function_exists( 'memory_get_usage' ) && is_callable( 'memory_get_usage' ) ) {
853 $mem = round( memory_get_usage() / 1024 / 1024, 2 );
854 } else {
855 $mem = 'N/A';
856 }
857
858 if ( ! empty( $mem ) ) {
859 $log .= ' :: [memory usage=' . $mem . '](MB)';
860 }
861
862 if ( 'end' === $progress ) {
863 $dbusage = MainWP_Execution_Helper::get_queries_stats();
864 if ( is_array( $dbusage ) ) {
865 if ( isset( $dbusage['total_queries'] ) ) {
866 $log .= ' :: [total queries=' . (int) $dbusage['total_queries'] . ']';
867 }
868 if ( isset( $dbusage['total_runtime'] ) ) {
869 $log .= ' :: [total runtime queries=' . sprintf( '%.5f', $dbusage['total_runtime'] ) . '](sec)';
870 }
871 }
872
873 $callstats = MainWP_Execution_Helper::get_exec_call_stats();
874
875 $rows_details = array();
876
877 if ( is_array( $callstats ) ) {
878 if ( isset( $callstats['check_count'] ) ) {
879 $log .= ' :: [total run check=' . (int) $callstats['check_count'] . ']';
880 }
881 if ( isset( $callstats['exec_time'] ) && is_array( $callstats['exec_time'] ) ) {
882 foreach ( $callstats['exec_time'] as $idx => $t ) {
883 $rows_details[] = '[check runtime=' . sprintf( '%.3f', $t ) . '](sec) :: [run desc=' . ( ! empty( $callstats['check_desc'][ $idx ] ) ? (string) $callstats['check_desc'][ $idx ] : '' ) . ']';
884 if ( static::EXECUTION_SYNC_DETAILS_LOG_PRIORITY === $this->logPriority ) {
885 $rows_details[] = '[run data=' . ( ! empty( $callstats['check_data'][ $idx ] ) && is_array( $callstats['check_data'][ $idx ] ) ? wp_json_encode( $callstats['check_data'][ $idx ] ) : '' ) . ']';
886 }
887 }
888 }
889 }
890 }
891
892 $log .= ' :: [ver=' . MainWP_System::get_mainwp_version() . ']';
893
894 $lg_type = (int) $exec_time >= 50 ? static::NOTICE : static::INFO;
895 $lg_type = (int) $exec_time >= 80 ? static::WARNING : $lg_type;
896
897 $event_name = static::EXECUTION_SYNC_DETAILS_LOG_PRIORITY === $this->logPriority ? 'execution-sync-details' : 'execution-sync';
898 $this->log_events( $event_name, $log, false, $lg_type );
899
900 if ( ! empty( $rows_details ) ) {
901 foreach ( $rows_details as $log_detail ) {
902 $this->log_events( $event_name, $log_detail, false, static::LOG );
903 }
904 }
905 }
906
907 /**
908 * Method get_execution_time().
909 *
910 * Get the execution time value.
911 *
912 * @param string $time_index Index for timer.
913 *
914 * @return int execution time.
915 */
916 private function get_execution_time( $time_index = '' ) {
917
918 if ( empty( static::$time_start ) ) {
919 return 0;
920 }
921
922 $start = 0;
923 if ( ! empty( $time_index ) ) {
924 $start = is_array( static::$time_start ) && isset( static::$time_start[ $time_index ] ) ? static::$time_start[ $time_index ] : 0;
925 }
926
927 if ( empty( $start ) ) {
928 $start = is_array( static::$time_start ) && isset( static::$time_start['start'] ) ? static::$time_start['start'] : 0;
929 }
930
931 if ( empty( $start ) ) {
932 return 0;
933 }
934 return microtime( true ) - $start; // seconds.
935 }
936
937 /**
938 * Method get_log_file()
939 *
940 * Grab Log File.
941 *
942 * @return mixed Log File.
943 */
944 public function get_log_file() {
945 if ( empty( $this->logDirectory ) ) {
946 $this->logDirectory = MainWP_System_Utility::get_mainwp_dir();
947 $this->logDirectory = $this->logDirectory[0];
948 }
949 return $this->logDirectory . $this->logFileNamePrefix . $this->logFileNameSuffix;
950 }
951
952 /**
953 * Method get_log_text()
954 *
955 * Grab what type of log entry.
956 *
957 * @param mixed $priority Set priority.
958 *
959 * @return string LOG -OR- DISABLED|DEBUG|INFO|WARNING|INFO UPDATE
960 */
961 public function get_log_text( $priority ) {
962 switch ( $priority ) {
963 case static::DISABLED:
964 return 'DISABLED';
965 case static::DEBUG:
966 return 'DEBUG';
967 case static::INFO:
968 return 'INFO';
969 case static::WARNING:
970 return 'WARNING';
971 default:
972 return 'SPEC LOG';
973 }
974 }
975
976 /**
977 * Method check_log_daily()
978 *
979 * Daily checks to clear the log file.
980 */
981 public function check_log_daily() {
982 $status = (int) $this->get_log_status();
983 if ( 0 >= $status ) {
984 return;
985 }
986
987 $today_m_y = date_i18n( 'd/m/Y' ); //phpcs:ignore -- local time.
988 // one time per day.
989 if ( get_option( 'mainwp_logger_check_daily' ) !== $today_m_y ) {
990 $num_days = apply_filters( 'mainwp_logger_keep_days', 7 );
991 MainWP_DB_Common::instance()->delete_action_log( $num_days );
992 MainWP_Utility::update_option( 'mainwp_logger_check_daily', $today_m_y );
993
994 $enabled_time = get_option( 'mainwp_actionlogs_enabled_timestamp', false );
995 if ( false === $enabled_time ) {
996 MainWP_Utility::update_option( 'mainwp_actionlogs_enabled_timestamp', time() );
997 } elseif ( $enabled_time + $num_days * DAY_IN_SECONDS < time() ) {
998 MainWP_Utility::update_option( 'mainwp_actionlogs', static::DISABLED );
999 }
1000 }
1001 }
1002
1003 /**
1004 * Method clear_log_db()
1005 *
1006 * Clear the log file.
1007 */
1008 public function clear_log_db() {
1009 MainWP_DB_Common::instance()->delete_action_log();
1010 }
1011
1012 /**
1013 * Method clear_log()
1014 *
1015 * Clear the log file.
1016 */
1017 public function clear_log() {
1018 $logFile = $this->get_log_file();
1019 wp_delete_file( $logFile );
1020 $fh = fopen( $logFile, 'w' );
1021 if ( false === $fh ) {
1022 return;
1023 }
1024 fclose( $fh );
1025 }
1026
1027 /**
1028 * Method show_log_db()
1029 *
1030 * Grab log file and build output to screen.
1031 */
1032 public function show_log_db() { //phpcs:ignore -- NOSONAR - complexity.
1033
1034 echo '<div class="ui hidden divider"></div>';
1035 echo '<div class="ui divided padded relaxed list" local-datetime="' . date( 'Y-m-d H:i:s' ) . '">'; // phpcs:ignore -- local time.
1036
1037 $limit = 500;
1038
1039 //phpcs:disable WordPress.Security.NonceVerification
1040 $paged = isset( $_GET['paged'] ) ? intval( $_GET['paged'] ) : 0; //phpcs:ignore -- NOSONAR -ok.
1041
1042 if ( $paged <= 0 ) {
1043 $paged = 0;
1044 } else {
1045 --$paged;
1046 }
1047
1048 $params = array();
1049
1050 if ( isset( $_GET['hour'] ) && ! empty( $_GET['hour'] ) ) { // phpcs:ignore -- local time.
1051 $params['hour'] = intval( $_GET['hour'] ); // phpcs:ignore -- local time.
1052 } else {
1053 $params['hour'] = $limit;
1054 }
1055
1056 $order = isset( $_GET['order'] ) ? sanitize_text_field( wp_unslash( $_GET['order'] ) ) : ''; //phpcs:ignore -- NOSONAR -ok.
1057
1058 $total = MainWP_DB::instance()->get_var_field( MainWP_DB_Common::instance()->get_sql_log( $paged, $order, array( 'count' => true ) ) );
1059
1060 $rows = MainWP_DB::instance()->query( MainWP_DB_Common::instance()->get_sql_log( $paged, $order, $params ) );
1061
1062 $count = $rows ? MainWP_DB::num_rows( $rows ) : 0;
1063
1064 $show_info = '';
1065
1066 if ( isset( $_GET['hour'] ) ) {
1067 $show_info = $count . ' latest items from a total of ' . $total;
1068 } elseif ( isset( $_GET['paged'] ) ) { //phpcs:ignore -- NOSONAR -ok.
1069 $from = $limit * $paged;
1070 $show_info = $count . ' items, from ' . $from . ' - ' . ( $from + $limit ) . ' of ' . $total . ' total.';
1071 }
1072
1073 if ( ! empty( $show_info ) ) {
1074 echo '<p><strong>' . esc_html__( 'Showing ', 'mainwp' ) . ':</strong> ' . $show_info; //phpcs:ignore -- NOSONAR ok.
1075 }
1076 //phpcs:enable WordPress.Security.NonceVerification
1077
1078 $start_wrapper = '<span class="ui mini label mainwp-action-log-show-more">Click to See Response</span><div class="mainwp-action-log-site-response" style="display: none;">';
1079 $end_wrapper = '</div>';
1080
1081 while ( $rows && ( $row = MainWP_DB::fetch_object( $rows ) ) ) {
1082 $line = $row->log_content;
1083 if ( 120 * 1024 < strlen( $line ) ) {
1084 $line = '[Data row too long]';
1085 }
1086
1087 $time = gmdate( $this->logDateFormat, MainWP_Utility::get_timestamp( $row->log_timestamp ) );
1088
1089 $showInfo = $this->get_log_type_info( (int) $row->log_type, $row->log_color );
1090
1091 $currentColor = $showInfo['log_color'];
1092
1093 $prefix = $time . ' ' . $showInfo['log_prefix'];
1094
1095 $line = nl2br( htmlentities( $line ) );
1096
1097 if ( false !== strpos( $line, '[data-start]' ) ) {
1098 $line = str_replace( '[data-start]', $start_wrapper, $line );
1099 $line = str_replace( '[data-end]', $end_wrapper, $line );
1100 }
1101
1102 echo '<div class="item" style="color:' . esc_attr( $currentColor ) . '"><div class="mainwpactionlogsline">';
1103
1104 echo $prefix . ' ' . $line; // phpcs:ignore WordPress.Security.EscapeOutput
1105
1106 echo '</div></div>';
1107 }
1108
1109 echo '</div></div>';
1110
1111 echo '</div>';
1112
1113 ?>
1114 <div class="ui large modal" id="mainwp-action-log-response-modal">
1115 <i class="close icon mainwp-reload"></i>
1116 <div class="header"><?php esc_html_e( 'Child Site Response', 'mainwp' ); ?></div>
1117 <div class="content">
1118 <div class="ui info message"><?php esc_html_e( 'To see the response in a more readable way, you can copy it and paste it into some HTML render tool, such as Codepen.io.', 'mainwp' ); ?>
1119 </div>
1120 </div>
1121 <div class="scrolling content content-response"></div>
1122 <div class="actions">
1123 <button class="ui green button mainwp-response-copy-button"><?php esc_html_e( 'Copy Response', 'mainwp' ); ?></button>
1124 </div>
1125 </div>
1126 <?php
1127 }
1128
1129 /**
1130 * Method show_log_file()
1131 *
1132 * Grab log file and build output to screen.
1133 */
1134 public function show_log_file() { // phpcs:ignore -- NOSONAR - complex.
1135 $logFile = $this->get_log_file();
1136
1137 if ( ! file_exists( $logFile ) ) {
1138 return;
1139 }
1140
1141 $fh = fopen( $logFile, 'r' );
1142 if ( false === $fh ) {
1143 return;
1144 }
1145
1146 $previousColor = '';
1147 $fontOpen = false;
1148 $firstLinePassedProcessed = false;
1149 echo '<div class="ui hidden divider"></div>';
1150 echo '<div class="ui divided padded relaxed list">';
1151 while ( false !== ( $line = fgets( $fh ) ) ) {
1152 $currentColor = $previousColor;
1153 if ( stristr( $line, '[DEBUG]' ) ) {
1154 $currentColor = static::DEBUG_COLOR;
1155 $firstLinePassed = true;
1156 } elseif ( stristr( $line, '[INFO]' ) ) {
1157 $currentColor = static::INFO_COLOR;
1158 $firstLinePassed = true;
1159 } elseif ( stristr( $line, '[WARNING]' ) ) {
1160 $currentColor = static::WARNING_COLOR;
1161 $firstLinePassed = true;
1162 } elseif ( stristr( $line, '[LOG]' ) ) {
1163 $currentColor = static::LOG_COLOR;
1164 $firstLinePassed = true;
1165 } else {
1166 $firstLinePassed = false;
1167 }
1168
1169 if ( $firstLinePassedProcessed && ! $firstLinePassed ) {
1170 echo ' <span class="ui mini label mainwp-action-log-show-more">Click to See Response</span></div><div class="mainwp-action-log-site-response" style="display: none;">';
1171 }
1172
1173 $firstLinePassedProcessed = $firstLinePassed;
1174
1175 if ( $currentColor !== $previousColor ) {
1176 if ( $fontOpen ) {
1177 echo '</div></div>';
1178 }
1179
1180 echo '<div class="item" style="color:' . esc_html( $currentColor ) . '"><div class="mainwpactionlogsline">';
1181 $fontOpen = true;
1182 }
1183
1184 echo esc_html( htmlentities( $line ) );
1185 }
1186
1187 if ( $fontOpen ) {
1188 echo '</div></div>';
1189 }
1190
1191 echo '</div>';
1192
1193 ?>
1194 <div class="ui large modal" id="mainwp-action-log-response-modal">
1195 <i class="close icon mainwp-reload"></i>
1196 <div class="header"><?php esc_html_e( 'Child Site Response', 'mainwp' ); ?></div>
1197 <div class="content">
1198 <div class="ui info message"><?php esc_html_e( 'To see the response in a more readable way, you can copy it and paste it into some HTML render tool, such as Codepen.io.', 'mainwp' ); ?>
1199 </div>
1200 </div>
1201 <div class="scrolling content content-response"></div>
1202 <div class="actions">
1203 <button class="ui green button mainwp-response-copy-button"><?php esc_html_e( 'Copy Response', 'mainwp' ); ?></button>
1204 </div>
1205 </div>
1206 <?php
1207
1208 fclose( $fh );
1209 }
1210
1211 /**
1212 * Method hook_module_log_record_inserted()
1213 *
1214 * @param int $record_id Log id.
1215 * @param array $record Log data.
1216 *
1217 * @return void
1218 */
1219 public function hook_module_log_record_inserted( $record_id = false, $record = false ) {
1220 if ( $record_id && is_array( $record ) && isset( $record['connector'] ) && isset( $record['action'] ) && isset( $record['item'] ) && isset( $record['context'] ) ) {
1221 $this->log_events( 'sites-changes', 'Logging info :: [site_id=' . ( isset( $record['site_id'] ) ? $record['site_id'] : 0 ) . '] :: [log_id=' . $record_id . '] :: [log_type_id=' . ( isset( $record['log_type_id'] ) ? $record['log_type_id'] : 0 ) . '] :: [item=' . $record['item'] . '] :: [connector=' . $record['connector'] . '] :: [context=' . $record['context'] . '] :: [action=' . $record['action'] . ']' );
1222 }
1223 }
1224 }
1225