PluginProbe
MainWP Dashboard: Self-hosted WordPress Management for Agencies / trunk
MainWP Dashboard: Self-hosted WordPress Management for Agencies vtrunk
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 trunk, at class/class-mainwp-logger.php

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