engines
1 year ago
modules
1 year ago
queries
1 year ago
admin.php
2 years ago
api.php
1 year ago
core.php
1 year ago
init.php
2 years ago
logging.php
1 year ago
reply.php
1 year ago
rest.php
1 year ago
logging.php
191 lines
| 1 | <?php |
| 2 | |
| 3 | class Meow_MWAI_Logging { |
| 4 | private static $plugin_name; |
| 5 | private static $option_name; |
| 6 | private static $log_file_path; |
| 7 | private static $log_count = 0; |
| 8 | private static $rotate_check_frequency = 10; |
| 9 | private static $max_log_size = 5 * 1024 * 1024; // 5 MB |
| 10 | |
| 11 | /** |
| 12 | * Initializes the Meow Logging class. |
| 13 | * |
| 14 | * @param string $option_name The name of the option where logging settings are stored. |
| 15 | * @param string $plugin_name The name of the plugin, used in the PHP Error Logs. |
| 16 | */ |
| 17 | public static function init( $option_name, $plugin_name ) { |
| 18 | self::$plugin_name = $plugin_name; |
| 19 | self::$option_name = $option_name; |
| 20 | self::$log_file_path = self::get_logs_path(); |
| 21 | } |
| 22 | |
| 23 | private static function add( $message = null, $icon = '', $error_log = false ) { |
| 24 | // Log to a local file |
| 25 | if ( self::is_logging_enabled() && self::$log_file_path ) { |
| 26 | $fh = @fopen( self::$log_file_path, 'a' ); |
| 27 | if ( $fh ) { |
| 28 | $date = date( "Y-m-d H:i:s" ); |
| 29 | $message = self::sanitize_message( $message ); |
| 30 | if ( empty( $message ) ) { |
| 31 | fwrite( $fh, "\n" ); |
| 32 | } |
| 33 | else { |
| 34 | if ( !empty( $icon ) ) { |
| 35 | fwrite( $fh, "$date: $icon $message\n" ); |
| 36 | } |
| 37 | else { |
| 38 | fwrite( $fh, "$date: $message\n" ); |
| 39 | } |
| 40 | } |
| 41 | fclose( $fh ); |
| 42 | } else { |
| 43 | error_log( self::$plugin_name . ": Failed to open log file for writing." ); |
| 44 | } |
| 45 | } |
| 46 | // Log to the PHP Error Logs |
| 47 | if ( $error_log === true && !empty( $message ) ) { |
| 48 | error_log( self::$plugin_name . ": $message" ); |
| 49 | } |
| 50 | self::$log_count++; |
| 51 | if ( self::$log_count >= self::$rotate_check_frequency ) { |
| 52 | self::maybe_rotate_log(); |
| 53 | self::$log_count = 0; |
| 54 | } |
| 55 | } |
| 56 | |
| 57 | /** |
| 58 | * Logs an error message. |
| 59 | * It will also be logged to the PHP Error Logs. |
| 60 | * |
| 61 | * @param string $message The error message to log. |
| 62 | * @param string $icon An optional icon to prepend to the log message. Default is '❌'. |
| 63 | */ |
| 64 | public static function error( $message = null, $icon = '❌' ) { |
| 65 | self::add( $message, $icon, true ); |
| 66 | } |
| 67 | |
| 68 | /** |
| 69 | * Logs a warning message. |
| 70 | * |
| 71 | * @param string $message The warning message to log. |
| 72 | * @param string $icon An optional icon to prepend to the log message. Default is '⚠️'. |
| 73 | */ |
| 74 | public static function warn( $message = null, $icon = '🥲' ) { |
| 75 | self::add( $message, $icon ); |
| 76 | } |
| 77 | |
| 78 | /** |
| 79 | * Logs a general message. |
| 80 | * |
| 81 | * @param string $message The message to log. |
| 82 | * @param string $icon An optional icon to prepend to the log message. Default is empty. |
| 83 | */ |
| 84 | public static function log( $message = null, $icon = '' ) { |
| 85 | self::add( $message, $icon ); |
| 86 | } |
| 87 | |
| 88 | /** |
| 89 | * Logs a notice of a deprecated feature. |
| 90 | * |
| 91 | * @param string $message The message to log. |
| 92 | * @param string $icon An optional icon to prepend to the log message. Default is '🐞'. |
| 93 | */ |
| 94 | public static function deprecated( $message = null ) { |
| 95 | self::add( $message, '🚨', true ); |
| 96 | } |
| 97 | |
| 98 | private static function is_logging_enabled() { |
| 99 | $options = get_option( self::$option_name, null ); |
| 100 | if ( is_null( $options ) ) { |
| 101 | return false; |
| 102 | } |
| 103 | $module_devtools = empty( $options['module_devtools'] ) ? false : $options['module_devtools']; |
| 104 | $server_debug_mode = empty( $options['server_debug_mode'] ) ? false : $options['server_debug_mode']; |
| 105 | return $module_devtools && $server_debug_mode; |
| 106 | } |
| 107 | |
| 108 | private static function get_logs_path() { |
| 109 | $uploads_dir = wp_upload_dir(); |
| 110 | $uploads_dir_path = trailingslashit( $uploads_dir['basedir'] ); |
| 111 | $options = get_option( self::$option_name, null ); |
| 112 | if ( is_null( $options ) ) { |
| 113 | return null; |
| 114 | } |
| 115 | $path = empty( $options['logs_path'] ) ? null : $options['logs_path']; |
| 116 | if ( $path && file_exists( $path ) ) { |
| 117 | // Ensure the path is legal (within the uploads directory with the MWAI_PREFIX and log extension) |
| 118 | if ( strpos( $path, $uploads_dir_path ) !== 0 || |
| 119 | strpos( $path, MWAI_PREFIX ) === false || substr( $path, -4 ) !== '.log' ) { |
| 120 | $path = null; |
| 121 | } |
| 122 | else { |
| 123 | return $path; |
| 124 | } |
| 125 | } |
| 126 | if ( !$path ) { |
| 127 | $path = $uploads_dir_path . MWAI_PREFIX . "_" . self::random_ascii_chars() . ".log"; |
| 128 | if ( !file_exists( $path ) ) { |
| 129 | touch( $path ); |
| 130 | } |
| 131 | $options['logs_path'] = $path; |
| 132 | update_option( self::$option_name, $options ); |
| 133 | } |
| 134 | return $path; |
| 135 | } |
| 136 | |
| 137 | private static function random_ascii_chars( $length = 8 ) { |
| 138 | $characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; |
| 139 | $result = ''; |
| 140 | for ( $i = 0; $i < $length; $i++ ) { |
| 141 | $result .= $characters[rand( 0, strlen( $characters ) - 1 )]; |
| 142 | } |
| 143 | return $result; |
| 144 | } |
| 145 | |
| 146 | /** |
| 147 | * Clears the log file and resets the log path. |
| 148 | */ |
| 149 | public static function clear() { |
| 150 | if ( self::$log_file_path ) { |
| 151 | if ( substr( self::$log_file_path, -4 ) === '.log' ) { |
| 152 | unlink( self::$log_file_path ); |
| 153 | } |
| 154 | $options = get_option( self::$option_name, null ); |
| 155 | if ( $options ) { |
| 156 | $options['logs_path'] = null; |
| 157 | update_option( self::$option_name, $options ); |
| 158 | self::$log_file_path = null; |
| 159 | } |
| 160 | } |
| 161 | } |
| 162 | |
| 163 | /** |
| 164 | * Retrieves the contents of the log file. |
| 165 | * The lines are returned in reverse order (newest first). |
| 166 | */ |
| 167 | public static function get() { |
| 168 | if ( self::$log_file_path && file_exists( self::$log_file_path ) ) { |
| 169 | $content = file_get_contents( self::$log_file_path ); |
| 170 | $lines = explode( "\n", $content ); |
| 171 | $lines = array_filter( $lines ); |
| 172 | $lines = array_reverse( $lines ); |
| 173 | $content = implode( "\n", $lines ); |
| 174 | return $content; |
| 175 | } |
| 176 | return 'Empty log file.'; |
| 177 | } |
| 178 | |
| 179 | private static function maybe_rotate_log() { |
| 180 | if ( file_exists( self::$log_file_path ) && filesize( self::$log_file_path ) > self::$max_log_size ) { |
| 181 | $info = pathinfo( self::$log_file_path ); |
| 182 | $new_name = $info['dirname'] . '/' . $info['filename'] . '_' . date( 'Y-m-d_H-i-s' ) . '.' . $info['extension']; |
| 183 | rename( self::$log_file_path, $new_name ); |
| 184 | touch( self::$log_file_path ); |
| 185 | } |
| 186 | } |
| 187 | |
| 188 | private static function sanitize_message( $message ) { |
| 189 | return is_string( $message ) ? strip_tags( $message ) : $message; |
| 190 | } |
| 191 | } |