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

/upload/wind/log/WindLogger.php

https://gitlab.com/wuhang2003/phpwind
PHP | 549 lines | 218 code | 38 blank | 293 comment | 37 complexity | a8560b0c22ce51a4d1fab66860d40fb5 MD5 | raw file
  1. <?php
  2. /**
  3. * 日志记录
  4. *
  5. * 日志记录的级别有以下几种:
  6. * <ul>
  7. * <li>WindLogger::LEVEL_INFO = 1: 信息记录,只记录需要记录的信息。</li>
  8. * <li>WindLogger::LEVEL_TRACE = 2: 信息记录,同时记录来自trace的信息。</li>
  9. * <li>WindLogger::LEVEL_DEBUG = 3: 信息记录,同时记录来自trace的信息。</li>
  10. * <li>WindLogger::LEVEL_ERROR = 4: 记录错误信息,不包含trace信息。</li>
  11. * <li>WindLogger::LEVEL_PROFILE = 5: 分析信息记录,包含详细的时间及内存使用情况等</li>
  12. * </ul>
  13. * 日志的存放形式也可以通过write_type设置:
  14. * <ul>
  15. * <li>0: 打印全部日志信息结果</li>
  16. * <li>1: 按照level分文件存储日志记录</li>
  17. * <li>2: 按照type分文件存储日志记录</li>
  18. * </ul>
  19. *
  20. * @author Qian Su <aoxue.1988.su.qian@163.com>
  21. * @copyright ©2003-2103 phpwind.com
  22. * @license http://www.windframework.com
  23. * @version $Id: WindLogger.php 3904 2013-01-08 07:01:26Z yishuo $
  24. * @package log
  25. */
  26. class WindLogger extends WindModule {
  27. /**
  28. * 级别1: 只是记录信息不记录trace信息
  29. *
  30. * @var int
  31. */
  32. const LEVEL_INFO = 1;
  33. /**
  34. * 级别2:将会打印出堆栈中trace信息
  35. *
  36. * @var int
  37. */
  38. const LEVEL_TRACE = 2;
  39. /**
  40. * 级别3:标志该信息是一个debug
  41. *
  42. * debug信息将会导出trace信息和debug详细信息
  43. *
  44. * @var int
  45. */
  46. const LEVEL_DEBUG = 3;
  47. /**
  48. * 级别4:记录错误信息,不包含trace信息
  49. *
  50. * @var int
  51. */
  52. const LEVEL_ERROR = 4;
  53. /**
  54. * 级别5:分析信息记录,包含详细的时间及内存使用情况等
  55. *
  56. * @var int
  57. */
  58. const LEVEL_PROFILE = 5;
  59. /**
  60. * 日志的方式
  61. *
  62. * @var int
  63. */
  64. const WRITE_TYPE = 2;
  65. /**
  66. * 日志记录中profile信息开始的标志
  67. *
  68. * @var string
  69. */
  70. const TOKEN_BEGIN = 'begin:';
  71. /**
  72. * 日志记录中profile信息结束的标志
  73. *
  74. * @var string
  75. */
  76. const TOKEN_END = 'end:';
  77. /**
  78. * 每次当日志数量达到1000条的时候,就写入文件一次
  79. *
  80. * @var int
  81. */
  82. private $_autoFlush = 1000;
  83. /**
  84. * 日志内容
  85. *
  86. * @var array
  87. */
  88. private $_logs = array();
  89. /**
  90. * 日志条数统计
  91. *
  92. * @var int
  93. */
  94. private $_logCount = 0;
  95. /**
  96. * 日志的详细信息
  97. *
  98. * @var array
  99. */
  100. private $_profiles = array();
  101. /**
  102. * 日志记录的地址
  103. *
  104. * @var string
  105. */
  106. private $_logDir;
  107. /**
  108. * 日志文件的最大长度
  109. *
  110. * @var int
  111. */
  112. private $_maxFileSize = 100;
  113. /**
  114. * 日志打印形式
  115. *
  116. * 0: 打印全部日志信息结果
  117. * 1: 按照level分文件存储日志记录
  118. * 2: 按照type分文件存储日志记录
  119. *
  120. * @var int
  121. */
  122. private $_writeType = 0;
  123. /**
  124. * 存放日志打印形式
  125. *
  126. * @var array
  127. */
  128. private $_types = array();
  129. /**
  130. * 构造函数
  131. *
  132. * @param string $logDir 日志文件存放的目录
  133. * @param int $writeType 日志文件的保存方式
  134. * @return void
  135. */
  136. public function __construct($logDir = '', $writeType = 0, $maxFileSize = 100) {
  137. $this->setLogDir($logDir);
  138. $this->_writeType = $writeType;
  139. $this->setMaxFileSize($maxFileSize);
  140. }
  141. /**
  142. * 添加info级别的日志信息
  143. *
  144. * @param string $msg 日志信息
  145. * @param string $type 日志的类型,默认为wind.system
  146. * @param boolean $flush 是否将日志输出到文件,为true的时候将写入文件,默认为false
  147. * @return void
  148. */
  149. public function info($msg, $type = 'wind.system', $flush = false) {
  150. $this->log($msg, self::LEVEL_INFO, $type, $flush);
  151. }
  152. /**
  153. * 添加trace级别的日志信息
  154. *
  155. * @param string $msg 日志信息
  156. * @param string $type 日志的类型,默认为wind.system
  157. * @param boolean $flush 是否将日志输出到文件,为true的时候将写入文件,默认为false
  158. * @return void
  159. */
  160. public function trace($msg, $type = 'wind.system', $flush = false) {
  161. $this->log($msg, self::LEVEL_TRACE, $type, $flush);
  162. }
  163. /**
  164. * 添加debug级别的日志信息
  165. *
  166. * @param string $msg 日志信息
  167. * @param string $type 日志的类型,默认为wind.system
  168. * @param boolean $flush 是否将日志输出到文件,为true的时候将写入文件,默认为false
  169. * @return void
  170. */
  171. public function debug($msg, $type = 'wind.system', $flush = false) {
  172. $this->log($msg, self::LEVEL_DEBUG, $type, $flush);
  173. }
  174. /**
  175. * 添加error级别的日志信息
  176. *
  177. * @param string $msg 日志信息
  178. * @param string $type 日志的类型,默认为wind.core
  179. * @param boolean $flush 是否将日志输出到文件,为true的时候将写入文件,默认为false
  180. * @return void
  181. */
  182. public function error($msg, $type = 'wind.core', $flush = false) {
  183. $this->log($msg, self::LEVEL_ERROR, $type, $flush);
  184. }
  185. /**
  186. * 添加profile级别的开始位置日志信息
  187. *
  188. * 通过该接口添加的日志信息将是记录一个开始位置的信息
  189. *
  190. * @param string $msg 日志信息
  191. * @param string $type 日志的类型,默认为wind.core
  192. * @param boolean $flush 是否将日志输出到文件,为true的时候将写入文件,默认为false
  193. * @return void
  194. */
  195. public function profileBegin($msg, $type = 'wind.core', $flush = false) {
  196. $this->log('begin:' . trim($msg), self::LEVEL_PROFILE, $type, $flush);
  197. }
  198. /**
  199. * 添加profile级别的结束位置日志信息
  200. *
  201. * 通过该接口添加的日志信息将是记录一个结束位置的信息
  202. *
  203. * @param string $msg 日志信息
  204. * @param string $type 日志的类型,默认为wind.core
  205. * @param boolean $flush 是否将日志输出到文件,为true的时候将写入文件,默认为false
  206. * @return void
  207. */
  208. public function profileEnd($msg, $type = 'wind.core', $flush = false) {
  209. $this->log('end:' . trim($msg), self::LEVEL_PROFILE, $type, $flush);
  210. }
  211. /**
  212. * 添加info级别的日志信息
  213. *
  214. * @param string $msg 日志信息
  215. * @param int $level 日志记录的级别,默认为INFO级别即为1
  216. * @param string $type 日志的类型,默认为wind.system
  217. * @param boolean $flush 是否将日志输出到文件,为true则将会写入文件,默认为false
  218. * @return void
  219. */
  220. public function log($msg, $level = self::LEVEL_INFO, $type = 'wind.system', $flush = false) {
  221. if ($this->_writeType & self::WRITE_TYPE)
  222. (count($this->_types) >= 5 || $this->_logCount >= $this->_autoFlush) && $this->flush();
  223. else
  224. $this->_logCount >= $this->_autoFlush && $this->flush();
  225. if ($level === self::LEVEL_PROFILE)
  226. $message = $this->_build($msg, $level, $type, microtime(true),
  227. $this->getMemoryUsage(false));
  228. elseif ($level === self::LEVEL_DEBUG)
  229. $message = $this->_build($msg, $level, $type, microtime(true));
  230. else
  231. $message = $this->_build($msg, $level, $type);
  232. $this->_logs[] = array($level, $type, $message);
  233. $this->_logCount++;
  234. if ($this->_writeType == self::WRITE_TYPE && !in_array($type, $this->_types)) $this->_types[] = $type;
  235. if ($flush) $this->flush();
  236. }
  237. /**
  238. * 将记录的日志列表信息写入文件
  239. *
  240. * @return boolean
  241. */
  242. public function flush() {
  243. if (empty($this->_logs)) return false;
  244. Wind::import('WIND:utility.WindFile');
  245. $_l = $_logTypes = $_logLevels = array();
  246. $_map = array(
  247. self::LEVEL_INFO => 'info',
  248. self::LEVEL_ERROR => 'error',
  249. self::LEVEL_DEBUG => 'debug',
  250. self::LEVEL_TRACE => 'trace',
  251. self::LEVEL_PROFILE => 'profile');
  252. foreach ($this->_logs as $key => $value) {
  253. $_l[] = $value[2];
  254. $_logTypes[$value[1]][] = $value[2];
  255. $_logLevels[$value[0]][] = $value[2];
  256. }
  257. if ($this->_writeType & 1) {
  258. foreach ($_logLevels as $key => $value) {
  259. if (!$fileName = $this->_getFileName($_map[$key])) continue;
  260. WindFile::write($fileName, join("", $value), 'a');
  261. }
  262. }
  263. if ($this->_writeType & 2) {
  264. foreach ($_logTypes as $key => $value) {
  265. if (!$fileName = $this->_getFileName($key)) continue;
  266. WindFile::write($fileName, join("", $value), 'a');
  267. }
  268. }
  269. if ($fileName = $this->_getFileName()) {
  270. WindFile::write($fileName, join("", $_l), 'a');
  271. }
  272. $this->_logs = array();
  273. $this->_logCount = 0;
  274. return true;
  275. }
  276. /**
  277. * 返回内存使用量
  278. *
  279. * @param boolean $peak 是否是内存峰值,默认为true
  280. * @return int
  281. */
  282. public function getMemoryUsage($peak = true) {
  283. if ($peak && function_exists('memory_get_peak_usage'))
  284. return memory_get_peak_usage();
  285. elseif (function_exists('memory_get_usage'))
  286. return memory_get_usage();
  287. $pid = getmypid();
  288. if (strncmp(PHP_OS, 'WIN', 3) === 0) {
  289. exec('tasklist /FI "PID eq ' . $pid . '" /FO LIST', $output);
  290. return isset($output[5]) ? preg_replace('/[\D]/', '', $output[5]) * 1024 : 0;
  291. } else {
  292. exec("ps -eo%mem,rss,pid | grep $pid", $output);
  293. $output = explode(" ", $output[0]);
  294. return isset($output[1]) ? $output[1] * 1024 : 0;
  295. }
  296. }
  297. /**
  298. * 组装日志信息
  299. *
  300. * @param string $msg 日志信息
  301. * @param int $level 日志级别
  302. * @param string $type 日志类型
  303. * @param int $timer 日志记录的时间,默认为0
  304. * @param int $mem 日志记录的时候内容使用情况,默认为0
  305. * @return string 构造好的信息字符串
  306. */
  307. private function _build($msg, $level, $type, $timer = 0, $mem = 0) {
  308. $result = '';
  309. switch ($level) {
  310. case self::LEVEL_INFO:
  311. $result = $this->_buildInfo($msg);
  312. break;
  313. case self::LEVEL_ERROR:
  314. $result = $this->_buildError($msg);
  315. break;
  316. case self::LEVEL_DEBUG:
  317. $result = $this->_buildDebug($msg);
  318. break;
  319. case self::LEVEL_TRACE:
  320. $result = $this->_buildTrace($msg);
  321. break;
  322. case self::LEVEL_PROFILE:
  323. $result = $this->_buildProfile($msg, $type, $timer, $mem);
  324. break;
  325. default:
  326. break;
  327. }
  328. return $result ? '[' . date('Y-m-d H:i:s') . '] ' . $result . "\r\n" : '';
  329. }
  330. /**
  331. * 构造profile信息格式
  332. *
  333. * @param string $msg 记录的信息
  334. * @param string $type 记录的信息类型
  335. * @param int $timer 记录的信息的时间
  336. * @param int $mem 记录的信息的时候内容使用情况
  337. * @return string 返回构造好的profile信息
  338. */
  339. private function _buildProfile($msg, $type, $timer, $mem) {
  340. $_msg = '';
  341. if (strncasecmp($msg, self::TOKEN_BEGIN, strlen(self::TOKEN_BEGIN)) == 0) {
  342. $_token = substr($msg, strlen(self::TOKEN_BEGIN));
  343. $_token = substr($_token, 0, strpos($_token, ':'));
  344. $this->_profiles[] = array(
  345. $_token,
  346. substr($msg, strpos($msg, ':', strlen(self::TOKEN_BEGIN)) + 1),
  347. $type,
  348. $timer,
  349. $mem);
  350. } elseif (strncasecmp(self::TOKEN_END, $msg, strlen(self::TOKEN_END)) == 0) {
  351. $_msg = "PROFILE! Message:";
  352. $_token = substr($msg, strlen(self::TOKEN_END));
  353. $_token = substr($_token, 0, strpos($_token, ':'));
  354. foreach ($this->_profiles as $key => $profile) {
  355. if ($profile[0] !== $_token) continue;
  356. if ($profile[1])
  357. $_msg .= "\r\n\t" . $profile[1];
  358. else
  359. $_msg .= "\r\n\t" . substr($msg, strpos($msg, ':', strlen(self::TOKEN_END)) + 1);
  360. $_msg .= "\r\n\tTime:" . ($timer - $profile[3]) . "\r\n\tMem:" . ($mem - $profile[4]) . "\r\n\tType:$profile[2]";
  361. break;
  362. unset($this->_profiles[$key]);
  363. }
  364. }
  365. return $_msg;
  366. }
  367. /**
  368. * 组装info级别的信息输出格式
  369. *
  370. * <code>
  371. * INFO! Message: $msg
  372. * </code>
  373. *
  374. * @param string $msg 输出的信息
  375. * @return string
  376. */
  377. private function _buildInfo($msg) {
  378. return "INFO! Message: " . $msg;
  379. }
  380. /**
  381. * 组装堆栈trace的信息输出格式
  382. *
  383. * <code>
  384. * TRACE! Message: $msg
  385. * #1 trace1
  386. * #2 trace2
  387. * </code>
  388. *
  389. * @param string $msg 输出的信息
  390. * @return string
  391. */
  392. private function _buildTrace($msg) {
  393. return "TRACE! Message: " . $msg . implode("\r\n", $this->_getTrace());
  394. }
  395. /**
  396. * 组装debug信息输出
  397. *
  398. * <code>
  399. * DEBUG! Message: $msg
  400. * #1 trace1
  401. * #2 trace2
  402. * </code>
  403. *
  404. * @param string $msg 输出的信息
  405. * @return string
  406. */
  407. private function _buildDebug($msg) {
  408. return 'DEBUG! Message: ' . $msg . implode("\r\n", $this->_getTrace());
  409. }
  410. /**
  411. *组装Error信息输出
  412. *
  413. * <code>
  414. * ERROR! Message: $msg
  415. * #1 trace1
  416. * #2 trace2
  417. * </code>
  418. *
  419. * @param string $msg 输出的错误信息
  420. * @return string
  421. */
  422. private function _buildError($msg) {
  423. return 'ERROR! Message: ' . $msg;
  424. }
  425. /**
  426. * 错误堆栈信息的获取及组装输出
  427. *
  428. * <code>
  429. * #1 trace
  430. * #2 trace
  431. * </code>
  432. *
  433. * @return string
  434. */
  435. private function _getTrace() {
  436. $num = 0;
  437. $info[] = 'Stack trace:';
  438. $traces = debug_backtrace();
  439. foreach ($traces as $traceKey => $trace) {
  440. if ($num >= 7) break;
  441. if ((isset($trace['class']) && $trace['class'] == __CLASS__) || isset($trace['file']) && strrpos(
  442. $trace['file'], __CLASS__ . '.php') !== false) continue;
  443. $file = isset($trace['file']) ? $trace['file'] . '(' . $trace['line'] . '): ' : '[internal function]: ';
  444. $function = isset($trace['class']) ? $trace['class'] . $trace['type'] . $trace['function'] : $trace['function'];
  445. if ($function == 'WindBase::log') continue;
  446. $args = array_map(array($this, '_buildArg'), $trace['args']);
  447. $info[] = '#' . ($num++) . ' ' . $file . $function . '(' . implode(',', $args) . ')';
  448. }
  449. return $info;
  450. }
  451. /**
  452. * 组装输出的trace中的参数组装
  453. *
  454. * @param mixed $arg 需要组装的信息
  455. * @return string 返回组装好的信息
  456. * <ul>
  457. * <li>如果是array: 返回 Array</li>
  458. * <li>如果是Object: 返回 Object classname</li>
  459. * <li>其他格式: 返回 $arg</li>
  460. * </ul>
  461. */
  462. private function _buildArg($arg) {
  463. switch (gettype($arg)) {
  464. case 'array':
  465. return 'Array';
  466. break;
  467. case 'object':
  468. return 'Object ' . get_class($arg);
  469. break;
  470. default:
  471. return "'" . $arg . "'";
  472. break;
  473. }
  474. }
  475. /**
  476. * 取得日志文件名
  477. *
  478. * @param string $suffix 日志文件的后缀,默认为空
  479. * @return string 返回日志文件名
  480. */
  481. private function _getFileName($suffix = '') {
  482. $_maxsize = ($this->_maxFileSize ? $this->_maxFileSize : 100) * 1024;
  483. $_logfile = $this->_logDir . '/log' . ($suffix ? '_' . $suffix : '') . '.txt';
  484. if (is_file($_logfile) && $_maxsize <= filesize($_logfile)) {
  485. do {
  486. $_newFile = $this->_logDir . '/log' . ($suffix ? '_' . $suffix : '') . '_' . time() . '.txt';
  487. } while (is_file($_newFile));
  488. @rename($_logfile, $_newFile);
  489. }
  490. return $_logfile;
  491. }
  492. /**
  493. * 设置日志保存的路径
  494. *
  495. * @param string $logFile 日志保存的路径
  496. * @return void
  497. */
  498. public function setLogDir($logDir) {
  499. $this->_logDir = Wind::getRealDir($logDir);
  500. WindFolder::mkRecur($this->_logDir);
  501. }
  502. /**
  503. * 设置日志文件最大的大小
  504. *
  505. * @param int $_maxFileSize 文件的最大值
  506. * @return void
  507. */
  508. public function setMaxFileSize($maxFileSize) {
  509. $this->_maxFileSize = (int) $maxFileSize;
  510. }
  511. }