LOGS, 'file' => null, 'types' => null, 'scopes' => array(), 'rotate' => 10, 'size' => 10485760, // 10MB 'mask' => null, ); /** * Path to save log files on. * * @var string */ protected $_path = null; /** * Log file name * * @var string */ protected $_file = null; /** * Max file size, used for log file rotation. * * @var integer */ protected $_size = null; /** * Constructs a new File Logger. * * Config * * - `types` string or array, levels the engine is interested in * - `scopes` string or array, scopes the engine is interested in * - `file` Log file name * - `path` The path to save logs on. * - `size` Used to implement basic log file rotation. If log file size * reaches specified size the existing file is renamed by appending timestamp * to filename and new log file is created. Can be integer bytes value or * human reabable string values like '10MB', '100KB' etc. * - `rotate` Log files are rotated specified times before being removed. * If value is 0, old versions are removed rather then rotated. * - `mask` A mask is applied when log files are created. Left empty no chmod * is made. * * @param array $options Options for the FileLog, see above. */ public function __construct($config = array()) { $config = Hash::merge($this->_defaults, $config); parent::__construct($config); } /** * Sets protected properties based on config provided * * @param array $config Engine configuration * @return array */ public function config($config = array()) { parent::config($config); if (!empty($config['path'])) { $this->_path = $config['path']; } if (Configure::read('debug') && !is_dir($this->_path)) { mkdir($this->_path, 0775, true); } if (!empty($config['file'])) { $this->_file = $config['file']; if (substr($this->_file, -4) !== '.log') { $this->_file .= '.log'; } } if (!empty($config['size'])) { if (is_numeric($config['size'])) { $this->_size = (int)$config['size']; } else { $this->_size = CakeNumber::fromReadableSize($config['size']); } } return $this->_config; } /** * Implements writing to log files. * * @param string $type The type of log you are making. * @param string $message The message you want to log. * @return boolean success of write. */ public function write($type, $message) { $output = date('Y-m-d H:i:s') . ' ' . ucfirst($type) . ': ' . $message . "\n"; $filename = $this->_getFilename($type); if (!empty($this->_size)) { $this->_rotateFile($filename); } $pathname = $this->_path . $filename; if (empty($this->_config['mask'])) { return file_put_contents($pathname, $output, FILE_APPEND); } $exists = file_exists($pathname); $result = file_put_contents($pathname, $output, FILE_APPEND); static $selfError = false; if (!$selfError && !$exists && !chmod($pathname, (int)$this->_config['mask'])) { $selfError = true; trigger_error(__d( 'cake_dev', 'Could not apply permission mask "%s" on log file "%s"', array($this->_config['mask'], $pathname)), E_USER_WARNING); $selfError = false; } return $result; } /** * Get filename * @param string $type The type of log. * @return string File name */ protected function _getFilename($type) { $debugTypes = array('notice', 'info', 'debug'); if (!empty($this->_file)) { $filename = $this->_file; } elseif ($type === 'error' || $type === 'warning') { $filename = 'error.log'; } elseif (in_array($type, $debugTypes)) { $filename = 'debug.log'; } else { $filename = $type . '.log'; } return $filename; } /** * Rotate log file if size specified in config is reached. * Also if `rotate` count is reached oldest file is removed. * * @param string $filename Log file name * @return mixed True if rotated successfully or false in case of error. * Void if file doesn't need to be rotated. */ protected function _rotateFile($filename) { $filepath = $this->_path . $filename; if (version_compare(PHP_VERSION, '5.3.0') >= 0) { clearstatcache(true, $filepath); } else { clearstatcache(); } if (!file_exists($filepath) || filesize($filepath) < $this->_size ) { return; } if ($this->_config['rotate'] === 0) { $result = unlink($filepath); } else { $result = rename($filepath, $filepath . '.' . time()); } $files = glob($filepath . '.*'); if ($files) { $filesToDelete = count($files) - $this->_config['rotate']; while ($filesToDelete > 0) { unlink(array_shift($files)); $filesToDelete--; } } return $result; } }