-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy patherror-log-dashboard-widget.php
193 lines (150 loc) · 4.93 KB
/
error-log-dashboard-widget.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
<?php
/*
Plugin Name: Error Log Dashboard Widget
Plugin URI: https://github.com/Rarst/error-log-dashboard-widget
Description: Robust zero-configuration and low-memory WordPress plugin to keep an eye on error log.
Author: Andrey "Rarst" Savchenko
Author URI: http://www.rarst.net/
Version: 1.0.5
License: GPLv2 or later
Includes last_lines() function by phant0m, licensed under cc-wiki and GPLv2+
*/
Error_Log_Dashboard_Widget::on_load();
/**
* Main plugin's class.
*/
class Error_Log_Dashboard_Widget {
/**
* Set up logic on load.
*/
public static function on_load() {
add_action( 'admin_init', array( __CLASS__, 'admin_init' ) );
}
/**
* Set up logic on admin init.
*/
public static function admin_init() {
add_action( 'wp_dashboard_setup', array( __CLASS__, 'wp_dashboard_setup' ) );
}
/**
* Add dashboard widget.
*/
public static function wp_dashboard_setup() {
if ( current_user_can( apply_filters( 'error_log_widget_capability', 'manage_options' ) ) ) {
wp_add_dashboard_widget( 'error-log-widget', __( 'Error Log', 'error-log-widget' ), array( __CLASS__, 'widget_callback' ) );
}
}
/**
* Read log and render widget output.
*/
public static function widget_callback() {
$log_errors = ini_get( 'log_errors' );
if ( ! $log_errors ) {
echo '<p>' . __( 'Error logging disabled.', 'error-log-widget' ) . ' <a href="https://codex.wordpress.org/Editing_wp-config.php#Configure_Error_Logging">' . __( 'Configure error log', 'error-log-widget' ) . '</a></p>';
}
$error_log = ini_get( 'error_log' );
$logs = apply_filters( 'error_log_widget_logs', array( $error_log ) );
$count = apply_filters( 'error_log_widget_lines', 10 );
$lines = array();
foreach ( $logs as $log ) {
if ( is_readable( $log ) ) {
$lines = array_merge( $lines, self::last_lines( $log, $count ) );
}
}
$lines = array_map( 'trim', $lines );
$lines = array_filter( $lines );
if ( empty( $lines ) ) {
echo '<p>' . __( 'No errors found... Yet.', 'error-log-widget' ) . '</p>';
return;
}
foreach ( $lines as $key => $line ) {
if ( false !== strpos( $line, ']' ) ) {
list( $time, $error ) = explode( ']', $line, 2 );
} else {
list( $time, $error ) = array( '', $line );
}
$time = trim( $time, '[]' );
$error = trim( $error );
$lines[ $key ] = compact( 'time', 'error' );
}
if ( count( $logs ) > 1 ) {
uasort( $lines, array( __CLASS__, 'time_field_compare' ) );
$lines = array_slice( $lines, 0, $count );
}
echo '<table class="widefat">';
foreach ( $lines as $line ) {
$error = esc_html( $line['error'] );
$time = esc_html( $line['time'] );
if ( ! empty( $error ) ) {
echo( "<tr><td>{$time}</td><td>{$error}</td></tr>" );
}
}
echo '</table>';
}
/**
* Compare callback for freeform date/time strings in line items.
*
* @param array $a First value.
* @param array $b Second value.
*
* @return int
*/
public static function time_field_compare( $a, $b ) {
if ( $a['time'] === $b['time'] ) {
return 0;
}
return ( strtotime( $a['time'] ) > strtotime( $b['time'] ) ) ? - 1 : 1;
}
/**
* Reads lines from end of file. Memory-safe.
*
* @link http://stackoverflow.com/questions/6451232/php-reading-large-files-from-end/6451391#6451391
*
* @param string $path Filesystem path to the file.
* @param integer $line_count How many lines to read.
* @param integer $block_size Size of block to use for read.
*
* @return array
*/
public static function last_lines( $path, $line_count, $block_size = 512 ) {
$lines = array();
// we will always have a fragment of a non-complete line
// keep this in here till we have our next entire line.
$leftover = '';
$fh = fopen( $path, 'r' );
// go to the end of the file.
fseek( $fh, 0, SEEK_END );
do {
// need to know whether we can actually go back
// $block_size bytes.
$can_read = $block_size;
if ( ftell( $fh ) <= $block_size ) {
$can_read = ftell( $fh );
}
if ( empty( $can_read ) ) {
break;
}
// go back as many bytes as we can
// read them to $data and then move the file pointer
// back to where we were.
fseek( $fh, - $can_read, SEEK_CUR );
$data = fread( $fh, $can_read );
$data .= $leftover;
fseek( $fh, - $can_read, SEEK_CUR );
// split lines by \n. Then reverse them,
// now the last line is most likely not a complete
// line which is why we do not directly add it, but
// append it to the data read the next time.
$split_data = array_reverse( explode( "\n", $data ) );
$new_lines = array_slice( $split_data, 0, - 1 );
$lines = array_merge( $lines, $new_lines );
$leftover = $split_data[ count( $split_data ) - 1 ];
} while ( count( $lines ) < $line_count && ftell( $fh ) != 0 );
if ( ftell( $fh ) == 0 ) {
$lines[] = $leftover;
}
fclose( $fh );
// Usually, we will read too many lines, correct that here.
return array_slice( $lines, 0, $line_count );
}
}