Debug.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  1. <?php
  2. Lib::loadClass('RouteBase');
  3. /**
  4. * TODO: debug to file based on session_id (/tmp/se-debug-{$date("Y-m-d")}-{$login}_{$ip}_{$session_id}.log)
  5. * TODO: function if loggind enabled, then save to file and print in default page
  6. * - DBG::log(string $msg or Exception $e)
  7. * TODO: better wfs log by fingerprint (User::getLogin() + IP + UserAgent)
  8. */
  9. class Route_Debug extends RouteBase {
  10. public function handleAuth() {
  11. if (!User::logged()) {
  12. throw new HttpException('Unauthorized', 401);
  13. }
  14. if (!User::hasAccess('dbg')) {// User::get('ADM_ADMIN_LEVEL') == 0
  15. throw new HttpException('Unauthorized - required dbg access', 401);
  16. }
  17. }
  18. public function defaultAction() {
  19. session_write_close();
  20. $this->defaultView();
  21. }
  22. public function defaultView($flashMsg = '') {
  23. UI::gora();
  24. UI::menu();
  25. UI::setTitle("Debug");
  26. if (!empty($_POST)) {
  27. UI::alert('info', "is POST - history.pushState");
  28. echo UI::h('script', [], "
  29. history.replaceState(null, 'Debug', window.location.href);
  30. ");
  31. }
  32. try {
  33. echo UI::h('h1', [], [
  34. "Debug ",
  35. UI::h('small', [], "(" . (DBG::isActive() ? "włączony" : "wyłączony") . ") "),
  36. UI::hButtonPost((DBG::isActive()) ? "Wyłącz debug" : "Włącz debug", [
  37. 'class' => "btn-success btn-xs",
  38. 'data' => [
  39. '_route' => 'Debug',
  40. '_task' => DBG::isActive() ? 'deactivateDebug' : 'activateDebug'
  41. ]
  42. ])
  43. ]);
  44. echo $flashMsg;
  45. // DBG::nicePrint($_SERVER, '$_SERVER');
  46. // DBG::nicePrint([
  47. // 'date' => date("Y-m-d"),
  48. // 'login' => User::getLogin(),
  49. // 'ip' => Request::getUserIp(),
  50. // 'session_id' => session_id()
  51. // ], 'dbg');
  52. UI::table([
  53. 'caption' => "Log Files " . UI::h('a', [
  54. 'href' => "index.php?_route=Debug&_task=viewLatestUserLog",
  55. 'class' => "btn btn-xs btn-primary"
  56. ], "Pokaż ostatni log") . " " . UI::hButtonPost("Usuń wszystkie swoje pliki", [
  57. 'class' => "btn-danger btn-xs",
  58. 'data' => [
  59. '_route' => 'Debug',
  60. '_task' => 'rmAllUserLogFiles'
  61. ]
  62. ]) . " " . UI::hButtonPost("Usuń stare pliki", [
  63. 'class' => "btn-warning btn-xs",
  64. 'data' => [
  65. '_route' => 'Debug',
  66. '_task' => 'rmOldUserLogFiles'
  67. ]
  68. ]),
  69. 'cols' => [
  70. '#',
  71. 'file',
  72. 'user',
  73. 'request date',
  74. 'ip',
  75. 'rm',
  76. ],
  77. 'rows' => array_map(
  78. function ($logFile) {
  79. // /tmp/se-debug-2017-01-25-plabudda-192.168.61.206-4qqrd0.log
  80. try {
  81. if ('/tmp/se-debug-' != substr($logFile, 0, strlen('/tmp/se-debug-'))) throw new Exception("Wrong log file name '{$logFile}'");
  82. if ('.log' != substr($logFile, -4)) throw new Exception("Wrong log file name extension '{$logFile}'");
  83. $logName = substr($logFile, strlen('/tmp/se-debug-'), -4);
  84. list($logYear, $logMonth, $logDay, $logUser, $logIP, $logSessId, $logReqDate) = explode('-', $logName);
  85. return [
  86. '#' => UI::h('a', [
  87. 'href' => "index.php?_route=Debug&_task=viewLog&name={$logName}"
  88. ], "Pokaż"),
  89. 'request date' => ($logReqDate) ? date("Y-m-d H:i:s", $logReqDate) : '',// 1485775975
  90. 'file' => $logFile,
  91. 'user' => $logUser,
  92. 'ip' => $logIP,
  93. 'rm' => UI::hButtonPost("Usuń", [
  94. 'class' => 'btn-default btn-xs',
  95. 'data' => [
  96. '_route' => 'Debug',
  97. '_task' => 'rmLogFile',
  98. 'logName' => $logName
  99. ]
  100. ]),
  101. ];
  102. } catch (Exception $e) {
  103. return [
  104. '#' => '',
  105. 'file' => $e->getMessage(),
  106. ];
  107. }
  108. }
  109. , glob("/tmp/se-debug-*.log", GLOB_NOSORT)
  110. )
  111. ]);
  112. echo UI::hButtonPost("Test dbg with sleep", [
  113. 'class' => "btn-warning btn-xs",
  114. 'data' => [
  115. '_route' => 'Debug',
  116. '_task' => 'testDebugWithSleep'
  117. ]
  118. ]);
  119. } catch (Exception $e) {
  120. UI::alert('danger', $e->getMessage());
  121. DBG::log($e);
  122. }
  123. UI::dol();
  124. }
  125. public function viewLatestUserLogAction() {
  126. session_write_close();
  127. UI::gora();
  128. // UI::menu();
  129. UI::setTitle("Debug");
  130. echo UI::h('div', ['class'=>'container'], [
  131. UI::h('a', ['href'=>'index.php?_route=Debug'], "wróć")
  132. ]);
  133. try {
  134. $filerUser = V::get('user', '', $_REQUEST);// TODO: show another user debug
  135. $logName = V::get('name', '', $_REQUEST);
  136. if (!$logName) {
  137. $today = date("Y-m-d");
  138. $cmd = "ls -1rt /tmp/se-debug-{$today}-*.log | tail -5";
  139. V::exec($cmd, $out, $ret);
  140. if (empty($out)) {
  141. UI::alert('warning', "No logs today. Searching previous...");
  142. $cmd = "ls -1rt /tmp/se-debug-*.log | tail -5";
  143. V::exec($cmd, $out, $ret);
  144. if (empty($out)) throw new Exception("Log files not found");
  145. }
  146. echo UI::h('div', ['class'=>"alert alert-info"], [
  147. "Last log files",
  148. " (return code: {$ret})",
  149. "<br>cmd: <code>{$cmd}</code>",
  150. UI::h('pre', [], implode("\n", $out))
  151. ]);
  152. $logName = end($out);// /tmp/se-debug-2017-01-30-plabudda-192.168.61.206-4qqrd0-1485775975.log
  153. {
  154. if ('/tmp/se-debug-' != substr($logName, 0, strlen('/tmp/se-debug-'))) throw new Exception("Wrong log name prefix");
  155. if ('.log' != substr($logName, -1 * strlen('.log'))) throw new Exception("Wrong log name suffix");
  156. $logName = substr($logName, strlen('/tmp/se-debug-'), -1 * strlen('.log'));
  157. }
  158. }
  159. $this->printLogFileView($logName);
  160. } catch (Exception $e) {
  161. UI::alert('danger', $e->getMessage());
  162. }
  163. UI::dol();
  164. }
  165. public function viewLogAction() {
  166. session_write_close();
  167. UI::gora();
  168. // UI::menu();
  169. UI::setTitle("Debug");
  170. echo UI::h('div', ['class'=>'container'], [
  171. UI::h('a', ['href'=>'index.php?_route=Debug'], "wróć")
  172. ]);
  173. try {
  174. $logName = V::get('name', '', $_REQUEST);
  175. $this->printLogFileView($logName);
  176. } catch (Exception $e) {
  177. UI::alert('danger', $e->getMessage());
  178. }
  179. UI::dol();
  180. }
  181. public function printLogFileView($logName) {
  182. if (empty($logName)) throw new Exception("Missing name");
  183. $logName = $this->validateParamLogName($logName);
  184. $logPath = "/tmp/se-debug-{$logName}.log";
  185. if (!file_exists($logPath)) throw new Exception("Log file not exists");
  186. $content = file_get_contents($logPath);
  187. UI::table([
  188. 'cols' => [
  189. 'date',
  190. 'type',
  191. 'msg',
  192. 'log',
  193. 'trace',
  194. ],
  195. 'cols_help' => [
  196. 'trace' => "Cick to show trace"
  197. ],
  198. 'rows' => array_map(
  199. function ($line) {
  200. if (empty($line)) return [];
  201. $dbg = @json_decode($line, $assoc = true);
  202. if (null == $dbg && 0 !== json_last_error()) {
  203. return [
  204. 'type' => 'decode json error',
  205. 'msg' => "Error Processing Request - Parse json error: " . json_last_error()
  206. ];
  207. }
  208. return [
  209. 'date' => '<nobr>' . $dbg['date'] . '</nobr>',
  210. '@style[date]' => "width:1%",
  211. 'type' => $dbg['type'],
  212. '@style[type]' => "width:1%",
  213. 'msg' => UI::h('div', ['style'=>'max-width:360px; overflow-x:auto'], $dbg['msg']),
  214. // 'log' => (!empty($dbg['log'])) ? json_encode($dbg['log']) : '',
  215. '@style[msg]' => "width:360px",
  216. 'log' => UI::h('div', [], [
  217. UI::h('div', [
  218. 'title' => htmlspecialchars( ('sql' == $dbg['type'] && is_string($dbg['log'])) ? $dbg['log'] : var_export($dbg['log'], true) ),
  219. 'onClick' => "return p5DBG__showLogTrace(this, event, '600px')",
  220. 'style' => "cursor:pointer"
  221. ], str_replace(array('\n', '\t'), ' ', substr(htmlspecialchars(json_encode($dbg['log'])), 0, 100)) . ' ...')
  222. ]),
  223. 'trace' => UI::h('div', [], [
  224. UI::h('div', [
  225. 'title' => htmlspecialchars($dbg['trace']),
  226. 'onClick' => "return p5DBG__showLogTrace(this, event)",
  227. 'style' => "cursor:pointer"
  228. ], ('Exception' == $dbg['type'])
  229. ? "Code: {$dbg['log']['code']}, File: {$dbg['log']['file']}"
  230. : '...'
  231. )
  232. ]),
  233. ];
  234. },
  235. explode("\n", $content)
  236. ),
  237. ]);
  238. echo UI::h('script', [], "
  239. function p5DBG__showLogTrace(n, e, maxWidth) {
  240. var maxWidth = maxWidth || null
  241. if (!e) return false;
  242. if (e.target && 'PRE' == e.target.tagName) return false;
  243. var preNode = n.parentNode.lastChild
  244. if (preNode.tagName == 'PRE') {
  245. if ('none' == preNode.style.display) {
  246. preNode.style.display = 'block'
  247. } else {
  248. preNode.style.display = 'none'
  249. }
  250. } else {
  251. var pre = document.createElement('pre')
  252. pre.appendChild( document.createTextNode(n.title) )
  253. if (maxWidth) pre.style.maxWidth = maxWidth
  254. pre.style.fontSize = 'x-small'
  255. n.parentNode.appendChild(pre)
  256. }
  257. }
  258. ");
  259. }
  260. public function activateDebugAction() {
  261. DBG::activate();
  262. $this->defaultView();
  263. }
  264. public function deactivateDebugAction() {
  265. DBG::deactivate();
  266. $this->defaultView();
  267. }
  268. public function testDebugWithSleepAction() {
  269. session_write_close();
  270. UI::gora();
  271. UI::setTitle("Debug Test Sleep");
  272. flush();
  273. for ($i = 0; $i < 10; $i++) {
  274. echo "TEST {$i}<br>";
  275. DBG::log("TEST {$i}");
  276. flush();
  277. sleep(2);
  278. }
  279. echo "DONE";
  280. DBG::log("DONE");
  281. UI::dol();
  282. }
  283. public function validateParamLogName($logName) {
  284. if (empty($logName)) throw new Exception("Missing log file name");
  285. if (!preg_match('/^[\-\.a-zA-Z0-9]+$/', $logName)) throw new Exception("Wrong log file name format");
  286. return $logName;
  287. }
  288. public function rmAllLogFilesAction() {
  289. session_write_close();
  290. try {
  291. $today = date("Y-m-d");
  292. $cmd = "rm -v /tmp/se-debug-*.log 2>&1";
  293. V::exec($cmd, $out, $ret);
  294. $this->defaultView(UI::h('div', ['class'=>"alert alert-success alert-dismissible"], [
  295. '<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>',
  296. (0 === $ret) ? "All Log Files Removed" : "Error?",
  297. " (return code: {$ret})",
  298. "<br>cmd: <code>{$cmd}</code>",
  299. UI::h('pre', [], implode("\n", $out))
  300. ]));
  301. } catch (AlertSuccessException $e) {
  302. $this->defaultView(UI::h('div', ['class'=>"alert alert-success"], $e->getMessage()));
  303. } catch (AlertWarningException $e) {
  304. $this->defaultView(UI::h('div', ['class'=>"alert alert-warning"], $e->getMessage()));
  305. } catch (Exception $e) {
  306. $this->defaultView(UI::h('div', ['class'=>"alert alert-danger"], $e->getMessage()));
  307. }
  308. }
  309. public function rmOldLogFilesAction() {
  310. session_write_close();
  311. try {
  312. $today = date("Y-m-d");
  313. $cmd = "ls -1 /tmp/se-debug-*.log | grep -v '/tmp/se-debug-{$today}-' | xargs rm -v 2>&1";
  314. V::exec($cmd, $out, $ret);
  315. $this->defaultView(UI::h('div', ['class'=>"alert alert-success alert-dismissible"], [
  316. '<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>',
  317. (0 === $ret) ? "Old Log Files Removed" : "Error?",
  318. " (return code: {$ret})",
  319. "<br>cmd: <code>{$cmd}</code>",
  320. UI::h('pre', [], implode("\n", $out))
  321. ]));
  322. } catch (AlertSuccessException $e) {
  323. $this->defaultView(UI::h('div', ['class'=>"alert alert-success"], $e->getMessage()));
  324. } catch (AlertWarningException $e) {
  325. $this->defaultView(UI::h('div', ['class'=>"alert alert-warning"], $e->getMessage()));
  326. } catch (Exception $e) {
  327. $this->defaultView(UI::h('div', ['class'=>"alert alert-danger"], $e->getMessage()));
  328. }
  329. }
  330. public function rmAllUserLogFilesAction() {
  331. session_write_close();
  332. try {
  333. $userLogin = User::getLogin();
  334. $today = date("Y-m-d");
  335. $cmd = "rm -v /tmp/se-debug-*-{$userLogin}-*.log 2>&1";
  336. V::exec($cmd, $out, $ret);
  337. $this->defaultView(UI::h('div', ['class'=>"alert alert-success alert-dismissible"], [
  338. '<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>',
  339. (0 === $ret) ? "All Log Files Removed" : "Error?",
  340. " (return code: {$ret})",
  341. "<br>cmd: <code>{$cmd}</code>",
  342. UI::h('pre', [], implode("\n", $out))
  343. ]));
  344. } catch (AlertSuccessException $e) {
  345. $this->defaultView(UI::h('div', ['class'=>"alert alert-success"], $e->getMessage()));
  346. } catch (AlertWarningException $e) {
  347. $this->defaultView(UI::h('div', ['class'=>"alert alert-warning"], $e->getMessage()));
  348. } catch (Exception $e) {
  349. $this->defaultView(UI::h('div', ['class'=>"alert alert-danger"], $e->getMessage()));
  350. }
  351. }
  352. public function rmOldUserLogFilesAction() {
  353. session_write_close();
  354. try {
  355. $userLogin = User::getLogin();
  356. $today = date("Y-m-d");
  357. $cmd = "ls -1 /tmp/se-debug-*-{$userLogin}-*.log | grep -v '/tmp/se-debug-{$today}-' | xargs rm -v 2>&1";
  358. V::exec($cmd, $out, $ret);
  359. $this->defaultView(UI::h('div', ['class'=>"alert alert-success alert-dismissible"], [
  360. '<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>',
  361. (0 === $ret) ? "Old Log Files Removed" : "Error?",
  362. " (return code: {$ret})",
  363. "<br>cmd: <code>{$cmd}</code>",
  364. UI::h('pre', [], implode("\n", $out))
  365. ]));
  366. } catch (AlertSuccessException $e) {
  367. $this->defaultView(UI::h('div', ['class'=>"alert alert-success"], $e->getMessage()));
  368. } catch (AlertWarningException $e) {
  369. $this->defaultView(UI::h('div', ['class'=>"alert alert-warning"], $e->getMessage()));
  370. } catch (Exception $e) {
  371. $this->defaultView(UI::h('div', ['class'=>"alert alert-danger"], $e->getMessage()));
  372. }
  373. }
  374. public function rmLogFileAction() {
  375. session_write_close();
  376. try {
  377. $logName = $this->validateParamLogName(V::get('logName', '', $_REQUEST));
  378. $logPath = "/tmp/se-debug-{$logName}.log";
  379. if (!file_exists($logPath)) throw new AlertWarningException("Log file not exists");
  380. unlink($logPath);
  381. throw new AlertSuccessException("File Removed");
  382. } catch (AlertSuccessException $e) {
  383. $this->defaultView(UI::h('div', ['class'=>"alert alert-success"], $e->getMessage()));
  384. } catch (AlertWarningException $e) {
  385. $this->defaultView(UI::h('div', ['class'=>"alert alert-warning"], $e->getMessage()));
  386. } catch (Exception $e) {
  387. $this->defaultView(UI::h('div', ['class'=>"alert alert-danger"], $e->getMessage()));
  388. }
  389. }
  390. }