PluginProbe
Debug Log Manager – Conveniently Monitor and Inspect Errors / 1.3.3
Debug Log Manager – Conveniently Monitor and Inspect Errors v1.3.3
2.5.2 2.5.1 trunk 1.0.0 1.0.1 1.1.0 1.2.0 1.3.0 1.3.1 1.3.3 1.4.0 1.5.0 1.5.1 1.5.2 1.5.3 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.7.0 1.8.0 1.8.2 1.8.3 1.8.4 All 49 releases
debug-log-manager / classes / class-debug-log.php

class-debug-log.php in Debug Log Manager – Conveniently Monitor and Inspect Errors 1.3.3, at classes/class-debug-log.php

450 lines 12.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace DLM\Classes;
4
5 /**
6 * Class related to the debug log file and entries
7 *
8 * @since 1.0.0
9 */
10 class Debug_Log {
11
12 // The wp_config object
13 private $wp_config;
14
15 /**
16 * Class constructor
17 */
18 public function __construct() {
19
20 $this->wp_config = new WP_Config_Transformer; // already in the DLM\Classes namespace, so, no need to repeat
21
22 }
23
24 /**
25 * Get status of WP_DEBUG
26 *
27 * @since 1.0.0
28 */
29 public function get_status() {
30
31 $value = get_option( 'debug_log_manager' );
32 $status = $value['status'];
33 $date_time = wp_date( 'M j, Y - H:i:s', strtotime( $value['on'] ) );
34
35 return '<div id="debug-log-status" class="dlm-log-status"><strong>Error Logging</strong>: '. esc_html( ucfirst( $status ) ) .' on '. esc_html( $date_time ) .'<div id="dlm-log-toggle-hint"></div></div>';
36
37 }
38
39 /**
40 * Get log auto-refresh status
41 *
42 * @since 1.3.0
43 */
44 public function get_autorefresh_status() {
45
46 if ( false !== get_option( 'debug_log_manager_autorefresh' ) ) {
47
48 $autorefresh_status = get_option( 'debug_log_manager_autorefresh' );
49
50 } else {
51
52 $autorefresh_status = 'disabled';
53 update_option( 'debug_log_manager_autorefresh', $autorefresh_status, false );
54
55 }
56
57 return '<div id="debug-autorefresh-status" class="dlm-autorefresh-status"><strong>Auto-Refresh</strong>: ' . ucfirst( $autorefresh_status ) . '</div>';
58
59 }
60
61 /**
62 * Enable / disable WP_DEBUG
63 *
64 * @since 1.0.0
65 */
66 public function toggle_debugging() {
67
68 if ( isset( $_REQUEST ) ) {
69
70 $log_info = get_option( 'debug_log_manager' );
71 $dlm_debug_log_file_path = get_option( 'debug_log_manager_file_path' );
72
73 $date_time = wp_date( 'M j, Y - H:i:s' ); // Localized according to WP timezone settings
74 $date_time_for_option = date( 'M j, Y H:i:s' ); // in UTC
75
76 if ( 'disabled' == $log_info['status'] ) {
77
78 $option_value = array(
79 'status' => 'enabled',
80 'on' => $date_time_for_option,
81 );
82
83 update_option( 'debug_log_manager', $option_value, false );
84
85 // If WP_DEBUG_LOG is defined, copy content of existing debug.log file into the log file created by this plugin
86
87 if ( $this->wp_config->exists( 'constant', 'WP_DEBUG_LOG' ) ) {
88
89 $wp_debug_log_const = $this->wp_config->get_value( 'constant', 'WP_DEBUG_LOG' );
90
91 if ( in_array( $wp_debug_log_const, array( 'true', 'false' ), true ) ) {
92 $wp_debug_log_const = (bool) $wp_debug_log_const;
93 }
94
95 if ( is_bool( $wp_debug_log_const ) ) {
96 // WP_DEBUG_LOG is true or false. Log file is in default location of /wp-content/debug.log.
97
98 if ( is_file( WP_CONTENT_DIR . '/debug.log' ) ) {
99 // Copy existing debug log content to this plugin's debug log.
100 $default_debug_log_content = file_get_contents( WP_CONTENT_DIR . '/debug.log' );
101 file_put_contents( $dlm_debug_log_file_path, $default_debug_log_content );
102 unlink( realpath( WP_CONTENT_DIR . '/debug.log' ) ); // delete existing debug log
103 }
104
105 } elseif ( is_string( $wp_debug_log_const ) ) {
106 // WP_DEBUG_LOG is custom path to log file. Copy existing debug log content to this plugin's debug log.
107
108 if ( is_file( $wp_debug_log_const ) && ( $wp_debug_log_const != $dlm_debug_log_file_path ) ) {
109 $custom_debug_log_content = file_get_contents( $wp_debug_log_const );
110 file_put_contents( $dlm_debug_log_file_path, $custom_debug_log_content );
111 unlink( $wp_debug_log_const ); // delete existing debug log
112 }
113
114 }
115
116 $copy = true; // existing debug.log file's entries are copied to this plugin's debug.log file
117
118 } else {
119
120 $copy = false; // existing debug.log file's entries are NOT copied to this plugin's debug.log file
121
122 }
123
124 // Prepare entries from the debug log for the data table
125
126 $errors_master_list = json_decode( $this->get_processed_entries(), true );
127
128 $n = 1;
129 $entries = array();
130
131 foreach ( $errors_master_list as $error ) {
132
133 $localized_timestamp = wp_date( 'M j, Y - H:i:s', strtotime( $error['occurrences'][0] ) ); // last occurrence
134 $occurrence_count = count( $error['occurrences'] );
135
136 $entry = array(
137 $n,
138 $error['type'],
139 $error['details'],
140 $localized_timestamp . '<br /><span class="dlm-faint">(' . $occurrence_count . ' occurrences logged)<span>',
141 );
142
143 $entries[] = $entry;
144
145 $n++;
146
147 }
148
149 // Define Debug constants in wp-config.php
150
151 $options = array(
152 'add' => true, // Add the config if missing.
153 'raw' => true, // Display value in raw format without quotes.
154 'normalize' => false, // Normalize config output using WP Coding Standards.
155 );
156
157 $this->wp_config->update( 'constant', 'WP_DEBUG', 'true', $options );
158
159 $options = array(
160 'add' => true, // Add the config if missing.
161 'raw' => false, // Display value in raw format without quotes.
162 'normalize' => false, // Normalize config output using WP Coding Standards.
163 );
164
165 $this->wp_config->update( 'constant', 'WP_DEBUG_LOG', get_option( 'debug_log_manager_file_path' ), $options );
166
167 $options = array(
168 'add' => true, // Add the config if missing.
169 'raw' => true, // Display value in raw format without quotes.
170 'normalize' => false, // Normalize config output using WP Coding Standards.
171 );
172
173 $this->wp_config->update( 'constant', 'WP_DEBUG_DISPLAY', 'false', $options );
174
175 // Get the debug.log file size
176
177 $log_file_path = get_option( 'debug_log_manager_file_path' );
178 $log_file_shortpath = str_replace( sanitize_text_field( $_SERVER['DOCUMENT_ROOT'] ), "", $log_file_path );
179 $file_size = size_format( (int) filesize( $log_file_path ) );
180
181 // Assemble data to return
182
183 $data = array(
184 'status' => 'enabled',
185 'copy' => $copy,
186 'message' => '<strong>Error Logging</strong>: Enabled on ' . esc_html( $date_time ),
187 'entries' => $entries,
188 'size' => $file_size,
189 );
190
191 echo json_encode( $data );
192
193 } elseif ( 'enabled' == $log_info['status'] ) {
194
195 $option_value = array(
196 'status' => 'disabled',
197 'on' => $date_time_for_option,
198 );
199
200 update_option( 'debug_log_manager', $option_value, false );
201
202 // Remove Debug constants in wp-config.php
203
204 $this->wp_config->remove( 'constant', 'WP_DEBUG' );
205 $this->wp_config->remove( 'constant', 'WP_DEBUG_LOG' );
206 $this->wp_config->remove( 'constant', 'WP_DEBUG_DISPLAY' );
207
208 // Assemble data to return
209
210 $data = array(
211 'status' => 'disabled',
212 'copy' => false,
213 'message' => '<strong>Error Logging</strong>: Disabled on ' . esc_html( $date_time ),
214 'entries' => '',
215 'size' => '',
216 );
217
218 echo json_encode( $data );
219
220 } else {}
221
222 }
223
224 }
225
226 /**
227 * Toggle auto-refresh of entries table
228 *
229 * @since 1.3.0
230 */
231 public function toggle_autorefresh() {
232
233 if ( isset( $_REQUEST ) ) {
234
235 $autorefresh_status = get_option( 'debug_log_manager_autorefresh' );
236
237 if ( $autorefresh_status == 'disabled' ) {
238
239 update_option( 'debug_log_manager_autorefresh', 'enabled', false );
240
241 $data = array(
242 'status' => 'enabled',
243 'message' => '<strong>Auto-Refresh</strong>: Enabled',
244 );
245
246 echo json_encode( $data );
247
248 } elseif ( $autorefresh_status == 'enabled' ) {
249
250 update_option( 'debug_log_manager_autorefresh', 'disabled', false );
251
252 $data = array(
253 'status' => 'disabled',
254 'message' => '<strong>Auto-Refresh</strong>: Disabled',
255 );
256
257 echo json_encode( $data );
258
259 } else {}
260
261 }
262
263 }
264
265 /**
266 * Get the processed debug log data
267 *
268 * @return string $errors_master_list The processed error log entries
269 * @since 1.2.0
270 */
271 public function get_processed_entries() {
272
273 $debug_log_file_path = get_option( 'debug_log_manager_file_path' );
274
275 // Read the erros log file, reverse the order of the entries, prune to the latest 5000 entries
276 $log = file_get_contents( $debug_log_file_path );
277 $lines = explode("[", $log);
278
279 // Put back the missing '[' after explode operation
280 $prepended_lines = array();
281 foreach ( $lines as $line ) {
282 if ( !empty($line) ) {
283 $line = str_replace( "]", "]@@@", $line ); // add line break after time stamp
284 $line = str_replace( "Stack trace:", "<hr />Stack trace:", $line ); // add line break for stack trace section
285 $line = str_replace( "#", "<hr />#", $line ); // add line break on stack trace lines
286 $line = str_replace( "Argument <hr />#", "Argument #", $line ); // add line break on stack trace lines
287 $prepended_line = '[' . $line;
288 $prepended_lines[] = $prepended_line;
289 }
290 }
291
292 $lines_newest_first = array_reverse( $prepended_lines );
293 $latest_lines = array_slice( $lines_newest_first, 0, 50000 );
294
295 // Will hold error details types
296 $errors_master_list = array();
297
298 foreach( $latest_lines as $line ) {
299
300 $line = explode("@@@ ", $line);
301
302 $timestamp = str_replace( [ "[", "]" ], "", $line[0] );
303 $error = $line[1];
304
305 if ( strpos( $error, 'PHP Fatal' ) !==false ) {
306 $error_type = 'PHP Fatal';
307 $error_details = str_replace( "PHP Fatal: ", "", $error );
308 } elseif ( strpos( $error, 'PHP Warning' ) !==false ) {
309 $error_type = 'PHP Warning';
310 $error_details = str_replace( "PHP Warning: ", "", $error );
311 } elseif ( strpos( $error, 'PHP Notice' ) !==false ) {
312 $error_type = 'PHP Notice';
313 $error_details = str_replace( "PHP Notice: ", "", $error );
314 } elseif ( strpos( $error, 'PHP Deprecated' ) !==false ) {
315 $error_type = 'PHP Deprecated';
316 $error_details = str_replace( "PHP Deprecated: ", "", $error );
317 } elseif ( strpos( $error, 'WordPress database error' ) !==false ) {
318 $error_type = 'WP DB error';
319 $error_details = str_replace( "WordPress database error ", "", $error );
320 } else {
321 $error_type = 'Other';
322 $error_details = $error;
323 }
324
325 // https://www.php.net/manual/en/function.array-search.php#120784
326 if ( array_search( trim( $error_details ), array_column( $errors_master_list, 'details' ) ) === false ) {
327
328 $errors_master_list[] = array(
329 'type' => $error_type,
330 'details' => trim( $error_details ),
331 'occurrences' => array( $timestamp ),
332 );
333
334 } else {
335
336 $error_position = array_search( trim( $error_details ), array_column( $errors_master_list, 'details' ) ); // integer
337
338 array_push( $errors_master_list[$error_position]['occurrences'], $timestamp );
339
340 }
341
342 }
343
344 return json_encode( $errors_master_list );
345
346 }
347
348 /**
349 * Get the latest entries for auto-refresh feature
350 *
351 * @since 1.0.0
352 */
353 public function get_latest_entries() {
354
355 $errors_master_list = json_decode( $this->get_processed_entries(), true );
356
357 $n = 1;
358 $entries = array();
359
360 foreach ( $errors_master_list as $error ) {
361
362 $localized_timestamp = wp_date( 'M j, Y - H:i:s', strtotime( $error['occurrences'][0] ) ); // last occurrence
363 $occurrence_count = count( $error['occurrences'] );
364
365 $entry = array(
366 $n,
367 $error['type'],
368 $error['details'],
369 $localized_timestamp . '<br /><span class="dlm-faint">(' . $occurrence_count . ' occurrences logged)<span>',
370 );
371
372 $entries[] = $entry;
373
374 $n++;
375
376 }
377
378 $data = array(
379 'entries' => $entries,
380 );
381
382 echo json_encode( $data );
383
384 }
385
386 /**
387 * Get debug log in data table format
388 *
389 * @since 1.0.0
390 */
391 public function get_entries_datatable() {
392
393 ?>
394 <table id="debug-log" class="wp-list-table widefat striped">
395 <thead>
396 <tr>
397 <th class="debug-log-number">#</th>
398 <th class="debug-log-error-type">Error Type</th>
399 <th class="debug-log-error-details">Details</th>
400 <th class="debug-log-timestamp">Last Occurrence</th>
401 </tr>
402 </thead>
403 <tbody>
404 <?php
405
406 $errors_master_list = json_decode( $this->get_processed_entries(), true );
407
408 $n = 1;
409
410 foreach ( $errors_master_list as $error ) {
411
412 $localized_timestamp = wp_date( 'M j, Y - H:i:s', strtotime( $error['occurrences'][0] ) ); // last occurrence
413 $occurrence_count = count( $error['occurrences'] );
414 ?>
415
416 <tr>
417 <td><?php echo esc_html( $n ); ?></td>
418 <td><?php echo esc_html( $error['type'] ); ?></td>
419 <td><?php echo wp_kses( $error['details'], 'post' ); ?></td>
420 <td><?php echo esc_html( $localized_timestamp ); ?><br /><span class="dlm-faint">(<?php echo esc_html( $occurrence_count ); ?> occurrences logged)<span></td>
421 </tr>
422
423 <?php
424 $n++;
425
426 }
427
428 ?>
429 </tbody>
430 </table>
431 <?php
432
433 }
434
435 /**
436 * Clear log file
437 *
438 * @since 1.0.0
439 */
440 public function clear_log() {
441
442 $debug_log_file_path = get_option( 'debug_log_manager_file_path' );
443
444 file_put_contents( $debug_log_file_path, '' );
445
446 echo true;
447
448 }
449
450 }