Logger.php
223 lines
| 1 | <?php |
| 2 | |
| 3 | namespace NitroPack\Feature\Logger; |
| 4 | |
| 5 | class Logger { |
| 6 | private $nitro; |
| 7 | public $separator; |
| 8 | const ERROR = 3; |
| 9 | const NOTICE = 2; |
| 10 | const INFO = 1; |
| 11 | |
| 12 | /** Mapping of log levels to their string representations */ |
| 13 | private $level_value; |
| 14 | |
| 15 | /** |
| 16 | * Logger constructor. |
| 17 | * |
| 18 | * @param int $level The logging level (default: ERROR) |
| 19 | */ |
| 20 | public function __construct( $nitro ) { |
| 21 | $this->nitro = $nitro; |
| 22 | $this->separator = ';'; |
| 23 | $this->level_value = [ |
| 24 | self::ERROR => 'ERROR', |
| 25 | self::NOTICE => 'NOTICE', |
| 26 | self::INFO => 'INFO' |
| 27 | ]; |
| 28 | } |
| 29 | |
| 30 | /** |
| 31 | * Determines if the current execution environment is CLI (Command Line Interface). |
| 32 | * |
| 33 | * This function checks various indicators to determine if the script is being run from the command line. |
| 34 | * It returns true if any of the following conditions are met: |
| 35 | * - The STDIN constant is defined. |
| 36 | * - The PHP SAPI name is 'cli'. |
| 37 | * - The 'SHELL' key exists in the $_ENV array. |
| 38 | * - The $_SERVER array lacks 'REMOTE_ADDR' and 'HTTP_USER_AGENT', and has arguments in 'argv'. |
| 39 | * - The 'REQUEST_METHOD' key does not exist in the $_SERVER array. |
| 40 | * |
| 41 | * @return bool True if the script is running in CLI mode, false otherwise. |
| 42 | */ |
| 43 | private function is_cli() { |
| 44 | if ( defined( 'STDIN' ) ) { |
| 45 | return true; |
| 46 | } |
| 47 | |
| 48 | if ( php_sapi_name() === 'cli' ) { |
| 49 | return true; |
| 50 | } |
| 51 | |
| 52 | if ( array_key_exists( 'SHELL', $_ENV ) ) { |
| 53 | return true; |
| 54 | } |
| 55 | |
| 56 | if ( empty( $_SERVER['REMOTE_ADDR'] ) and ! isset( $_SERVER['HTTP_USER_AGENT'] ) and count( $_SERVER['argv'] ) > 0 ) { |
| 57 | return true; |
| 58 | } |
| 59 | |
| 60 | if ( ! array_key_exists( 'REQUEST_METHOD', $_SERVER ) ) { |
| 61 | return true; |
| 62 | } |
| 63 | |
| 64 | return false; |
| 65 | } |
| 66 | /** |
| 67 | * Log a message with the specified level. |
| 68 | * The message will be logged only if the current log level meets the minimum log level requirement. |
| 69 | * The log level is first fetched from the config.json if the value is not found then it is fetched from the database. |
| 70 | * The reason is that when connecting or disconnecting NitroPack, the config.json is empty. |
| 71 | * |
| 72 | * @param int $level The log level of the message |
| 73 | * @param string $message The message to log |
| 74 | * @return void |
| 75 | */ |
| 76 | private function log( $level, $message ) { |
| 77 | $siteConfig = $this->nitro->getSiteConfig(); |
| 78 | $configLevel = ! empty( $siteConfig['minimumLogLevel'] ) ? $siteConfig['minimumLogLevel'] : null; |
| 79 | |
| 80 | if ( ! $configLevel ) { |
| 81 | if ( ! function_exists( 'get_option' ) ) { |
| 82 | return; |
| 83 | } |
| 84 | |
| 85 | $configLevel = (int) get_option( 'nitropack-minimumLogLevel', null ); |
| 86 | } |
| 87 | |
| 88 | // Check if the log level is set and if the current log level meets the minimum log level requirement |
| 89 | if ( $configLevel === null || $level < $configLevel ) { |
| 90 | return; |
| 91 | } |
| 92 | |
| 93 | if ( ! $this->init_logs_dir() ) |
| 94 | return; |
| 95 | |
| 96 | $log_file = $this->get_log_file_path(); |
| 97 | $max_size = $this->get_max_log_filesize(); |
| 98 | |
| 99 | if ( file_exists( $log_file ) && filesize( $log_file ) > $max_size ) { |
| 100 | error_log( "NitroPack log file has reached maximum size. Logging stopped." ); |
| 101 | return; |
| 102 | } |
| 103 | |
| 104 | $prepend = ''; |
| 105 | if ( $this->is_cli() ) |
| 106 | $prepend = '[CLI] '; |
| 107 | $level = $this->level_value[ $level ]; |
| 108 | $content = [ |
| 109 | 'Date' => date( 'Y-m-d H:i:s' ), |
| 110 | 'Level' => $level, |
| 111 | 'Message' => $prepend . $message, |
| 112 | ]; |
| 113 | |
| 114 | $this->write_to_log_file( $log_file, $content ); |
| 115 | } |
| 116 | public function error( $message ) { |
| 117 | $this->log( self::ERROR, $message ); |
| 118 | } |
| 119 | public function notice( $message ) { |
| 120 | $this->log( self::NOTICE, $message ); |
| 121 | } |
| 122 | public function info( $message ) { |
| 123 | $this->log( self::INFO, $message ); |
| 124 | } |
| 125 | /** |
| 126 | * Initialize the logs directory. |
| 127 | * |
| 128 | * @return bool True if the directory exists or was created successfully, false otherwise |
| 129 | */ |
| 130 | private function init_logs_dir() { |
| 131 | if ( $this->data_logs_dir_exists() ) |
| 132 | return true; |
| 133 | |
| 134 | $create_dir = mkdir( NITROPACK_LOGS_DATA_DIR ); |
| 135 | |
| 136 | if ( $create_dir ) { |
| 137 | // Create .htaccess file with deny from all |
| 138 | $htaccess_path = NITROPACK_LOGS_DATA_DIR . '/.htaccess'; |
| 139 | file_put_contents( $htaccess_path, "Order Allow,Deny\nAllow from all\n<FilesMatch \"\.(csv|zip)$\">\nOrder Deny,Allow\nAllow from all\n</FilesMatch>" ); |
| 140 | |
| 141 | // Create empty index.html file |
| 142 | $index_path = NITROPACK_LOGS_DATA_DIR . '/index.html'; |
| 143 | file_put_contents( $index_path, "" ); |
| 144 | return true; |
| 145 | } else { |
| 146 | error_log( "Failed to create nitroopack logs directory: " . NITROPACK_LOGS_DATA_DIR ); |
| 147 | } |
| 148 | return false; |
| 149 | } |
| 150 | |
| 151 | /** |
| 152 | * Check if the data logs directory exists. |
| 153 | * |
| 154 | * @return bool True if the directory exists, false otherwise |
| 155 | */ |
| 156 | private function data_logs_dir_exists() { |
| 157 | return defined( "NITROPACK_LOGS_DATA_DIR" ) && is_dir( NITROPACK_LOGS_DATA_DIR ); |
| 158 | } |
| 159 | |
| 160 | /** |
| 161 | * Get the full path of the log file. |
| 162 | * |
| 163 | * @return string The log file path |
| 164 | */ |
| 165 | private function get_log_file_path() { |
| 166 | return nitropack_trailingslashit( NITROPACK_LOGS_DATA_DIR ) . $this->get_log_filename(); |
| 167 | } |
| 168 | |
| 169 | /** |
| 170 | * Get the log filename based on the current date. |
| 171 | * |
| 172 | * @return string The log filename |
| 173 | */ |
| 174 | private function get_log_filename() { |
| 175 | return date( 'Y-m-d' ) . '_nitropack_log.csv'; |
| 176 | } |
| 177 | |
| 178 | /** |
| 179 | * Get the maximum log file size. |
| 180 | * |
| 181 | * @return int The maximum log file size in bytes |
| 182 | */ |
| 183 | private function get_max_log_filesize() { |
| 184 | return apply_filters( 'nitropack_max_log_filesize', 20 * 1024 * 1024 ); // 20MB |
| 185 | } |
| 186 | |
| 187 | /** |
| 188 | * Rotate the log file by renaming it with a timestamp. |
| 189 | * |
| 190 | * @param string $log_file The path to the log file |
| 191 | * @return bool True if rotation was successful, false otherwise |
| 192 | */ |
| 193 | private function rotate_log_file( $log_file ) { |
| 194 | $rotated_file = $log_file . '.' . date( 'Y-m-d-H-i-s' ); |
| 195 | return rename( $log_file, $rotated_file ); |
| 196 | } |
| 197 | |
| 198 | /** |
| 199 | * Write content to the log file. |
| 200 | * |
| 201 | * @param string $log_file The path to the log file |
| 202 | * @param array $content The content to write to the log file |
| 203 | * @return void |
| 204 | */ |
| 205 | private function write_to_log_file( $log_file, $content ) { |
| 206 | $file_exists = file_exists( $log_file ); |
| 207 | $file_handle = fopen( $log_file, 'a' ); |
| 208 | |
| 209 | if ( $file_handle === false ) { |
| 210 | error_log( "Failed to open nitropack log file: $log_file" ); |
| 211 | return; |
| 212 | } |
| 213 | |
| 214 | //chmod($log_file, $this->get_chmod()); |
| 215 | |
| 216 | if ( ! $file_exists ) { |
| 217 | fputcsv( $file_handle, array_keys( $content ), $this->separator ); |
| 218 | } |
| 219 | |
| 220 | fputcsv( $file_handle, $content, $this->separator ); |
| 221 | fclose( $file_handle ); |
| 222 | } |
| 223 | } |