PluginProbe
Debug Log Manager – Conveniently Monitor and Inspect Errors / 1.4.0
Debug Log Manager – Conveniently Monitor and Inspect Errors v1.4.0
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.4.0, at classes/class-debug-log.php

480 lines 13.6 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, 'PHP Parse' ) !==false ) {
318 $error_type = 'PHP Parse';
319 $error_details = str_replace( "PHP Parse error: ", "", $error );
320 } elseif ( strpos( $error, 'WordPress database error' ) !==false ) {
321 $error_type = 'Database';
322 $error_details = str_replace( "WordPress database error ", "", $error );
323 } elseif ( strpos( $error, 'JavaScript Error' ) !==false ) {
324 $error_type = 'JavaScript';
325 $error_details = str_replace( "JavaScript Error: ", "", $error );
326 } else {
327 $error_type = 'Other';
328 $error_details = $error;
329 }
330
331 // https://www.php.net/manual/en/function.array-search.php#120784
332 if ( array_search( trim( $error_details ), array_column( $errors_master_list, 'details' ) ) === false ) {
333
334 $errors_master_list[] = array(
335 'type' => $error_type,
336 'details' => trim( $error_details ),
337 'occurrences' => array( $timestamp ),
338 );
339
340 } else {
341
342 $error_position = array_search( trim( $error_details ), array_column( $errors_master_list, 'details' ) ); // integer
343
344 array_push( $errors_master_list[$error_position]['occurrences'], $timestamp );
345
346 }
347
348 }
349
350 return json_encode( $errors_master_list );
351
352 }
353
354 /**
355 * Get the latest entries for auto-refresh feature
356 *
357 * @since 1.0.0
358 */
359 public function get_latest_entries() {
360
361 $errors_master_list = json_decode( $this->get_processed_entries(), true );
362
363 $n = 1;
364 $entries = array();
365
366 foreach ( $errors_master_list as $error ) {
367
368 $localized_timestamp = wp_date( 'M j, Y - H:i:s', strtotime( $error['occurrences'][0] ) ); // last occurrence
369 $occurrence_count = count( $error['occurrences'] );
370
371 $entry = array(
372 $n,
373 $error['type'],
374 $error['details'],
375 $localized_timestamp . '<br /><span class="dlm-faint">(' . $occurrence_count . ' occurrences logged)<span>',
376 );
377
378 $entries[] = $entry;
379
380 $n++;
381
382 }
383
384 $data = array(
385 'entries' => $entries,
386 );
387
388 echo json_encode( $data );
389
390 }
391
392 /**
393 * Get debug log in data table format
394 *
395 * @since 1.0.0
396 */
397 public function get_entries_datatable() {
398
399 ?>
400 <table id="debug-log" class="wp-list-table widefat striped">
401 <thead>
402 <tr>
403 <th class="debug-log-number">#</th>
404 <th class="debug-log-error-type">Error Type</th>
405 <th class="debug-log-error-details">Details</th>
406 <th class="debug-log-timestamp">Last Occurrence</th>
407 </tr>
408 </thead>
409 <tbody>
410 <?php
411
412 $errors_master_list = json_decode( $this->get_processed_entries(), true );
413
414 $n = 1;
415
416 foreach ( $errors_master_list as $error ) {
417
418 $localized_timestamp = wp_date( 'M j, Y - H:i:s', strtotime( $error['occurrences'][0] ) ); // last occurrence
419 $occurrence_count = count( $error['occurrences'] );
420 ?>
421
422 <tr>
423 <td><?php echo esc_html( $n ); ?></td>
424 <td><?php echo esc_html( $error['type'] ); ?></td>
425 <td><?php echo wp_kses( $error['details'], 'post' ); ?></td>
426 <td><?php echo esc_html( $localized_timestamp ); ?><br /><span class="dlm-faint">(<?php echo esc_html( $occurrence_count ); ?> occurrences logged)<span></td>
427 </tr>
428
429 <?php
430 $n++;
431
432 }
433
434 ?>
435 </tbody>
436 </table>
437 <?php
438
439 }
440
441 /**
442 * Clear log file
443 *
444 * @since 1.0.0
445 */
446 public function clear_log() {
447
448 $debug_log_file_path = get_option( 'debug_log_manager_file_path' );
449
450 file_put_contents( $debug_log_file_path, '' );
451
452 echo true;
453
454 }
455
456 /**
457 * Log javascript errors
458 *
459 * @since 1.4.0
460 */
461 public function log_js_errors() {
462
463 // Since we are using XHR for the js error logging, JSON data comes in via php://input
464 $request = json_decode(urldecode(file_get_contents('php://input')), true); // an array
465
466 // Verify error content and nonce and then log the JS error
467 // Source: https://plugins.svn.wordpress.org/lh-javascript-error-log/trunk/lh-javascript-error-log.php
468 if ( isset( $request['message'] ) && isset( $request['script'] ) && isset( $request['lineNo'] ) && isset( $request['columnNo'] ) && ! empty( $request['nonce'] ) && wp_verify_nonce( $request['nonce'], DLM_SLUG ) ) {
469
470 error_log( 'JavaScript Error: ' . $request['message'] . ' in ' . $request['script'] . ' on line ' . $request['lineNo'] . ' column ' . $request['columnNo'] . ' at ' . get_site_url() . $request['pageUrl'] );
471
472 } else {
473
474 wp_die();
475
476 }
477
478 }
479
480 }