PluginProbe ʕ •ᴥ•ʔ
AI Engine – The Chatbot, AI Framework & MCP for WordPress / 3.1.2
AI Engine – The Chatbot, AI Framework & MCP for WordPress v3.1.2
3.5.7 3.5.6 3.5.5 3.5.4 3.5.3 3.5.2 3.5.1 3.5.0 3.4.9 3.4.8 3.4.7 0.2.1 1.6.91 0.2.2 1.6.92 0.2.3 1.6.93 0.2.4 1.6.94 0.2.5 1.6.95 0.2.6 1.6.96 0.2.7 1.6.97 0.2.8 1.6.98 0.2.9 1.6.99 0.3.0 1.7.0 0.3.1 1.7.1 0.3.2 1.7.2 0.3.3 1.7.3 0.3.4 1.7.4 0.3.5 1.7.5 0.3.6 1.7.6 0.4.0 1.7.7 0.4.1 1.7.8 0.4.2 1.7.9 0.4.3 1.8.0 0.4.4 1.8.1 0.4.5 1.8.2 0.4.6 1.8.3 0.4.7 1.8.4 0.4.8 1.8.5 0.4.9 1.8.6 0.5.0 1.8.7 0.5.1 1.8.8 0.5.2 1.8.9 0.5.3 1.9.0 0.5.4 1.9.1 0.5.5 1.9.2 0.5.6 1.9.3 0.5.7 1.9.4 0.5.8 1.9.5 0.5.9 1.9.6 0.6.0 1.9.7 0.6.1 1.9.8 0.6.2 1.9.81 0.6.3 1.9.82 0.6.4 1.9.83 0.6.5 1.9.84 0.6.6 1.9.85 0.6.7 1.9.86 0.6.8 1.9.87 0.6.9 1.9.88 0.7.0 1.9.89 0.7.1 1.9.90 0.7.2 1.9.91 0.7.3 1.9.92 0.7.4 1.9.93 0.7.5 1.9.94 0.7.6 1.9.95 0.7.7 1.9.96 0.7.8 1.9.97 0.7.9 1.9.98 0.8.0 1.9.99 0.8.1 2.0.0 0.8.2 2.0.1 0.8.3 2.0.2 0.8.4 2.0.3 0.8.5 2.0.4 0.8.6 2.0.5 0.8.7 2.0.6 0.8.8 2.0.7 0.8.9 2.0.8 0.9.0 2.0.9 0.9.2 2.1.0 0.9.3 2.1.1 0.9.4 2.1.2 0.9.5 2.1.3 0.9.6 2.1.4 0.9.7 2.1.5 0.9.8 2.1.6 0.9.81 2.1.7 0.9.82 2.1.8 0.9.83 2.1.9 0.9.84 2.2.0 0.9.85 2.2.1 0.9.86 2.2.2 0.9.87 2.2.3 0.9.88 2.2.4 0.9.89 2.2.5 0.9.9 2.2.51 0.9.91 2.2.52 0.9.92 2.2.53 0.9.93 2.2.54 0.9.94 2.2.56 0.9.95 2.2.57 0.9.96 2.2.6 0.9.97 2.2.60 0.9.98 2.2.61 0.9.99 2.2.62 1.0.0 2.2.63 1.0.01 2.2.70 1.0.1 2.2.80 1.0.2 2.2.81 1.0.3 2.2.90 1.0.4 2.2.91 1.0.5 2.2.92 1.0.6 2.2.93 1.0.7 2.2.94 1.0.8 2.2.95 1.0.9 2.3.0 1.1.0 2.3.1 1.1.1 2.3.2 1.1.2 2.3.3 1.1.3 2.3.4 1.1.4 2.3.5 1.1.5 2.3.6 1.1.6 2.3.7 1.1.7 2.3.8 1.1.8 2.3.9 1.1.9 2.4.0 1.2.0 2.4.1 1.2.1 2.4.2 1.2.2 2.4.3 1.2.21 2.4.4 1.2.3 2.4.5 1.2.30 2.4.6 1.3.0 2.4.7 1.3.1 2.4.8 1.3.2 2.4.9 1.3.3 2.5.0 1.3.31 2.5.1 1.3.32 2.5.2 1.3.33 2.5.3 1.3.34 2.5.4 1.3.35 2.5.5 1.3.36 2.5.6 1.3.37 2.5.7 1.3.38 2.5.8 1.3.39 2.5.9 1.3.40 2.6.0 1.3.41 2.6.1 1.3.42 2.6.2 1.3.43 2.6.3 1.3.44 2.6.5 1.3.45 2.6.6 1.3.46 2.6.7 1.3.47 2.6.8 1.3.48 2.6.9 1.3.49 2.7.0 1.3.50 2.7.1 1.3.51 2.7.2 1.3.52 2.7.3 1.3.53 2.7.4 1.3.54 2.7.5 1.3.56 2.7.6 1.3.57 2.7.7 1.3.58 2.7.8 1.3.59 2.7.9 1.3.60 2.8.0 1.3.61 2.8.1 1.3.62 2.8.2 1.3.63 2.8.3 1.3.64 2.8.4 1.3.65 2.8.5 1.3.66 2.8.6 1.3.67 2.8.7 1.3.68 2.8.8 1.3.69 2.8.9 1.3.70 2.9.0 1.3.71 2.9.1 1.3.72 2.9.2 1.3.73 2.9.3 1.3.74 2.9.4 1.3.75 2.9.5 1.3.76 2.9.6 1.3.77 2.9.7 1.3.78 2.9.8 1.3.79 2.9.9 1.3.80 3.0.0 1.3.81 3.0.1 1.3.82 3.0.2 1.3.83 3.0.3 1.3.84 3.0.4 1.3.85 3.0.5 1.3.86 3.0.6 1.3.87 3.0.7 1.3.88 3.0.8 1.3.89 3.0.9 1.3.90 3.1.0 1.3.91 3.1.1 1.3.92 3.1.2 1.3.93 3.1.3 1.3.94 3.1.4 1.3.95 3.1.5 1.3.96 3.1.6 1.3.97 3.1.7 1.3.98 3.1.8 1.3.99 3.1.9 1.4.0 3.2.0 1.4.1 3.2.1 1.4.2 3.2.2 1.4.3 3.2.3 1.4.4 3.2.4 1.4.5 3.2.5 1.4.6 3.2.6 1.4.7 3.2.7 1.4.8 3.2.8 1.4.9 3.2.9 1.5.0 3.3.0 1.5.1 3.3.1 1.5.2 3.3.2 1.5.3 3.3.3 1.5.4 3.3.4 1.5.5 3.3.5 1.5.6 3.3.6 1.5.7 3.3.7 1.5.8 3.3.8 1.5.9 3.3.9 1.6.0 3.4.0 1.6.1 3.4.1 1.6.2 3.4.2 1.6.3 3.4.3 1.6.5 3.4.4 1.6.51 3.4.5 1.6.52 3.4.6 1.6.53 1.6.54 1.6.55 1.6.56 1.6.57 1.6.58 1.6.59 1.6.60 1.6.61 1.6.62 1.6.63 1.6.64 1.6.65 1.6.66 1.6.67 1.6.68 trunk 1.6.69 0.0.1 1.6.70 0.0.2 1.6.71 0.0.3 1.6.72 0.0.4 1.6.73 0.0.5 1.6.74 0.0.6 1.6.75 0.0.7 1.6.76 0.0.8 1.6.77 0.0.9 1.6.78 0.1.0 1.6.79 0.1.1 1.6.81 0.1.2 1.6.82 0.1.3 1.6.83 0.1.4 1.6.84 0.1.5 1.6.85 0.1.6 1.6.86 0.1.7 1.6.87 0.1.8 1.6.88 0.1.9 1.6.89 0.2.0 1.6.90
ai-engine / classes / logging.php
ai-engine / classes Last commit date
data 11 months ago engines 8 months ago exceptions 11 months ago modules 8 months ago query 8 months ago rest 8 months ago services 8 months ago admin.php 8 months ago api.php 8 months ago core.php 8 months ago discussion.php 11 months ago event.php 11 months ago init.php 8 months ago logging.php 11 months ago reply.php 8 months ago rest.php 8 months ago
logging.php
281 lines
1 <?php
2
3 /**
4 * Class Meow_MWAI_Logging
5 *
6 * A logging utility that uses the WordPress Filesystem API for storage,
7 * with fallback to PHP error_log when necessary.
8 */
9 class Meow_MWAI_Logging {
10 private static $plugin_name;
11 private static $option_name;
12 private static $log_file_path;
13 private static $fs;
14 private static $log_count = 0;
15 private static $rotate_check_frequency = 10;
16 private static $max_log_size = 5 * 1024 * 1024; // 5 MB
17
18 /**
19 * Initialize the logger.
20 *
21 * @param string $option_name Option key for settings.
22 * @param string $plugin_name Plugin identifier for error log prefix.
23 */
24 public static function init( $option_name, $plugin_name ) {
25 self::$plugin_name = $plugin_name;
26 self::$option_name = $option_name;
27
28 // Attempt to use WP_Filesystem only if the 'direct' method is available.
29 if ( !function_exists( 'WP_Filesystem' ) ) {
30 require_once ABSPATH . 'wp-admin/includes/file.php';
31 }
32
33 if ( function_exists( 'get_filesystem_method' ) && 'direct' === get_filesystem_method() ) {
34 // If 'direct' is allowed, try to initialize the filesystem (no credentials prompt).
35 if ( WP_Filesystem() ) {
36 global $wp_filesystem;
37 self::$fs = $wp_filesystem;
38 }
39 else {
40 // Could not initialize
41 error_log( self::$plugin_name . ': Could not init direct WP_Filesystem. Falling back to error_log only.' );
42 self::$fs = null;
43 }
44 }
45 else {
46 // Not 'direct' or not available; skip filesystem usage
47 self::$fs = null;
48 }
49
50 // Attempt to determine or create the log file path
51 self::$log_file_path = self::get_logs_path( true );
52 }
53
54 /**
55 * Determine or create the log file path using WP_Filesystem.
56 *
57 * @param bool $create Whether to generate a new file if none exists.
58 * @return string|false Path to log file or false if unavailable.
59 */
60 private static function get_logs_path( $create = false ) {
61 $options = get_option( self::$option_name, null );
62 if ( is_null( $options ) ) {
63 return null;
64 }
65
66 // If we don't have a filesystem reference, we can't create or write a file
67 if ( empty( self::$fs ) ) {
68 return null;
69 }
70
71 $path = empty( $options['logs_path'] ) ? '' : $options['logs_path'];
72
73 if ( $path && self::$fs->exists( $path ) ) {
74 return $path;
75 }
76
77 if ( !$create ) {
78 return null;
79 }
80
81 $uploads = wp_upload_dir();
82 $base_dir = trailingslashit( $uploads['basedir'] );
83
84 if ( !self::$fs->is_dir( $base_dir ) ) {
85 self::$fs->mkdir( $base_dir );
86 }
87
88 // Adjust MWAI_PREFIX to whatever your actual constant or value is
89 $filename = MWAI_PREFIX . '_' . self::random_ascii_chars() . '.log';
90 $new_path = $base_dir . $filename;
91
92 self::$fs->put_contents( $new_path, '', FS_CHMOD_FILE );
93
94 $options['logs_path'] = $new_path;
95 update_option( self::$option_name, $options );
96
97 return $new_path;
98 }
99
100 /**
101 * Check if logging is enabled via plugin options and FS availability.
102 *
103 * @return bool
104 */
105 private static function is_logging_enabled() {
106 $options = get_option( self::$option_name, null );
107 if ( is_null( $options ) ) {
108 return false;
109 }
110
111 $module_devtools = empty( $options['module_devtools'] ) ? false : $options['module_devtools'];
112 $server_debug_mode = empty( $options['server_debug_mode'] ) ? false : $options['server_debug_mode'];
113
114 return ( $module_devtools && $server_debug_mode && !empty( self::$fs ) );
115 }
116
117 /**
118 * Internal log writer. Appends to file and/or error_log.
119 */
120 private static function add( $message = null, $icon = '', $error_log = false ) {
121 $date = date( 'Y-m-d H:i:s' );
122 $message = is_string( $message ) ? strip_tags( $message ) : $message;
123
124 if ( empty( $message ) ) {
125 $entry = "\n";
126 }
127 else if ( !empty( $icon ) ) {
128 $entry = "$date: $icon $message\n";
129 }
130 else {
131 $entry = "$date: $message\n";
132 }
133
134 // Write to file if enabled and if a log file path exists
135 if ( self::is_logging_enabled() && self::$log_file_path ) {
136 if ( self::$fs->exists( self::$log_file_path ) ) {
137 $current = self::$fs->get_contents( self::$log_file_path );
138 self::$fs->put_contents( self::$log_file_path, $current . $entry, FS_CHMOD_FILE );
139 }
140 else {
141 self::$fs->put_contents( self::$log_file_path, $entry, FS_CHMOD_FILE );
142 }
143 }
144
145 // Always send to PHP error_log if $error_log is true
146 if ( $error_log && !empty( $message ) ) {
147 \error_log( self::$plugin_name . ": $message" );
148 }
149
150 self::$log_count++;
151
152 if ( self::$log_count >= self::$rotate_check_frequency ) {
153 self::maybe_rotate_log();
154 self::$log_count = 0;
155 }
156 }
157
158 /**
159 * Logs a general message.
160 *
161 * @param string $message The message to log.
162 * @param string $icon Optional icon to prepend.
163 */
164 public static function log( $message = null, $icon = '' ) {
165 self::add( $message, $icon );
166 }
167
168 /**
169 * Logs a warning message.
170 *
171 * @param string $message The warning message to log.
172 * @param string $icon Optional icon to prepend (default ⚠️).
173 */
174 public static function warn( $message = null, $icon = '⚠️' ) {
175 self::add( $message, $icon );
176 }
177
178 /**
179 * Logs an error message and sends to PHP error_log.
180 *
181 * @param string $message The error message to log.
182 * @param string $icon Optional icon to prepend (default ❌).
183 */
184 public static function error( $message = null, $icon = '' ) {
185 self::add( $message, $icon, true );
186 }
187
188 /**
189 * Logs a deprecated feature notice.
190 *
191 * @param string $message The message to log.
192 */
193 public static function deprecated( $message = null ) {
194 self::add( $message, '🚨', true );
195 }
196
197 /**
198 * Clears the log file and resets the option.
199 */
200 public static function clear() {
201 if ( self::$fs && self::$log_file_path && self::$fs->exists( self::$log_file_path ) ) {
202 self::$fs->delete( self::$log_file_path );
203 $options = get_option( self::$option_name, null );
204 $options['logs_path'] = '';
205 update_option( self::$option_name, $options );
206 self::$log_file_path = '';
207 }
208 }
209
210 /**
211 * Retrieves the log contents in reverse order (newest first).
212 *
213 * @return string
214 */
215 public static function get() {
216 if ( self::$fs && self::$log_file_path && self::$fs->exists( self::$log_file_path ) ) {
217 $content = self::$fs->get_contents( self::$log_file_path );
218 $lines = explode( "\n", $content );
219 $lines = array_filter( $lines );
220 $lines = array_reverse( $lines );
221
222 return implode( "\n", $lines );
223 }
224
225 return 'Empty log file.';
226 }
227
228 /**
229 * Checks file size and rotates if exceeding maximum.
230 */
231 private static function maybe_rotate_log() {
232 if ( empty( self::$fs ) || empty( self::$log_file_path ) ) {
233 return;
234 }
235
236 if ( self::$fs->exists( self::$log_file_path ) ) {
237 $size = self::$fs->size( self::$log_file_path );
238
239 if ( $size > self::$max_log_size ) {
240 $info = pathinfo( self::$log_file_path );
241 $archived = $info['dirname'] . '/' . $info['filename'] . '_' . date( 'Y-m-d_H-i-s' ) . '.' . $info['extension'];
242
243 self::$fs->move( self::$log_file_path, $archived, true );
244 self::$fs->put_contents( self::$log_file_path, '', FS_CHMOD_FILE );
245 }
246 }
247 }
248
249 /**
250 * Generates a random ASCII string.
251 *
252 * @param int $length String length.
253 * @return string
254 */
255 private static function random_ascii_chars( $length = 8 ) {
256 $characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
257 $result = '';
258
259 for ( $i = 0; $i < $length; $i++ ) {
260 $result .= $characters[ mt_rand( 0, strlen( $characters ) - 1 ) ];
261 }
262
263 return $result;
264 }
265
266 /**
267 * Shortens a string to a specified length, adding ellipsis if needed.
268 *
269 * @param int $length String length.
270 * @return string
271 */
272 public static function shorten( $string, $length = 50 ) {
273 if ( strlen( $string ) > $length ) {
274 $string = rtrim( $string, " \t\n\r\0\x0B,." );
275 $string = substr( $string, 0, $length - 3 ) . '...';
276 }
277
278 return $string;
279 }
280 }
281