logview.html 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. <!DOCTYPE html>
  2. <html ng-app="App">
  3. <head>
  4. <title>System Logs</title>
  5. <meta charset="UTF-8">
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0 user-scalable=no">
  7. <link rel="stylesheet" href="../script/semantic/semantic.min.css">
  8. <script type="text/javascript" src="../script/jquery-3.6.0.min.js"></script>
  9. <script type="text/javascript" src="../script/semantic/semantic.min.js"></script>
  10. <style>
  11. .clickable{
  12. cursor: pointer;
  13. }
  14. .clickable:hover{
  15. opacity: 0.7;
  16. }
  17. .logfile{
  18. padding-left: 1em !important;
  19. position: relative;
  20. padding-right: 1em !important;
  21. }
  22. .loglist{
  23. background-color: rgb(250, 250, 250);
  24. }
  25. .logfile .showing{
  26. position: absolute;
  27. top: 0.18em;
  28. right: 0em;
  29. margin-right: -0.4em;
  30. opacity: 0;
  31. }
  32. .logfile.active .showing{
  33. opacity: 1;
  34. }
  35. #logrender{
  36. width: 100% !important;
  37. height: calc(100% - 1.2em);
  38. min-height: calc(90vh - 1.2em) !important;
  39. border: 0px solid transparent !important;
  40. background-color: #252630;
  41. color: white;
  42. font-family: monospace;
  43. overflow-x: scroll !important;
  44. white-space: pre;
  45. resize: none;
  46. scrollbar-width: thin;
  47. font-size: 1.2em;
  48. }
  49. #logrender::selection{
  50. background:#3643bb;
  51. color:white;
  52. }
  53. body.darkTheme .loglist {
  54. background-color: #1b1c1d;
  55. color: #ffffff;
  56. }
  57. body.darkTheme .loglist .ui.header .content .sub.header {
  58. color: #bbbbbb;
  59. }
  60. body.darkTheme .loglist .ui.divider {
  61. border-color: #333333;
  62. }
  63. body.darkTheme .loglist .ui.accordion .title,
  64. body.darkTheme .loglist .ui.accordion .content {
  65. background-color: #1b1c1d;
  66. color: #ffffff;
  67. }
  68. body.darkTheme .loglist .ui.accordion .title:hover {
  69. background-color: #333333;
  70. }
  71. body.darkTheme .loglist .ui.list .item {
  72. color: #ffffff;
  73. }
  74. body.darkTheme .loglist .ui.list .item .content {
  75. color: #ffffff;
  76. }
  77. body.darkTheme .loglist .ui.list .item .showing {
  78. color: #ffffff;
  79. }
  80. body.darkTheme .loglist .ui.button.filterbtn {
  81. background-color: #333333;
  82. color: #ffffff;
  83. }
  84. body.darkTheme .loglist .ui.button.filterbtn:hover {
  85. background-color: #555555;
  86. }
  87. body.darkTheme .loglist .ui.toggle.checkbox label {
  88. color: #ffffff;
  89. }
  90. body.darkTheme .loglist small {
  91. color: #bbbbbb;
  92. }
  93. </style>
  94. </head>
  95. <body>
  96. <link rel="stylesheet" href="../darktheme.css">
  97. <script src="../script/darktheme.js"></script>
  98. <br>
  99. <div class="ui container">
  100. <div class="ui stackable grid">
  101. <div class="four wide column loglist">
  102. <h3 class="ui header" style="padding-top: 1em;">
  103. <div class="content">
  104. Log View
  105. <div class="sub header">Check System Log in Real Time</div>
  106. </div>
  107. </h3>
  108. <div class="ui divider"></div>
  109. <div id="logList" class="ui accordion">
  110. </div>
  111. <div class="ui divider"></div>
  112. <h5>Filters</h5>
  113. <button style="margin-top: 0.4em;" filter="system" class="ui fluid basic small button filterbtn"><i class="ui blue info circle icon"></i> System</button>
  114. <button style="margin-top: 0.4em;" filter="request" class="ui fluid basic small button filterbtn"><i class="green exchange icon"></i> Request</button>
  115. <button style="margin-top: 0.4em;" filter="error" class="ui fluid basic small button filterbtn"><i class="red exclamation triangle icon"></i> Error</button>
  116. <button style="margin-top: 0.4em;" filter="all" class="ui fluid basic active small button filterbtn">All</button>
  117. <div class="ui divider"></div>
  118. <div class="ui toggle checkbox">
  119. <input type="checkbox" id="enableAutoScroll" onchange="handleAutoScrollTicker(event, this.checked);">
  120. <label>Auto Refresh<br>
  121. <small>Refresh the viewing log every 10 seconds</small></label>
  122. </div>
  123. <div class="ui divider"></div>
  124. <small>Notes: Some log files might be huge. Make sure you have checked the log file size before opening</small>
  125. </div>
  126. <div class="twelve wide column">
  127. <textarea id="logrender" spellcheck="false" readonly="true">
  128. ← Pick a log file from the left menu to start debugging
  129. </textarea>
  130. <a href="#" onclick="openLogInNewTab();">Open In New Tab</a>
  131. <br><br>
  132. </div>
  133. </div>
  134. </div>
  135. <br>
  136. </body>
  137. <script>
  138. var currentOpenedLogURL = "";
  139. var currentFilter = "all";
  140. var autoscroll = false;
  141. $(".checkbox").checkbox();
  142. function openLogInNewTab(){
  143. if (currentOpenedLogURL != ""){
  144. window.open(currentOpenedLogURL);
  145. }
  146. }
  147. function openLog(object, catergory, filename){
  148. $(".logfile.active").removeClass('active');
  149. $(object).addClass("active");
  150. currentOpenedLogURL = "/api/log/read?file=" + filename;
  151. $.get(currentOpenedLogURL, function(data){
  152. if (data.error !== undefined){
  153. alert(data.error);
  154. return;
  155. }
  156. renderLogWithCurrentFilter(data);
  157. });
  158. }
  159. function initLogList(){
  160. $("#logList").html("");
  161. $.get("/api/log/list", function(data){
  162. //console.log(data);
  163. for (let [key, value] of Object.entries(data)) {
  164. console.log(key, value);
  165. value.reverse(); //Default value was from oldest to newest
  166. var fileItemList = "";
  167. value.forEach(file => {
  168. fileItemList += `<div class="item clickable logfile" onclick="openLog(this, '${key}','${file.Filename}');">
  169. <i class="file outline icon"></i>
  170. <div class="content">
  171. ${file.Title} (${formatBytes(file.Filesize)})
  172. <div class="showing"><i class="green chevron right icon"></i></div>
  173. </div>
  174. </div>`;
  175. })
  176. $("#logList").append(`<div class="title">
  177. <i class="dropdown icon"></i>
  178. ${key}
  179. </div>
  180. <div class="content">
  181. <div class="ui list">
  182. ${fileItemList}
  183. </div>
  184. </div>`);
  185. }
  186. $(".ui.accordion").accordion();
  187. });
  188. }
  189. initLogList();
  190. function formatBytes(x){
  191. var units = ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
  192. let l = 0, n = parseInt(x, 10) || 0;
  193. while(n >= 1024 && ++l){
  194. n = n/1024;
  195. }
  196. return(n.toFixed(n < 10 && l > 0 ? 1 : 0) + ' ' + units[l]);
  197. }
  198. //Filter the log and render it to text area based on current filter choice
  199. function renderLogWithCurrentFilter(data){
  200. if (currentFilter == "all"){
  201. $("#logrender").val(data);
  202. }else{
  203. let filterLines = data.split("\n");
  204. let filteredLogFile = "";
  205. for (var i = 0; i < filterLines.length; i++){
  206. const thisLine = filterLines[i];
  207. if (currentFilter == "system" && thisLine.indexOf("[system:") >= 0){
  208. filteredLogFile += thisLine + "\n";
  209. }else if (currentFilter == "request" && thisLine.indexOf("[router:") >= 0){
  210. filteredLogFile += thisLine + "\n";
  211. }else if (currentFilter == "error" && thisLine.indexOf(":error]") >= 0){
  212. filteredLogFile += thisLine + "\n";
  213. }
  214. }
  215. $("#logrender").val(filteredLogFile);
  216. }
  217. var textarea = document.getElementById('logrender');
  218. textarea.scrollTop = textarea.scrollHeight;
  219. }
  220. /* Filter related functions */
  221. $(".filterbtn").on("click", function(evt){
  222. //Set filter type
  223. let filterType = $(this).attr("filter");
  224. currentFilter = (filterType);
  225. $(".filterbtn.active").removeClass("active");
  226. $(this).addClass('active');
  227. //Reload the log with filter
  228. if (currentOpenedLogURL != ""){
  229. $.get(currentOpenedLogURL, function(data){
  230. if (data.error !== undefined){
  231. alert(data.error);
  232. return;
  233. }
  234. renderLogWithCurrentFilter(data);
  235. });
  236. }
  237. });
  238. /* Auto scroll function */
  239. setInterval(function(){
  240. if (autoscroll){
  241. //Update the log and scroll to bottom
  242. if (currentOpenedLogURL != ""){
  243. $.get(currentOpenedLogURL, function(data){
  244. if (data.error !== undefined){
  245. console.log(data.error);
  246. return;
  247. }
  248. renderLogWithCurrentFilter(data);
  249. });
  250. }
  251. }
  252. }, 10000);
  253. function handleAutoScrollTicker(event, checked){
  254. autoscroll = checked;
  255. }
  256. </script>
  257. </html>