PageRenderTime 52ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/Lib/Base/Service/Profiler/Log.php

https://github.com/ilyich/iqyou
PHP | 577 lines | 390 code | 64 blank | 123 comment | 31 complexity | ff00e4234c3215d61f400fc5fa7c320e MD5 | raw file
  1. <?php
  2. class Base_Service_Profiler_Log
  3. {
  4. const LOG_TYPE_GENERAL = 0;
  5. const LOG_TYPE_DB = 1;
  6. const LOG_TYPE_MC = 2;
  7. const LOG_TYPE_DALD = 3;
  8. const LOG_TYPE_SQ = 4;
  9. const LOG_TYPE_LEMON = 5;
  10. const LOG_TYPE_EMAIL = 6;
  11. const LOG_TYPE_MONGO = 7;
  12. const LOG_TYPE_ER = 8;
  13. const LOG_TYPE_STAT = 9;
  14. const LOG_TYPE_PACMAN = 10;
  15. const DATA_ROWS = 'rows';
  16. const DATA_SIZE = 'size';
  17. const DATA_METAMSG = 'meta';
  18. public static $enabled = false;
  19. private static $_enabled = false;
  20. private static $_xDebugProperties = array(
  21. 'xdebug.var_display_max_children' => -1,
  22. 'xdebug.var_display_max_data' => -1,
  23. 'xdebug.var_display_max_depth' => -1,
  24. );
  25. public static function reinitXdebug() {
  26. if (extension_loaded('xdebug')) {
  27. foreach (self::$_xDebugProperties as $propName => $propVal) {
  28. $savedConfigs[$propName] = ini_get($propName);
  29. ini_set($propName, $propVal);
  30. }
  31. }
  32. }
  33. private static $_logLimit = 100000;
  34. private static $_logCount = 0;
  35. private static $_log = array();
  36. private static $_dumps = array();
  37. private static $_server = array();
  38. private static $_types = array(
  39. self::LOG_TYPE_GENERAL => array(
  40. 'title' => 'ALL', // Заголовок, с которым будет отображаться в профайлере
  41. 'lTime' => 0, // Допустимое значение времени выполнения, при привышении которого запись будет красной
  42. 'lSize' => 0, // Допустимое значение размера результата в байтах, при привышении которого запись будет красной
  43. 'lRows' => 0, // Допустимое значение затронутых событием строк, при привышении которого запись будет красной
  44. ),
  45. self::LOG_TYPE_DB => array(
  46. 'title' => 'DB',
  47. 'desc' => 'Data Base',
  48. 'lTime' => 0.01,
  49. 'lSize' => 0,
  50. 'lRows' => 200,
  51. ),
  52. self::LOG_TYPE_MC => array(
  53. 'title' => 'MC',
  54. 'desc' => 'Memcache',
  55. 'lTime' => 0.01,
  56. 'lSize' => 4096,
  57. 'lRows' => 0,
  58. ),
  59. self::LOG_TYPE_DALD => array(
  60. 'title' => 'DALD',
  61. 'desc' => 'Dald',
  62. 'lTime' => 0.01,
  63. 'lSize' => 0,
  64. 'lRows' => 0,
  65. ),
  66. self::LOG_TYPE_SQ => array(
  67. 'title' => 'SQ',
  68. 'lTime' => 0,
  69. 'lSize' => 0,
  70. 'lRows' => 0,
  71. ),
  72. self::LOG_TYPE_STAT => array(
  73. 'title' => 'STATS',
  74. 'lTime' => 0,
  75. 'lSize' => 0,
  76. 'lRows' => 0,
  77. ),
  78. self::LOG_TYPE_PACMAN => array(
  79. 'title' => 'Pacman',
  80. 'lTime' => 0,
  81. 'lSize' => 0,
  82. 'lRows' => 0,
  83. ),
  84. self::LOG_TYPE_LEMON => array(
  85. 'title' => 'LEM',
  86. 'desc' => 'Lemon',
  87. 'lTime' => 0.01,
  88. 'lSize' => 4096,
  89. 'lRows' => 0,
  90. ),
  91. self::LOG_TYPE_EMAIL => array(
  92. 'title' => 'EMAIL',
  93. 'desc' => 'Emails',
  94. 'lTime' => 0,
  95. 'lSize' => 0,
  96. 'lRows' => 0,
  97. ),
  98. self::LOG_TYPE_MONGO => array(
  99. 'title' => 'MONGO',
  100. 'desc' => 'Mongo DB',
  101. 'lTime' => 0,
  102. 'lSize' => 0,
  103. 'lRows' => 0,
  104. ),
  105. self::LOG_TYPE_ER => array(
  106. 'title' => 'ER',
  107. 'desc' => 'External Request',
  108. 'lTime' => 0.1,
  109. 'lSize' => 16384,
  110. 'lRows' => 0,
  111. ),
  112. );
  113. private static $_xhprof = null;
  114. /**
  115. * @return bool
  116. */
  117. public static function getEnabled()
  118. {
  119. return self::$_enabled;
  120. }
  121. /**
  122. * @param $enabled
  123. */
  124. public static function setEnable($enabled)
  125. {
  126. if (defined('TESTING')) {
  127. return;
  128. }
  129. $enabled = (bool) $enabled;
  130. self::$_enabled = $enabled;
  131. self::$enabled = self::$_enabled;
  132. }
  133. /**
  134. * Активен новый профайлер или нет
  135. *
  136. * @return bool
  137. */
  138. public static function isFs2ProfEnabled ()
  139. {
  140. return self::$_enabled;
  141. }
  142. /**
  143. * @param int $type Тип записи
  144. * @param int $ts Время начала выполнения журналируемого участка
  145. * @param string $msg Сообщение журнала
  146. * @param string $caller Метод, в котором произошол вызов
  147. * @param int $resultSize Размер полученного результата в байтах
  148. * @param int $resultRows Кол-во строк в полученном результате
  149. * @param string $metaMsg Дополнительная информация
  150. * @param array $data
  151. *
  152. * @internal param int $rows
  153. * @return bool
  154. */
  155. public static function log($type, $ts, $msg, $caller = null, $resultSize = 0, $resultRows = 0, $metaMsg = '', $data = array())
  156. {
  157. self::reinitXdebug();
  158. if (!self::getEnabled() || self::$_logCount >= self::$_logLimit) {
  159. return false;
  160. }
  161. self::$_log[] = array(
  162. 'type' => $type,
  163. 'ts' => microtime(true) - $ts,
  164. 'memory' => round(memory_get_peak_usage() / 1024 / 1024, 2), // in Mb
  165. 'msg' => $msg,
  166. 'caller' => $caller,
  167. 'size' => $resultSize,
  168. 'rows' => $resultRows,
  169. 'meta' => $metaMsg,
  170. 'data' => $data
  171. );
  172. self::$_logCount++;
  173. return true;
  174. }
  175. /**
  176. * @param $message
  177. */
  178. public static function logGeneral($message)
  179. {
  180. self::log(self::LOG_TYPE_GENERAL, microtime(1), $message);
  181. }
  182. /**
  183. * @param $query
  184. *
  185. * @return string
  186. */
  187. public static function getQueryHash($query)
  188. {
  189. $config = Base_Application::getInstance()->config['db']['connect'];
  190. $hashValue = '';
  191. foreach ($config as $db => $record) {
  192. foreach ($record as $host => $data) {
  193. $realValues = parse_url($data);
  194. $hashValue = @md5($realValues['user'] . '_' . $realValues['pass']);
  195. break 2;
  196. }
  197. }
  198. return md5('_afioawfhipawfhipgs_' . $query . '_AFWEOUHOAUWEGHuofef_' . $hashValue);
  199. }
  200. /**
  201. * @param $ts
  202. * @param $dbName
  203. * @param $dbHost
  204. * @param $method
  205. * @param $query
  206. * @param int $count
  207. */
  208. public static function profilerDb($ts, $dbName, $dbHost, $method, $query, $count = 0)
  209. {
  210. $meta = $dbName . ':' . $dbHost;
  211. $data = array('hash' => self::getQueryHash($query));
  212. self::log(self::LOG_TYPE_DB, $ts, (string)$query, $method, 0, $count, $meta, $data);
  213. }
  214. /**
  215. * @param $ts
  216. * @param $method
  217. * @param $query
  218. * @param string $keys
  219. * @param string $value
  220. */
  221. public static function profilerMc($ts, $method, $query, $keys = '', $value = '')
  222. {
  223. $resultSize = is_array($value) || is_object($value) ? Utf::strlen(serialize($value)) : Utf::strlen($value);
  224. if ($query == 'connect') { // обнуляем ключи
  225. $keys = array();
  226. } elseif (is_array($keys)) { // чтобы на клиент уходил массив а не обьект - перестрахуемся и обновим ключи
  227. $keys = array_values($keys);
  228. } else { // если ключ один - все равно массивом отдаем
  229. $keys = (array) $keys;
  230. }
  231. self::log(self::LOG_TYPE_MC, $ts, $query, $method, $resultSize, count($keys), '', array('keys' => $keys));
  232. }
  233. /**
  234. * @param $ts
  235. * @param $method
  236. * @param $query
  237. * @param string $keys
  238. * @param string $value
  239. * @param int $lemonType
  240. */
  241. public static function profilerLemon($ts, $method, $query, $keys = '', $value = '', $lemonType = 1)
  242. {
  243. $resultSize = is_array($value) || is_object($value) ? Utf::strlen(serialize($value)) : Utf::strlen($value);
  244. $keys = (array) $keys;
  245. $lemonTypes = array(
  246. 2 => 'Lemon2',
  247. 3 => 'Ab',
  248. 4 => 'Bigfoot',
  249. 5 => 'FriendsActivity',
  250. 6 => 'Int2',
  251. 7 => 'IntBig',
  252. 8 => 'Retentions'
  253. );
  254. self::log(
  255. self::LOG_TYPE_LEMON, $ts, $query, $method, $resultSize, count($keys),
  256. array_key_exists($lemonType, $lemonTypes) ? $lemonTypes[$lemonType] : '',
  257. array('keys' => $keys, 'lemonType' => $lemonType)
  258. );
  259. }
  260. /**
  261. * @param $ts
  262. * @param $method
  263. * @param $query
  264. * @param $index
  265. * @param $value
  266. * @param string $server
  267. * @param int $countServerAnswer
  268. * @param int $countNoEmpty
  269. */
  270. public static function profilerSharedQueue($ts, $method, $query, $index, $value, $server = '', $countServerAnswer = 0, $countNoEmpty = 0)
  271. {
  272. $resultSize = 0;
  273. $resultRows = 0;
  274. $data = '';
  275. switch ($query) {
  276. case 'PUSH' : {
  277. $data = serialize($value);
  278. $resultSize = Utf::strlen($data);
  279. if ($resultSize > 200) {
  280. $key = Utf::substr($data, 0, 200) . '...';
  281. }
  282. break;
  283. }
  284. case 'POP' : {
  285. $resultRows = count($value);
  286. break;
  287. }
  288. case 'POPALL' : {
  289. $resultRows = count($value);
  290. break;
  291. }
  292. }
  293. $message = $query . ($query == 'PUSH' ? ' to ' : ' from ') . $index . (empty($data) ? '' : ' [' . $data . ']');
  294. self::log(self::LOG_TYPE_SQ, $ts, $message, $method, $resultSize, $resultRows);
  295. }
  296. /**
  297. * @param $ts
  298. * @param $client
  299. * @param $host
  300. * @param $method
  301. * @param $query
  302. */
  303. public static function profilerDald($ts, $client, $host, $method, $query)
  304. {
  305. $meta = $client . ':' . $host;
  306. self::log(self::LOG_TYPE_DALD, $ts, $query, $method, 0, 0, $meta);
  307. }
  308. /**
  309. * @param $ts
  310. * @param $typeId
  311. * @param $eventId
  312. * @param $userId
  313. * @param $emails
  314. * @param $isSystem
  315. * @param $viaPhpMail
  316. */
  317. public static function profilerEmail($ts, $typeId, $eventId, $userId, $emails, $isSystem, $viaPhpMail)
  318. {
  319. $message = http_build_query(array(
  320. 'typrId' => $typeId,
  321. 'eventId' => $eventId,
  322. 'userId' => $userId,
  323. 'emails' => implode(',', $emails),
  324. 'system' => (int)$isSystem,
  325. 'phpmail' => (int)$viaPhpMail
  326. ));
  327. self::log(self::LOG_TYPE_EMAIL, $ts, $message);
  328. }
  329. /**
  330. * @param $ts
  331. * @param $dbName
  332. * @param $dbHost
  333. * @param $method
  334. * @param $query
  335. * @param int $count
  336. */
  337. public static function profilerMongo($ts, $dbName, $dbHost, $method, $query, $count = 0)
  338. {
  339. $meta = $dbName . ':' . $dbHost;
  340. $data = array('hash' => self::getQueryHash($query));
  341. self::log(self::LOG_TYPE_MONGO, $ts, (string)$query, $method, 0, $count, $meta, $data);
  342. }
  343. /**
  344. * Метод учета чтения внешних данных через http (file_get_content, curl)
  345. *
  346. * @param int $ts
  347. * @param string $caller
  348. * @param string $msg
  349. * @param string $requestType - имя метода, через который было произведено получение данных
  350. * @param int $responseSize
  351. * @param array $requestParams
  352. */
  353. public static function profilerExternalRequest($ts, $caller, $msg, $requestType, $responseSize,
  354. array $requestParams = array()
  355. )
  356. {
  357. self::log(self::LOG_TYPE_ER, $ts, $msg, $caller, $responseSize, 0, $requestType, $requestParams);
  358. }
  359. /**
  360. * @param mixed $var
  361. */
  362. public static function dump($var)
  363. {
  364. if (PRODUCTION && !Base_Service_Common::isStage(false)) {
  365. $trace = Base_Service_Log::getTrace(3);
  366. if (is_array($trace)) {
  367. $traceStr = ' from ' . (isset($trace[0]) ? $trace[0] : ' xz') . ' from ' . (isset($trace[1]) ? $trace[1] : ' xz');
  368. } else {
  369. $traceStr = 'no trace';
  370. }
  371. trigger_error(__METHOD__ . ' called on production server ' . $traceStr);
  372. return;
  373. }
  374. $savedConfigs = array();
  375. if (extension_loaded('xdebug')) {
  376. foreach (self::$_xDebugProperties as $propName => $propVal) {
  377. $savedConfigs[$propName] = ini_get($propName);
  378. ini_set($propName, $propVal);
  379. }
  380. }
  381. ob_start();
  382. var_dump($var); // production-ok
  383. $output = ob_get_clean();
  384. if (!extension_loaded('xdebug')) {
  385. $output = preg_replace("/\]\=\>\n(\s+)/m", "] => ", $output);
  386. $output = htmlspecialchars($output);
  387. $output = '<pre>' . $output . '</pre>';
  388. } else {
  389. foreach ($savedConfigs as $propName => $propVal) {
  390. ini_set($propName, $propVal);
  391. }
  392. }
  393. self::$_dumps[] = $output;
  394. }
  395. /**
  396. * Получить массив с журналом
  397. * @return array
  398. */
  399. public static function getLogArray()
  400. {
  401. return self::getEnabled() ? self::$_log : array();
  402. }
  403. public static function profilerWd($_ts, $widgetName, $widgetId = 0, $cached = false)
  404. {
  405. // Do nothing
  406. }
  407. /**
  408. * Получить журнал как json
  409. *
  410. * @return string
  411. */
  412. public static function getLogJson()
  413. {
  414. if (!self::getEnabled()) {
  415. return '';
  416. }
  417. $dumps = array();
  418. foreach (self::$_dumps as $str) {
  419. $dumps[] = iconv('CP1251', 'UTF-8', $str);
  420. }
  421. $result = array(
  422. 'url' => $_SERVER['REQUEST_URI'],
  423. 'method' => $_SERVER['REQUEST_METHOD'],
  424. 'status' => '',
  425. 'types' => self::$_types,
  426. 'params' => '',
  427. 'total' => array(
  428. 'time' => microtime(true) - $GLOBALS['time_start'],
  429. 'memory' => round(memory_get_peak_usage(true) / 1024, 2),
  430. ),
  431. 'events' => array(),
  432. 'dumps' => $dumps,
  433. 'cstats' => array(
  434. 'service' => Service_StatsFuncToId::getServiceByUrl($_SERVER['REQUEST_URI']),
  435. 'url' => Service_StatsFuncToId::parseUrl($_SERVER['REQUEST_URI'])
  436. ),
  437. 'xhprof' => self::getXHProfData(),
  438. 'server' => self::getServer(),
  439. );
  440. // определим нарушение ограничений
  441. foreach (self::$_log as $log) {
  442. $typeInfo = self::$_types[$log['type']];
  443. if (!empty($typeInfo['lTime']) && $log['ts'] >= $typeInfo['lTime']) {
  444. $log['bold'][] = 'time';
  445. }
  446. if (!empty($typeInfo['lSize']) && $log['ts'] >= $typeInfo['lSize']) {
  447. $log['bold'][] = 'size';
  448. }
  449. if (!empty($typeInfo['lRows']) && $log['ts'] >= $typeInfo['lRows']) {
  450. $log['bold'][] = 'rows';
  451. }
  452. $result['events'][] = $log;
  453. }
  454. Utf::toUtfRecursive($result);
  455. $result = json_encode($result);
  456. if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') {
  457. $result = '<![CDATA[profiler:'.$result.']]>';
  458. } else {
  459. $result = '<script>var __profilerData = '.$result.'</script>';
  460. }
  461. return $result;
  462. }
  463. /**
  464. * For test only
  465. * @return string
  466. */
  467. public static function getLogHtml()
  468. {
  469. $html = '';
  470. if (self::getEnabled()) {
  471. $html = '<div id="newprof">';
  472. $html .= '<p>' . str_repeat('=', 250) . '</p>';
  473. foreach (self::$_log as $log) {
  474. $type = self::$_types[$log['type']];
  475. $html .= '<p>';
  476. $html .= number_format($log['ts'], 6) . ' > ';
  477. $html .= $type['title'] . ' ';
  478. $html .= $log['msg'];
  479. $html .= '</p>';
  480. }
  481. $html .= '</div>';
  482. }
  483. $css = '<style>
  484. #newprof { font-size: 11px }
  485. #newprof p { margin: 0; padding: 0; line-height: 14px; }
  486. </style>';
  487. return $css . $html;
  488. }
  489. /**
  490. * @param $data mixed
  491. */
  492. public static function setXHProfData ($data)
  493. {
  494. self::$_xhprof = $data;
  495. }
  496. /**
  497. * @return mixed
  498. */
  499. public static function getXHProfData ()
  500. {
  501. return self::$_xhprof;
  502. }
  503. public static function getServer() {
  504. return self::$_server;
  505. }
  506. public static function setServer($ip, $name, $group) {
  507. self::$_server = array(
  508. 'ip' => $ip,
  509. 'name' => $name,
  510. 'group' => $group,
  511. );
  512. }
  513. }