PageRenderTime 38ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/symphony/lib/core/class.log.php

http://github.com/symphonycms/symphony-2
PHP | 485 lines | 186 code | 46 blank | 253 comment | 27 complexity | 54c94b8c9a5ad43dbaa1e72f12070243 MD5 | raw file
  1. <?php
  2. /**
  3. * @package core
  4. */
  5. /**
  6. * The Log class acts a simple wrapper to write errors to a file so that it can
  7. * be read at a later date. There is one Log file in Symphony, stored in the main
  8. * `LOGS` directory.
  9. */
  10. class Log
  11. {
  12. /**
  13. * A constant for if this message should add to an existing log file
  14. * @var integer
  15. */
  16. const APPEND = 10;
  17. /**
  18. * A constant for if this message should overwrite the existing log
  19. * @var integer
  20. */
  21. const OVERWRITE = 11;
  22. /**
  23. * The path to this log file
  24. * @var string
  25. */
  26. private $_log_path = null;
  27. /**
  28. * An array of log messages to write to the log.
  29. * @var array
  30. */
  31. private $_log = array();
  32. /**
  33. * The maximise size of the log can reach before it is rotated and a new
  34. * Log file written started. The units are bytes. Default is -1, which
  35. * means that the log will never be rotated.
  36. * @var integer
  37. */
  38. private $_max_size = -1;
  39. /**
  40. * Whether to archive olds logs or not, by default they will not be archived.
  41. * @var boolean
  42. */
  43. private $_archive = false;
  44. /**
  45. * The filter applied to logs before they are written.
  46. * @since Symphony 2.7.1
  47. * @var integer
  48. */
  49. private $_filter = -1;
  50. /**
  51. * The date format that this Log entries will be written as.
  52. * @since Symphony 3.0.0, it defaults to ISO 8601.
  53. * @var string
  54. */
  55. private $_datetime_format = 'c';
  56. /**
  57. * A random value used to identify which Log instance created the data.
  58. * @since Symphony 3.0.0
  59. * @var string
  60. */
  61. private $id;
  62. /**
  63. * The log constructor takes a path to the folder where the Log should be
  64. * written to.
  65. *
  66. * @param string $path
  67. * The path to the folder where the Log files should be written
  68. */
  69. public function __construct($path)
  70. {
  71. $this->setLogPath($path);
  72. $this->id = substr(uniqid(), 0, 6);
  73. }
  74. /**
  75. * Setter for the `$_log_path`.
  76. *
  77. * @param string $path
  78. * The path to the folder where the Log files should be written
  79. */
  80. public function setLogPath($path)
  81. {
  82. $this->_log_path = $path;
  83. }
  84. /**
  85. * Accessor for the `$_log_path`.
  86. *
  87. * @return string
  88. */
  89. public function getLogPath()
  90. {
  91. return $this->_log_path;
  92. }
  93. /**
  94. * Accessor for the `$_log`.
  95. *
  96. * @return array
  97. */
  98. public function getLog()
  99. {
  100. return $this->_log;
  101. }
  102. /**
  103. * Accessor for the `$id` variable.
  104. * @since Symphony 3.0.0
  105. * @return string
  106. */
  107. public function getId()
  108. {
  109. return $this->id;
  110. }
  111. /**
  112. * Setter for the `$_archive`.
  113. *
  114. * @param boolean $archive
  115. * If true, Log files will be archived using gz when they are rotated,
  116. * otherwise they will just be overwritten when they are due for rotation
  117. */
  118. public function setArchive($archive)
  119. {
  120. $this->_archive = $archive;
  121. }
  122. /**
  123. * Setter for the `$_max_size`.
  124. *
  125. * @param integer $size
  126. * The size, in bytes, that the Log can reach before it is rotated.
  127. */
  128. public function setMaxSize($size)
  129. {
  130. $this->_max_size = General::intval($size);
  131. }
  132. /**
  133. * Setter for the `$_filter`.
  134. *
  135. * @since Symphony 2.7.1
  136. * @param mixed $filter
  137. * The filter used on log $type parameter.
  138. */
  139. public function setFilter($filter)
  140. {
  141. $this->_filter = General::intval($filter);
  142. }
  143. /**
  144. * Setter for the `$_date_format`.
  145. *
  146. * @since Symphony 2.2
  147. * @link http://au.php.net/manual/en/function.date.php
  148. * @param string $format
  149. * Takes a valid date format using the PHP date tokens
  150. */
  151. public function setDateTimeFormat($format)
  152. {
  153. if (empty($format)) {
  154. throw new Exception('Datetime format can not be empty');
  155. }
  156. $this->_datetime_format = $format;
  157. }
  158. /**
  159. * Given a PHP error constant, return a human readable name. Uses the
  160. * `ErrorHandler::$errorTypeStrings` array to return
  161. * the name
  162. *
  163. * @see core.ErrorHandler::$errorTypeStrings
  164. * @param integer $type
  165. * A PHP error constant
  166. * @return string
  167. * A human readable name of the error constant, or if the type is not
  168. * found, UNKNOWN.
  169. */
  170. private function __defineNameString($type)
  171. {
  172. if (isset(ErrorHandler::$errorTypeStrings[$type])) {
  173. return ErrorHandler::$errorTypeStrings[$type];
  174. }
  175. return is_string($type) ? $type : 'UNKNOWN';
  176. }
  177. /**
  178. * Function will return the last message added to `$_log` and remove
  179. * it from the array.
  180. *
  181. * @return array|boolean
  182. * Returns an associative array of a log message, containing the type of the log
  183. * message, the actual message and the time at the which it was added to the log.
  184. * If the log is empty, this function removes false.
  185. */
  186. public function popFromLog()
  187. {
  188. if (!empty($this->_log)) {
  189. return array_pop($this->_log);
  190. }
  191. return false;
  192. }
  193. /**
  194. * Given a message, this function will add it to the internal `$_log`
  195. * so that it can be written to the Log. Optional parameters all the message to
  196. * be immediately written, insert line breaks or add to the last log message
  197. *
  198. * @param string $message
  199. * The message to add to the Log
  200. * @param integer $type
  201. * A PHP error constant for this message, defaults to E_NOTICE.
  202. * If null or 0, will be converted to E_ERROR.
  203. * @param boolean $writeToLog
  204. * If set to true, this message will be immediately written to the log. By default
  205. * this is set to false, which means that it will only be added to the array ready
  206. * for writing
  207. * @param boolean $addbreak
  208. * To be used in conjunction with `$writeToLog`, this will add a line break
  209. * before writing this message in the log file. Defaults to true.
  210. * @param boolean $append
  211. * If set to true, the given `$message` will be append to the previous log
  212. * message found in the `$_log` array
  213. * @return boolean|null
  214. * If `$writeToLog` is passed, this function will return boolean, otherwise
  215. * void
  216. */
  217. public function pushToLog($message, $type = E_NOTICE, $writeToLog = false, $addbreak = true, $append = false)
  218. {
  219. if (!$type) {
  220. $type = E_ERROR;
  221. }
  222. if ($append) {
  223. $this->_log[count($this->_log) - 1]['message'] = $this->_log[count($this->_log) - 1]['message'] . $message;
  224. } else {
  225. array_push($this->_log, array('type' => $type, 'time' => time(), 'message' => $message));
  226. $message = DateTimeObj::get($this->_datetime_format) .
  227. ' ' . $this->id .
  228. ' > ' . $this->__defineNameString($type) .
  229. ': ' . $message;
  230. }
  231. if (!is_numeric($type)) {
  232. $type = E_ERROR;
  233. }
  234. if ($writeToLog && ($this->_filter === -1 || ($this->_filter & $type))) {
  235. return $this->writeToLog($message, $addbreak);
  236. }
  237. }
  238. /**
  239. * This function will write the given message to the log file. Messages will be appended
  240. * the existing log file.
  241. *
  242. * @param string $message
  243. * The message to add to the Log
  244. * @param boolean $addbreak
  245. * To be used in conjunction with `$writeToLog`, this will add a line break
  246. * before writing this message in the log file. Defaults to true.
  247. * @return boolean
  248. * Returns true if the message was written successfully, false otherwise
  249. */
  250. public function writeToLog($message, $addbreak = true)
  251. {
  252. if (file_exists($this->_log_path) && !is_writable($this->_log_path)) {
  253. $this->pushToLog('Could not write to Log. It is not readable.');
  254. return false;
  255. }
  256. $permissions = Symphony::Configuration() ? Symphony::Configuration()->get('write_mode', 'file') : '0664';
  257. return General::writeFile($this->_log_path, $message . ($addbreak ? PHP_EOL : ''), $permissions, 'a+');
  258. }
  259. /**
  260. * Given an Throwable, this function will add it to the internal `$_log`
  261. * so that it can be written to the Log.
  262. *
  263. * @since Symphony 2.3.2
  264. *
  265. * @since Symphony 2.7.0
  266. * This function works with both Exceptions and Throwable
  267. * Supporting both PHP 5.6 and 7 forces use to not qualify the $e parameter
  268. *
  269. * @param Throwable $exception
  270. * @param boolean $writeToLog
  271. * If set to true, this message will be immediately written to the log. By default
  272. * this is set to false, which means that it will only be added to the array ready
  273. * for writing
  274. * @param boolean $addbreak
  275. * To be used in conjunction with `$writeToLog`, this will add a line break
  276. * before writing this message in the log file. Defaults to true.
  277. * @param boolean $append
  278. * If set to true, the given `$message` will be append to the previous log
  279. * message found in the `$_log` array
  280. * @return boolean|null
  281. * If `$writeToLog` is passed, this function will return boolean, otherwise
  282. * void
  283. */
  284. public function pushExceptionToLog($exception, $writeToLog = false, $addbreak = true, $append = false)
  285. {
  286. $message = sprintf(
  287. '%s %s - %s on line %d of %s',
  288. get_class($exception),
  289. $exception->getCode(),
  290. $exception->getMessage(),
  291. $exception->getLine(),
  292. $exception->getFile()
  293. );
  294. return $this->pushToLog($message, $exception->getCode(), $writeToLog, $addbreak, $append);
  295. }
  296. /**
  297. * Given an method name, this function will properly format a message
  298. * and pass it down to `pushToLog()`
  299. *
  300. * @see Log::pushToLog()
  301. * @since Symphony 2.7.0
  302. * @param string $method
  303. * The name of the deprecated call
  304. * @param string $alternative
  305. * The name of the new method to use
  306. * @param array $opts (optional)
  307. * @param string $opts.message-format
  308. * The sprintf format to apply to $method
  309. * @param string $opts.alternative-format
  310. * The sprintf format to apply to $alternative
  311. * @param string $opts.removal-format
  312. * The sprintf format to apply to $opts.removal-version
  313. * @param string $opts.removal-version
  314. * The Symphony version at which the removal is planned
  315. * @param boolean $opts.write-to-log
  316. * If set to true, this message will be immediately written to the log. By default
  317. * this is set to false, which means that it will only be added to the array ready
  318. * for writing
  319. * @param boolean $opts.addbreak
  320. * To be used in conjunction with `$opts.write-to-log`, this will add a line break
  321. * before writing this message in the log file. Defaults to true.
  322. * @param boolean $opts.append
  323. * If set to true, the given `$message` will be append to the previous log
  324. * message found in the `$_log` array
  325. * @param boolean $opts.addtrace
  326. * If set to true, the caller of the function will be added. Defaults to true.
  327. * @return boolean|null
  328. * If `$writeToLog` is passed, this function will return boolean, otherwise
  329. * void
  330. */
  331. public function pushDeprecateWarningToLog($method, $alternative = null, array $opts = array())
  332. {
  333. $defaults = array(
  334. 'message-format' => __('The method `%s` is deprecated.'),
  335. 'alternative-format' => __('Please use `%s` instead.'),
  336. 'removal-format' => __('It will be removed in Symphony %s.'),
  337. 'removal-version' => '3.0.0',
  338. 'write-to-log' => true,
  339. 'addbreak' => true,
  340. 'append' => false,
  341. 'addtrace' => true,
  342. );
  343. $opts = array_replace($defaults, $opts);
  344. $message = sprintf($opts['message-format'], $method);
  345. if (!empty($opts['removal-version'])) {
  346. $message .= ' ' . sprintf($opts['removal-format'], $opts['removal-version']);
  347. }
  348. if (!empty($alternative)) {
  349. $message .= ' ' . sprintf($opts['alternative-format'], $alternative);
  350. }
  351. if ($opts['addtrace'] === true) {
  352. $trace = debug_backtrace(0, 3);
  353. $index = isset($trace[2]['class']) ? 2 : 1;
  354. $caller = $trace[$index]['class'] . '::' . $trace[$index]['function'] . '()';
  355. $file = basename($trace[$index - 1]['file']);
  356. $line = $trace[$index - 1]['line'];
  357. $message .= " Called from `$caller` in $file at line $line";
  358. }
  359. return $this->pushToLog($message, E_DEPRECATED, $opts['write-to-log'], $opts['addbreak'], $opts['append']);
  360. }
  361. /**
  362. * The function handles the rotation of the log files. By default it will open
  363. * the current log file, 'main', which is written to `$_log_path` and
  364. * check it's file size doesn't exceed `$_max_size`. If it does, the log
  365. * is appended with a date stamp and if `$_archive` has been set, it will
  366. * be archived and stored. If a log file has exceeded it's size, or `Log::OVERWRITE`
  367. * flag is set, the existing log file is removed and a new one created. Essentially,
  368. * if a log file has not reached it's `$_max_size` and the the flag is not
  369. * set to `Log::OVERWRITE`, this function does nothing.
  370. *
  371. * @link http://au.php.net/manual/en/function.intval.php
  372. * @param integer $flag
  373. * One of the Log constants, either `Log::APPEND` or `Log::OVERWRITE`
  374. * By default this is `Log::APPEND`
  375. * @param integer $mode
  376. * The file mode used to apply to the archived log, by default this is 0777. Note that this
  377. * parameter is modified using PHP's intval function with base 8.
  378. * @throws Exception
  379. * @return integer
  380. * Returns 1 if the log was overwritten, or 2 otherwise.
  381. */
  382. public function open($flag = self::APPEND, $mode = 0777)
  383. {
  384. if (!file_exists($this->_log_path)) {
  385. $flag = self::OVERWRITE;
  386. }
  387. if ($flag == self::APPEND && file_exists($this->_log_path) && is_readable($this->_log_path)) {
  388. if ($this->_max_size > 0 && filesize($this->_log_path) > $this->_max_size) {
  389. $flag = self::OVERWRITE;
  390. if ($this->_archive) {
  391. $this->close();
  392. $file = $this->_log_path . DateTimeObj::get('Ymdh').'.gz';
  393. if (function_exists('gzopen64')) {
  394. $handle = gzopen64($file, 'w9');
  395. } else {
  396. $handle = gzopen($file, 'w9');
  397. }
  398. gzwrite($handle, file_get_contents($this->_log_path));
  399. gzclose($handle);
  400. chmod($file, intval($mode, 8));
  401. }
  402. }
  403. }
  404. if ($flag == self::OVERWRITE) {
  405. General::deleteFile($this->_log_path);
  406. $this->writeToLog('============================================', true);
  407. $this->writeToLog('Log Created: ' . DateTimeObj::get('c'), true);
  408. $this->writeToLog('============================================', true);
  409. chmod($this->_log_path, intval($mode, 8));
  410. return 1;
  411. }
  412. return 2;
  413. }
  414. /**
  415. * Writes a end of file block at the end of the log file with a datetime
  416. * stamp of when the log file was closed.
  417. */
  418. public function close()
  419. {
  420. $this->writeToLog('============================================', true);
  421. $this->writeToLog('Log Closed: ' . DateTimeObj::get('c'), true);
  422. $this->writeToLog("============================================" . PHP_EOL . PHP_EOL, true);
  423. }
  424. /* Initialises the log file by writing into it the log name, the date of
  425. * creation, the current Symphony version and the current domain.
  426. *
  427. * @param string $name
  428. * The name of the log being initialised
  429. */
  430. public function initialise($name)
  431. {
  432. $version = (is_null(Symphony::Configuration())) ? VERSION : Symphony::Configuration()->get('version', 'symphony');
  433. $this->writeToLog($name, true);
  434. $this->writeToLog('Opened: '. DateTimeObj::get('c'), true);
  435. $this->writeToLog('Version: '. $version, true);
  436. $this->writeToLog('Domain: '. DOMAIN, true);
  437. $this->writeToLog('--------------------------------------------', true);
  438. }
  439. }