Overview

Packages

  • AlfredBundler
  • None

Classes

  • AlfredBundler
  • AlfredBundlerIcon
  • AlfredBundlerInternalClass
  • AlfredBundlerLogger
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: 
  3: /**
  4:  * Alfred Bundler Logging Fiel
  5:  *
  6:  * Generic interface for logging to files and the console to be used with the
  7:  * PHP implementation of the Alfred Bundler.
  8:  *
  9:  * This file is part of the Alfred Bundler, released under the MIT licence.
 10:  * Copyright (c) 2014 The Alfred Bundler Team
 11:  * See https://github.com/shawnrice/alfred-bundler for more information
 12:  *
 13:  * @copyright  The Alfred Bundler Team 2014
 14:  * @license    http://opensource.org/licenses/MIT  MIT
 15:  * @version    Taurus 1
 16:  * @link       http://shawnrice.github.io/alfred-bundler
 17:  * @package    AlfredBundler
 18:  * @since      File available since Taurus 1
 19:  */
 20: 
 21: 
 22: if ( ! class_exists( 'AlfredBundlerLogger' ) ) :
 23: /**
 24:  *
 25:  * Simple logging functionality that writes to files or STDERR
 26:  *
 27:  * Usage: just create a single object and reuse it solely. Initialize the
 28:  * object with a full path to the log file (no log extension)
 29:  *
 30:  * This class was written to be part of the PHP implementation of the
 31:  * Alfred Bundler for the bundler's internal logging requirements. However
 32:  * its functionality is also made available to any workflow author implementing
 33:  * the bundler.
 34:  *
 35:  * While you can use this class without going through the bundler, it is
 36:  * easier just to use the logging functionality indirectly via the
 37:  * bundler object.
 38:  *
 39:  * @see       AlfredBundlerInternalClass::log
 40:  * @see       AlfredBundlerInternalClass::debug
 41:  * @see       AlfredBundlerInternalClass::info
 42:  * @see       AlfredBundlerInternalClass::warning
 43:  * @see       AlfredBundlerInternalClass::error
 44:  * @see       AlfredBundlerInternalClass::critical
 45:  * @see       AlfredBundlerInternalClass::console
 46:  *
 47:  * @package   AlfredBundler
 48:  * @since     Class available since Taurus 1
 49:  *
 50:  */
 51: class AlfredBundlerLogger {
 52: 
 53:   /**
 54:    * Log file
 55:    *
 56:    * Full path to log file with no extension; set by user at instantiation
 57:    *
 58:    * @var  string
 59:    * @since Taurus 1
 60:    */
 61:   public $log;
 62: 
 63:   /**
 64:    * An array of log levels (int => string )
 65:    *
 66:    * 0 => 'DEBUG'
 67:    * 1 => 'INFO'
 68:    * 2 => 'WARNING'
 69:    * 3 => 'ERROR'
 70:    * 4 => 'CRITICAL'
 71:    *
 72:    * @var  array
 73:    * @since Taurus 1
 74:    */
 75:   protected $logLevels;
 76: 
 77:   /**
 78:    * Stacktrace information; reset with each message
 79:    *
 80:    * @var  array
 81:    * @since Taurus 1
 82:    */
 83:   private $trace;
 84: 
 85:   /**
 86:    * File from stacktrace; reset with each message
 87:    *
 88:    * @var  string
 89:    * @since Taurus 1
 90:    */
 91:   private $file;
 92: 
 93:   /**
 94:    * Line from stacktrace; reset with each message
 95:    *
 96:    * @var  int
 97:    * @since Taurus 1
 98:    */
 99:   private $line;
100: 
101:   /**
102:    * Log level; reset with each message
103:    *
104:    * @var  mixed
105:    * @since Taurus 1
106:    */
107:   private $level;
108: 
109:   /**
110:    * Default destination to send a log message to
111:    *
112:    * @var  string   options: file, console, both
113:    */
114:   private $defaultDestination;
115: 
116:   /**
117:    * Sets variables and ini settings to ensure there are no errors
118:    *
119:    * @param  string  $log                   filename to use as a log
120:    * @param  string  $destination = 'file'  default destination for messages
121:    * @since Taurus 1
122:    */
123:   public function __construct( $log, $destination = 'file' ) {
124: 
125:     $this->log = $log . '.log';
126:     $this->initializeLog();
127: 
128:     if ( ! in_array( $destination, [ 'file', 'console', 'both' ] ) )
129:       $this->defaultDestination = 'file';
130:     else
131:       $this->defaultDestination = $destination;
132: 
133:     // These are the appropriate log levels
134:     $this->logLevels = array( 0 => 'DEBUG',
135:                               1 => 'INFO',
136:                               2 => 'WARNING',
137:                               3 => 'ERROR',
138:                               4 => 'CRITICAL',
139:     );
140: 
141:     // Set date/time to avoid warnings/errors.
142:     if ( ! ini_get( 'date.timezone' ) ) {
143:       $tz = exec( 'tz=`ls -l /etc/localtime` && echo ${tz#*/zoneinfo/}' );
144:       ini_set( 'date.timezone', $tz );
145:     }
146: 
147:     // This is needed because, Macs don't read EOLs well.
148:     if ( ! ini_get( 'auto_detect_line_endings' ) )
149:       ini_set( 'auto_detect_line_endings', TRUE );
150: 
151:   }
152: 
153:   /**
154:    * Logs a message to either a file or STDERR
155:    *
156:    * After initializing the log object, this should be the only
157:    * method with which you interact.
158:    *
159:    * While you could use this separate from the bundler itself, it is
160:    * easier to use the logging functionality from the bundler object.
161:    *
162:    * @see AlfredBundlerInternalClass::log
163:    *
164:    * <code>
165:    * $log = new AlfredBundlerLogger( '/full/path/to/mylog' );
166:    * $log->log( 'Write this to a file', 'INFO' );
167:    * $log->log( 'Warning message to console', 2, 'console' );
168:    * $log->log( 'This message will go to both the console and the log', 3, 'both');
169:    * </code>
170:    *
171:    *
172:    * @param   string  $message      message to log
173:    * @param   mixed   $level        either int or string of log level
174:    * @param   string  $destination  where the message should appear:
175:    *                                valid options: 'file', 'console', 'both'
176:    * @since Taurus 1
177:    */
178:   public function log( $message, $level = 'INFO', $destination = '', $trace = 0 ) {
179: 
180:     // Set the destination to the default if not implied
181:     if ( empty( $destination ) )
182:       $destination = $this->defaultDestination;
183: 
184:     // print_r( debug_backtrace() );
185: 
186:     // Get the relevant information from the backtrace
187:     $this->trace = debug_backtrace();
188:     $this->trace = $this->trace[ $trace ];
189:     $this->file  = basename( $this->trace[ 'file' ] );
190:     $this->line  = $this->trace[ 'line' ];
191: 
192:     // check / normalize the arguments
193:     $this->level = $this->normalizeLogLevel( $level );
194:     $destination = strtolower( $destination );
195: 
196:     if ( $destination == 'file' || $destination == 'both' )
197:       $this->logFile( $message );
198:     if ( $destination == 'console' || $destination == 'both' )
199:       $this->logConsole( $message );
200: 
201:   }
202: 
203:   /**
204:    * Creates log directory and file if necessary
205:    * @since Taurus 1
206:    */
207:   private function initializeLog() {
208:     if ( ! file_exists( $this->log ) ) {
209:       if ( ! is_dir( realpath( dirname( $this->log ) ) ) )
210:         mkdir( dirname( $this->log ), 0775, TRUE );
211:       file_put_contents( $this->log, '' );
212:     }
213:   }
214: 
215: 
216:   /**
217:    * Checks to see if the log needs to be rotated
218:    * @since Taurus 1
219:    */
220:   private function checkLog() {
221:     if ( filesize( $this->log ) > 1048576 )
222:       $this->rotateLog();
223:   }
224: 
225: 
226:   /**
227:    * Rotates the log
228:    * @since Taurus 1
229:    */
230:   private function rotateLog() {
231:       $old = substr( $this->log, -4 );
232:       if ( file_exists( $old . '1.log' ) )
233:         unlink( $old . '1.log' );
234: 
235:       rename( $this->log, $old . '1.log' );
236:       file_put_contents( $this->log, '' );
237:   }
238: 
239:   /**
240:    * Ensures that the log level is valid
241:    *
242:    * @param   mixed  $level   either an int or a string denoting log level
243:    *
244:    * @return  string          log level as string
245:    * @since Taurus 1
246:    */
247:   public function normalizeLogLevel( $level ) {
248: 
249:     $date = date( 'H:i:s', time() );
250: 
251:     // If the level is okay, then just return it
252:     if ( isset( $this->logLevels[ $level ] )
253:          || in_array( $level, $this->logLevels ) ) {
254:       return $level;
255:     }
256: 
257:     // the level is invalid; log a message to the console
258:     file_put_contents( 'php://stderr', "[{$date}] " .
259:       "[{$this->file},{$this->line}] [WARNING] Log level '{$level}' " .
260:       "is not valid. Falling back to 'INFO' (1)" . PHP_EOL );
261: 
262:     // set level to info
263:     return 'INFO';
264:   }
265: 
266:   /**
267:    * Writes a message to the console (STDERR)
268:    *
269:    * @param   string  $message  message to log
270:    * @since Taurus 1
271:    */
272:   public function logConsole( $message ) {
273:     $date = date( 'H:i:s', time() );
274:     file_put_contents( 'php://stderr', "[{$date}] " .
275:       "[{$this->file}:{$this->line}] [{$this->level}] {$message}" . PHP_EOL );
276:   }
277: 
278:   /**
279:    * Writes message to log file
280:    *
281:    * @param   string  $message  message to log
282:    * @since Taurus 1
283:    */
284:   public function logFile( $message ) {
285:     $date = date( "Y-m-d H:i:s" );
286:     $message = "[{$date}] [{$this->file}:{$this->line}] " .
287:                "[{$this->level}] ". $message . PHP_EOL;
288:     file_put_contents( $this->log, $message, FILE_APPEND | LOCK_EX );
289:   }
290: 
291: 
292: }
293: endif;
Alfred Bundler API documentation generated by ApiGen 2.8.0