PluginProbe
Document Gallery / 3.5.3
Document Gallery v3.5.3
trunk 0.8 0.8.5 1.0 1.0.1 1.0.2 1.0.3 1.0.4 1.1 1.2 1.2.1 1.3 1.3.1 1.4 1.4.1 1.4.2 1.4.3 2.0 2.0.1 2.0.10 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 All 94 releases
document-gallery / inc / class-logger.php

class-logger.php in Document Gallery 3.5.3, at inc/class-logger.php

360 lines 9.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 defined( 'WPINC' ) OR exit;
3
4 /**
5 * Encapsulates the logic required to maintain and read log files.
6 */
7 class DG_Logger {
8
9 /**
10 * @var string Name of the log purge action.
11 */
12 const PurgeLogsAction = 'document-gallery_purge-logs';
13
14 /**
15 * Appends DG log file if logging is enabled.
16 *
17 * @param int $level The level of serverity (should be passed using DG_LogLevel consts).
18 * @param string $entry Value to be logged.
19 * @param bool $stacktrace Whether to include full stack trace.
20 * @param bool $force Whether to ignore logging flag and log no matter what.
21 * Only should be used when the user *must* know about something.
22 */
23 public static function writeLog( $level, $entry, $stacktrace = false, $force = false ) {
24 if ( $force || self::logEnabled() ) {
25 $fp = fopen( self::getLogFileName(), 'a' );
26 if ( false !== $fp ) {
27 $fields = array( time(), $level, $entry );
28
29 $trace = debug_backtrace( false );
30 if ( $stacktrace ) {
31 unset( $trace[0] );
32 $fields[] = self::getStackTraceString( $trace );
33 } else {
34 // Ignore first item from backtrace as it's this function which is redundant.
35 $caller = $trace[1];
36
37 $class = isset( $caller['class'] ) ? $caller['class'] : '';
38 $type = isset( $caller['type'] ) ? $caller['type'] : '';
39 $caller = $class . $type . $caller['function'];
40
41 $fields[2] = '(' . $caller . ') ' . $fields[2];
42 }
43
44 fputcsv( $fp, $fields );
45 fclose( $fp );
46 } // TODO: else
47 }
48 }
49
50 /**
51 * Reads the current blog's log file, placing the values in to a 2-dimensional array.
52 *
53 * @param int $skip How many lines to skip before returning rows.
54 * @param int $limit Max number of lines to read.
55 *
56 * @return array|null The rows from the log file or null if failed to open log.
57 */
58 public static function readLog( $skip = 0, $limit = PHP_INT_MAX ) {
59 $ret = null;
60 $fp = @fopen( self::getLogFileName(), 'r' );
61
62 if ( $fp !== false ) {
63 $ret = array();
64 while ( count( $ret ) < $limit && false !== ( $fields = fgetcsv( $fp ) ) ) {
65 if ( $skip > 0 ) {
66 $skip --;
67 continue;
68 }
69
70 if ( ! is_null( $fields ) ) {
71 $ret[] = $fields;
72 }
73 }
74
75 @fclose( $fp );
76 }
77
78 return $ret;
79 }
80
81 /**
82 * Clears the log file for the active blog.
83 */
84 public static function clearLog() {
85 // we don't care if the file actually exists -- it won't when we're done
86 @unlink( self::getLogFileName() );
87 }
88
89 /**
90 * Clears the log file for all blogs.
91 */
92 public static function clearLogs() {
93 // we don't care if the files actually exist -- they won't when we're done
94 foreach ( DG_Util::getBlogIds() as $id ) {
95 @unlink( self::getLogFileName( $id ) );
96 }
97 }
98
99 /**
100 * Truncates all blog logs to the current purge interval.
101 *
102 * TODO: This is a memory hog. Consider switching to stream filter.
103 */
104 public static function purgeExpiredEntries() {
105 self::writeLog( DG_LogLevel::Detail, 'Beginning scheduled log file purge.' );
106
107 $blogs = array( null );
108 if ( is_multisite() ) {
109 $blogs = DG_Util::getBlogIds();
110 }
111
112 // truncate each blog's log file
113 $time = time();
114 foreach ( $blogs as $blog ) {
115 $blog_num = ! is_null( $blog ) ? $blog : get_current_blog_id();
116 $options = self::getOptions( $blog );
117 $purge_time = $time - $options['purge_interval'] * DAY_IN_SECONDS;
118
119 // purging is disabled for this blog
120 if ( $purge_time >= $time ) {
121 continue;
122 }
123
124 // do purge for this blog
125 $file = self::getLogFileName( $blog_num );
126 if ( file_exists( $file ) ) {
127 $fp = @fopen( $file, 'r' );
128
129 if ( $fp !== false ) {
130 $truncate = false;
131 $offset = 0;
132
133 // find the first non-expired entry
134 while ( ( $fields = fgetcsv( $fp ) ) !== false ) {
135 if ( ! is_null( $fields ) && intval( $fields[0] ) >= $purge_time ) {
136 // we've reached the recent entries -- nothing beyond here will be removed
137 break;
138 }
139
140 $offset = @ftell( $fp );
141 if ( false === $offset ) {
142 break;
143 }
144
145 $truncate = true;
146 }
147
148 @fclose( $fp );
149
150 // if any expired entries exist -- remove them from the file
151 if ( $truncate ) {
152 self::writeLog( DG_LogLevel::Detail, "Purging log entries for blog #$blog_num." );
153 $data = file_get_contents( $file, false, null, $offset );
154 file_put_contents( $file, $data, LOCK_EX );
155 }
156 }
157 }
158 }
159 }
160
161 /**
162 * Generally not necessary to call external to this class -- only use if generating
163 * log entry will take significant resources and you want to avoid this operation
164 * if it will not actually be logged.
165 *
166 * @return bool Whether debug logging is currently enabled.
167 */
168 public static function logEnabled() {
169 $options = self::getOptions();
170
171 return $options['enabled'];
172 }
173
174 /**
175 * Gets logging options.
176 *
177 * @param int $blog ID of the blog to be retrieved in multisite env.
178 *
179 * @return array Logger options for the blog.
180 */
181 public static function getOptions( $blog = null ) {
182 $options = DocumentGallery::getOptions( $blog );
183
184 return $options['logging'];
185 }
186
187 /**
188 * @param $id int The ID of the blog to retrieve log file name for. Defaults to current blog.
189 *
190 * @return string Full path to log file for current blog.
191 */
192 private static function getLogFileName( $id = null ) {
193 $id = ! is_null( $id ) ? $id : get_current_blog_id();
194
195 return DG_PATH . 'log/' . $id . '.log';
196 }
197
198 /**
199 * @param array $trace Array containing stack trace to be converted to string.
200 *
201 * @return string The stack trace in human-readable form.
202 */
203 private static function getStackTraceString( $trace ) {
204 $trace_str = '';
205 $i = 1;
206
207 foreach ( $trace as $node ) {
208 $trace_str .= "#$i ";
209
210 $file = '';
211 if ( isset( $node['file'] ) ) {
212 // convert to relative path from WP root
213 $file = str_replace( ABSPATH, '', $node['file'] );
214 }
215
216 if ( isset( $node['line'] ) ) {
217 $file .= "({$node['line']})";
218 }
219
220 if ( $file ) {
221 $trace_str .= "$file: ";
222 }
223
224 if ( isset( $node['class'] ) ) {
225 $trace_str .= "{$node['class']}{$node['type']}";
226 }
227
228 if ( isset( $node['function'] ) ) {
229 // only include args for first item in stack trace
230 $args = '';
231 if ( 1 === $i && isset( $node['args'] ) ) {
232 $args = implode( ', ', array_map( array( __CLASS__, 'print_r' ), $node['args'] ) );
233 }
234
235 $trace_str .= "{$node['function']}($args)" . PHP_EOL;
236 }
237 $i ++;
238 }
239
240 return $trace_str;
241 }
242
243 /**
244 * Wraps print_r passing true for the return argument.
245 *
246 * @param mixed $v Value to be printed.
247 *
248 * @return string Printed value.
249 */
250 private static function print_r( $v ) {
251 return preg_replace( '/\s+/', ' ', print_r( $v, true ) );
252 }
253
254 /**
255 * Blocks instantiation. All functions are static.
256 */
257 private function __construct() {
258
259 }
260 }
261
262 /**
263 * LogLevel acts as an enumeration of all possible log levels.
264 */
265 class DG_LogLevel {
266 /**
267 * @var int Log level for anything that doesn't indicate a problem.
268 */
269 const Detail = 0;
270
271 /**
272 * @var int Log level for anything that is a minor issue.
273 */
274 const Warning = 1;
275
276 /**
277 * @var int Log level for when something went wrong.
278 */
279 const Error = 2;
280
281 /**
282 * @var ReflectionClass Backs the getter.
283 */
284 private static $ref = null;
285
286 /**
287 * @return ReflectionClass Instance of reflection class for this class.
288 */
289 private static function getReflectionClass() {
290 if ( is_null( self::$ref ) ) {
291 self::$ref = new ReflectionClass( __CLASS__ );
292 }
293
294 return self::$ref;
295 }
296
297 /**
298 * @var array Backs the getter.
299 */
300 private static $levels = null;
301
302 /**
303 * @return array Associative array containing all log level names mapped to their int value.
304 */
305 public static function getLogLevels() {
306 if ( is_null( self::$levels ) ) {
307 $ref = self::getReflectionClass();
308 self::$levels = $ref->getConstants();
309 }
310
311 return self::$levels;
312 }
313
314 /**
315 * @param string $name Name to be checked for validity.
316 *
317 * @return bool Whether given name represents valid log level.
318 */
319 public static function isValidName( $name ) {
320 return array_key_exists( $name, self::getLogLevels() );
321 }
322
323 /**
324 * @param int $value Value to be checked for validity.
325 *
326 * @return bool Whether given value represents valid log level.
327 */
328 public static function isValidValue( $value ) {
329 return ( false !== array_search( $value, self::getLogLevels() ) );
330 }
331
332 /**
333 * @param string $name The name for which to retrieve a value.
334 *
335 * @return int|null The value associated with the given name.
336 */
337 public static function getValueByName( $name ) {
338 $levels = self::getLogLevels();
339
340 return array_key_exists( $name, self::getLogLevels() ) ? $levels[ $name ] : null;
341 }
342
343 /**
344 * @param int $value The value for which to retrieve a name.
345 *
346 * @return string|null The name associated with the given value.
347 */
348 public static function getNameByValue( $value ) {
349 $ret = array_search( $value, self::getLogLevels() );
350
351 return ( false !== $ret ) ? $ret : null;
352 }
353
354 /**
355 * Blocks instantiation. All functions are static.
356 */
357 private function __construct() {
358
359 }
360 }