| 1 |
<?php |
| 2 |
|
| 3 |
namespace YayMail\Utils; |
| 4 |
|
| 5 |
/** |
| 6 |
* Logger |
| 7 |
*/ |
| 8 |
class Logger { |
| 9 |
private $log_directory; |
| 10 |
private $wp_filesystem; |
| 11 |
private $max_log_files; |
| 12 |
|
| 13 |
/** |
| 14 |
* Constructor for the Logger class. |
| 15 |
* |
| 16 |
* Initializes the logger with a specified log directory, prefix for log entries, |
| 17 |
* and the maximum number of log files to keep. Ensures the log directory exists and |
| 18 |
* performs cleanup of old log files. |
| 19 |
*/ |
| 20 |
public function __construct( $log_directory = null, $max_log_files = 30 ) { |
| 21 |
if ( ! $log_directory ) { |
| 22 |
$log_directory = WP_CONTENT_DIR . '/yaymail-logs'; |
| 23 |
} |
| 24 |
$this->log_directory = $log_directory; |
| 25 |
$this->max_log_files = $max_log_files; |
| 26 |
$this->initialize_filesystem(); |
| 27 |
|
| 28 |
// Ensure the log directory exists (only if filesystem is available) |
| 29 |
if ( $this->wp_filesystem && ! $this->wp_filesystem->is_dir( $this->log_directory ) ) { |
| 30 |
$this->wp_filesystem->mkdir( $this->log_directory, 0755 ); |
| 31 |
} |
| 32 |
|
| 33 |
// Clean up old log files (only if filesystem is available) |
| 34 |
if ( $this->wp_filesystem ) { |
| 35 |
$this->cleanup_old_logs(); |
| 36 |
} |
| 37 |
} |
| 38 |
|
| 39 |
private function initialize_filesystem() { |
| 40 |
global $wp_filesystem; |
| 41 |
|
| 42 |
// Initialize the WordPress filesystem |
| 43 |
require_once ABSPATH . 'wp-admin/includes/file.php'; |
| 44 |
|
| 45 |
// Try to initialize WP_Filesystem, fallback to direct method if failed |
| 46 |
if ( ! WP_Filesystem() || ! $wp_filesystem ) { |
| 47 |
// Fallback to direct filesystem access |
| 48 |
require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-direct.php'; |
| 49 |
$this->wp_filesystem = new \WP_Filesystem_Direct( null ); |
| 50 |
} else { |
| 51 |
$this->wp_filesystem = $wp_filesystem; |
| 52 |
} |
| 53 |
} |
| 54 |
|
| 55 |
private function cleanup_old_logs() { |
| 56 |
// Return early if filesystem is not available or log directory is not a directory |
| 57 |
if ( ! $this->wp_filesystem || ! $this->wp_filesystem->is_dir( $this->log_directory ) ) { |
| 58 |
return; |
| 59 |
} |
| 60 |
|
| 61 |
// Get all log files in the directory |
| 62 |
$files = $this->wp_filesystem->dirlist( $this->log_directory ); |
| 63 |
|
| 64 |
if ( ! is_array( $files ) ) { |
| 65 |
return; |
| 66 |
} |
| 67 |
|
| 68 |
// Filter only log files |
| 69 |
$log_files = array_filter( |
| 70 |
$files, |
| 71 |
function( $file ) { |
| 72 |
return strpos( $file['name'], '.log' ) !== false; |
| 73 |
} |
| 74 |
); |
| 75 |
|
| 76 |
// Sort log files by name (assuming the name is in the format 'Y-m-d.log') |
| 77 |
usort( |
| 78 |
$log_files, |
| 79 |
function( $a, $b ) { |
| 80 |
return strcmp( $b['name'], $a['name'] ); |
| 81 |
} |
| 82 |
); |
| 83 |
|
| 84 |
// If there are more log files than the allowed maximum, delete the oldest |
| 85 |
if ( count( $log_files ) > $this->max_log_files ) { |
| 86 |
$files_to_delete = array_slice( $log_files, $this->max_log_files ); |
| 87 |
|
| 88 |
foreach ( $files_to_delete as $file ) { |
| 89 |
$this->wp_filesystem->delete( $this->log_directory . '/' . $file['name'] ); |
| 90 |
} |
| 91 |
} |
| 92 |
} |
| 93 |
|
| 94 |
public function log( $message ) { |
| 95 |
// Return early if filesystem is not available |
| 96 |
if ( ! $this->wp_filesystem ) { |
| 97 |
return; |
| 98 |
} |
| 99 |
|
| 100 |
// Get the current date to create a log file for each day, based on local time |
| 101 |
$date = current_time( 'Y-m-d' ); |
| 102 |
$log_file = $this->log_directory . '/' . $date . '.log'; |
| 103 |
|
| 104 |
// Construct the log message with a timestamp based on local time |
| 105 |
$timestamp = current_time( 'Y-m-d H:i:s' ); |
| 106 |
$log_message = "[$timestamp] $message" . PHP_EOL; |
| 107 |
|
| 108 |
// Append the log message to the log file |
| 109 |
if ( $this->wp_filesystem->exists( $log_file ) ) { |
| 110 |
$existing_content = $this->wp_filesystem->get_contents( $log_file ); |
| 111 |
$log_message = $existing_content . $log_message; |
| 112 |
} |
| 113 |
|
| 114 |
$this->wp_filesystem->put_contents( $log_file, $log_message, FS_CHMOD_FILE ); |
| 115 |
} |
| 116 |
|
| 117 |
/** |
| 118 |
* Logs an exception message and sends a JSON error response. |
| 119 |
* Message will display in folder wp-content/yaymail-logs |
| 120 |
*/ |
| 121 |
public function log_exception_message( $ex, $log_type = 'error', $additional_data = null ) { |
| 122 |
$prefix = ( $log_type === 'warning' ) ? __( 'WARNING:', 'yaymail' ) : __( 'SYSTEM ERROR:', 'yaymail' ); |
| 123 |
|
| 124 |
$message = $prefix . ' ' . $ex->getCode() . ' : ' . $ex->getMessage(); |
| 125 |
$message .= PHP_EOL . $ex->getFile() . '(' . $ex->getLine() . ')'; |
| 126 |
$message .= PHP_EOL . $ex->getTraceAsString(); |
| 127 |
|
| 128 |
// Add additional data if provided |
| 129 |
if ( $additional_data !== null ) { |
| 130 |
$message .= PHP_EOL . 'Additional data: '; |
| 131 |
if ( is_array( $additional_data ) || is_object( $additional_data ) ) { |
| 132 |
$message .= PHP_EOL . print_r( $additional_data, true ); |
| 133 |
} else { |
| 134 |
$message .= $additional_data; |
| 135 |
} |
| 136 |
} |
| 137 |
|
| 138 |
$this->log( $message ); |
| 139 |
|
| 140 |
// Only send JSON response for errors, not for warnings |
| 141 |
if ( $log_type === 'error' ) { |
| 142 |
wp_send_json_error( [ 'mess' => $message ] ); |
| 143 |
} |
| 144 |
} |
| 145 |
} |
| 146 |
|