Debug.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  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. 'type' => $dbg['type'],
  211. 'msg' => $dbg['msg'],
  212. // 'log' => (!empty($dbg['log'])) ? json_encode($dbg['log']) : '',
  213. 'log' => UI::h('div', [
  214. 'title' => htmlspecialchars( ('sql' == $dbg['type'] && is_string($dbg['log'])) ? $dbg['log'] : var_export($dbg['log'], true) ),
  215. 'onClick' => "return p5DBG__showLogTrace(this, event)",
  216. 'style' => "cursor:pointer"
  217. ], str_replace(array('\n', '\t'), ' ', substr(htmlspecialchars(json_encode($dbg['log'])), 0, 100)) . ' ...'
  218. ),
  219. 'trace' => UI::h('div', [
  220. 'title' => htmlspecialchars($dbg['trace']),
  221. 'onClick' => "return p5DBG__showLogTrace(this, event)",
  222. 'style' => "cursor:pointer"
  223. ], ('Exception' == $dbg['type'])
  224. ? "Code: {$dbg['log']['code']}, File: {$dbg['log']['file']}"
  225. : '...'
  226. ),
  227. ];
  228. },
  229. explode("\n", $content)
  230. ),
  231. ]);
  232. echo UI::h('script', [], "
  233. function p5DBG__showLogTrace(n, e) {
  234. if (!e) return false;
  235. if (e.target && 'PRE' == e.target.tagName) return false;
  236. if (n.lastChild.tagName == 'PRE') {
  237. if ('none' == n.lastChild.style.display) {
  238. n.lastChild.style.display = 'block'
  239. } else {
  240. n.lastChild.style.display = 'none'
  241. }
  242. } else {
  243. var pre = document.createElement('pre')
  244. pre.appendChild( document.createTextNode(n.title) )
  245. n.appendChild(pre)
  246. }
  247. }
  248. ");
  249. }
  250. public function activateDebugAction() {
  251. DBG::activate();
  252. $this->defaultView();
  253. }
  254. public function deactivateDebugAction() {
  255. DBG::deactivate();
  256. $this->defaultView();
  257. }
  258. public function testDebugWithSleepAction() {
  259. session_write_close();
  260. UI::gora();
  261. UI::setTitle("Debug Test Sleep");
  262. flush();
  263. for ($i = 0; $i < 10; $i++) {
  264. echo "TEST {$i}<br>";
  265. DBG::log("TEST {$i}");
  266. flush();
  267. sleep(2);
  268. }
  269. echo "DONE";
  270. DBG::log("DONE");
  271. UI::dol();
  272. }
  273. public function validateParamLogName($logName) {
  274. if (empty($logName)) throw new Exception("Missing log file name");
  275. if (!preg_match('/^[\-\.a-zA-Z0-9]+$/', $logName)) throw new Exception("Wrong log file name format");
  276. return $logName;
  277. }
  278. public function rmAllLogFilesAction() {
  279. session_write_close();
  280. try {
  281. $today = date("Y-m-d");
  282. $cmd = "rm -v /tmp/se-debug-*.log 2>&1";
  283. V::exec($cmd, $out, $ret);
  284. $this->defaultView(UI::h('div', ['class'=>"alert alert-success alert-dismissible"], [
  285. '<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>',
  286. (0 === $ret) ? "All Log Files Removed" : "Error?",
  287. " (return code: {$ret})",
  288. "<br>cmd: <code>{$cmd}</code>",
  289. UI::h('pre', [], implode("\n", $out))
  290. ]));
  291. } catch (AlertSuccessException $e) {
  292. $this->defaultView(UI::h('div', ['class'=>"alert alert-success"], $e->getMessage()));
  293. } catch (AlertWarningException $e) {
  294. $this->defaultView(UI::h('div', ['class'=>"alert alert-warning"], $e->getMessage()));
  295. } catch (Exception $e) {
  296. $this->defaultView(UI::h('div', ['class'=>"alert alert-danger"], $e->getMessage()));
  297. }
  298. }
  299. public function rmOldLogFilesAction() {
  300. session_write_close();
  301. try {
  302. $today = date("Y-m-d");
  303. $cmd = "ls -1 /tmp/se-debug-*.log | grep -v '/tmp/se-debug-{$today}-' | xargs rm -v 2>&1";
  304. V::exec($cmd, $out, $ret);
  305. $this->defaultView(UI::h('div', ['class'=>"alert alert-success alert-dismissible"], [
  306. '<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>',
  307. (0 === $ret) ? "Old Log Files Removed" : "Error?",
  308. " (return code: {$ret})",
  309. "<br>cmd: <code>{$cmd}</code>",
  310. UI::h('pre', [], implode("\n", $out))
  311. ]));
  312. } catch (AlertSuccessException $e) {
  313. $this->defaultView(UI::h('div', ['class'=>"alert alert-success"], $e->getMessage()));
  314. } catch (AlertWarningException $e) {
  315. $this->defaultView(UI::h('div', ['class'=>"alert alert-warning"], $e->getMessage()));
  316. } catch (Exception $e) {
  317. $this->defaultView(UI::h('div', ['class'=>"alert alert-danger"], $e->getMessage()));
  318. }
  319. }
  320. public function rmAllUserLogFilesAction() {
  321. session_write_close();
  322. try {
  323. $userLogin = User::getLogin();
  324. $today = date("Y-m-d");
  325. $cmd = "rm -v /tmp/se-debug-*-{$userLogin}-*.log 2>&1";
  326. V::exec($cmd, $out, $ret);
  327. $this->defaultView(UI::h('div', ['class'=>"alert alert-success alert-dismissible"], [
  328. '<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>',
  329. (0 === $ret) ? "All Log Files Removed" : "Error?",
  330. " (return code: {$ret})",
  331. "<br>cmd: <code>{$cmd}</code>",
  332. UI::h('pre', [], implode("\n", $out))
  333. ]));
  334. } catch (AlertSuccessException $e) {
  335. $this->defaultView(UI::h('div', ['class'=>"alert alert-success"], $e->getMessage()));
  336. } catch (AlertWarningException $e) {
  337. $this->defaultView(UI::h('div', ['class'=>"alert alert-warning"], $e->getMessage()));
  338. } catch (Exception $e) {
  339. $this->defaultView(UI::h('div', ['class'=>"alert alert-danger"], $e->getMessage()));
  340. }
  341. }
  342. public function rmOldUserLogFilesAction() {
  343. session_write_close();
  344. try {
  345. $userLogin = User::getLogin();
  346. $today = date("Y-m-d");
  347. $cmd = "ls -1 /tmp/se-debug-*-{$userLogin}-*.log | grep -v '/tmp/se-debug-{$today}-' | xargs rm -v 2>&1";
  348. V::exec($cmd, $out, $ret);
  349. $this->defaultView(UI::h('div', ['class'=>"alert alert-success alert-dismissible"], [
  350. '<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>',
  351. (0 === $ret) ? "Old Log Files Removed" : "Error?",
  352. " (return code: {$ret})",
  353. "<br>cmd: <code>{$cmd}</code>",
  354. UI::h('pre', [], implode("\n", $out))
  355. ]));
  356. } catch (AlertSuccessException $e) {
  357. $this->defaultView(UI::h('div', ['class'=>"alert alert-success"], $e->getMessage()));
  358. } catch (AlertWarningException $e) {
  359. $this->defaultView(UI::h('div', ['class'=>"alert alert-warning"], $e->getMessage()));
  360. } catch (Exception $e) {
  361. $this->defaultView(UI::h('div', ['class'=>"alert alert-danger"], $e->getMessage()));
  362. }
  363. }
  364. public function rmLogFileAction() {
  365. session_write_close();
  366. try {
  367. $logName = $this->validateParamLogName(V::get('logName', '', $_REQUEST));
  368. $logPath = "/tmp/se-debug-{$logName}.log";
  369. if (!file_exists($logPath)) throw new AlertWarningException("Log file not exists");
  370. unlink($logPath);
  371. throw new AlertSuccessException("File Removed");
  372. } catch (AlertSuccessException $e) {
  373. $this->defaultView(UI::h('div', ['class'=>"alert alert-success"], $e->getMessage()));
  374. } catch (AlertWarningException $e) {
  375. $this->defaultView(UI::h('div', ['class'=>"alert alert-warning"], $e->getMessage()));
  376. } catch (Exception $e) {
  377. $this->defaultView(UI::h('div', ['class'=>"alert alert-danger"], $e->getMessage()));
  378. }
  379. }
  380. }