1: <?php
  2:   3:   4:   5:   6:   7:   8:   9:  10:  11:  12:  13:  14:  15:  16:  17: 
 18: 
 19: App::uses('BaseLog', 'Log/Engine');
 20: App::uses('Hash', 'Utility');
 21: App::uses('CakeNumber', 'Utility');
 22: 
 23:  24:  25:  26:  27:  28: 
 29: class FileLog extends BaseLog {
 30: 
 31:  32:  33:  34:  35:  36: 
 37:     protected $_defaults = array(
 38:         'path' => LOGS,
 39:         'file' => null,
 40:         'types' => null,
 41:         'scopes' => array(),
 42:         'rotate' => 10,
 43:         'size' => 10485760, 
 44:         'mask' => null,
 45:     );
 46: 
 47:  48:  49:  50:  51: 
 52:     protected $_path = null;
 53: 
 54:  55:  56:  57:  58: 
 59:     protected $_file = null;
 60: 
 61:  62:  63:  64:  65: 
 66:     protected $_size = null;
 67: 
 68:  69:  70:  71:  72:  73:  74:  75:  76:  77:  78:  79:  80:  81:  82:  83:  84:  85:  86:  87: 
 88:     public function __construct($config = array()) {
 89:         $config = Hash::merge($this->_defaults, $config);
 90:         parent::__construct($config);
 91:     }
 92: 
 93:  94:  95:  96:  97:  98: 
 99:     public function config($config = array()) {
100:         parent::config($config);
101: 
102:         if (!empty($config['path'])) {
103:             $this->_path = $config['path'];
104:         }
105:         if (Configure::read('debug') && !is_dir($this->_path)) {
106:             mkdir($this->_path, 0775, true);
107:         }
108: 
109:         if (!empty($config['file'])) {
110:             $this->_file = $config['file'];
111:             if (substr($this->_file, -4) !== '.log') {
112:                 $this->_file .= '.log';
113:             }
114:         }
115:         if (!empty($config['size'])) {
116:             if (is_numeric($config['size'])) {
117:                 $this->_size = (int)$config['size'];
118:             } else {
119:                 $this->_size = CakeNumber::fromReadableSize($config['size']);
120:             }
121:         }
122: 
123:         return $this->_config;
124:     }
125: 
126: 127: 128: 129: 130: 131: 132: 
133:     public function write($type, $message) {
134:         $output = date('Y-m-d H:i:s') . ' ' . ucfirst($type) . ': ' . $message . "\n";
135:         $filename = $this->_getFilename($type);
136:         if (!empty($this->_size)) {
137:             $this->_rotateFile($filename);
138:         }
139: 
140:         $pathname = $this->_path . $filename;
141:         if (empty($this->_config['mask'])) {
142:             return file_put_contents($pathname, $output, FILE_APPEND);
143:         }
144: 
145:         $exists = file_exists($pathname);
146:         $result = file_put_contents($pathname, $output, FILE_APPEND);
147:         static $selfError = false;
148:         if (!$selfError && !$exists && !chmod($pathname, (int)$this->_config['mask'])) {
149:             $selfError = true;
150:             trigger_error(__d(
151:                 'cake_dev', 'Could not apply permission mask "%s" on log file "%s"',
152:                 array($this->_config['mask'], $pathname)), E_USER_WARNING);
153:             $selfError = false;
154:         }
155:         return $result;
156:     }
157: 
158: 159: 160: 161: 162: 163: 
164:     protected function _getFilename($type) {
165:         $debugTypes = array('notice', 'info', 'debug');
166: 
167:         if (!empty($this->_file)) {
168:             $filename = $this->_file;
169:         } elseif ($type === 'error' || $type === 'warning') {
170:             $filename = 'error.log';
171:         } elseif (in_array($type, $debugTypes)) {
172:             $filename = 'debug.log';
173:         } else {
174:             $filename = $type . '.log';
175:         }
176: 
177:         return $filename;
178:     }
179: 
180: 181: 182: 183: 184: 185: 186: 187: 
188:     protected function _rotateFile($filename) {
189:         $filepath = $this->_path . $filename;
190:         if (version_compare(PHP_VERSION, '5.3.0') >= 0) {
191:             clearstatcache(true, $filepath);
192:         } else {
193:             clearstatcache();
194:         }
195: 
196:         if (!file_exists($filepath) ||
197:             filesize($filepath) < $this->_size
198:         ) {
199:             return;
200:         }
201: 
202:         if ($this->_config['rotate'] === 0) {
203:             $result = unlink($filepath);
204:         } else {
205:             $result = rename($filepath, $filepath . '.' . time());
206:         }
207: 
208:         $files = glob($filepath . '.*');
209:         if ($files) {
210:             $filesToDelete = count($files) - $this->_config['rotate'];
211:             while ($filesToDelete > 0) {
212:                 unlink(array_shift($files));
213:                 $filesToDelete--;
214:             }
215:         }
216: 
217:         return $result;
218:     }
219: 
220: }
221: