logview.html 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  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. </style>
  54. </head>
  55. <body>
  56. <br>
  57. <div class="ui container">
  58. <div class="ui stackable grid">
  59. <div class="four wide column loglist">
  60. <h3 class="ui header" style="padding-top: 1em;">
  61. <div class="content">
  62. Log View
  63. <div class="sub header">Check System Log in Real Time</div>
  64. </div>
  65. </h3>
  66. <div class="ui divider"></div>
  67. <div id="logList" class="ui accordion">
  68. </div>
  69. <div class="ui divider"></div>
  70. <h5>Filters</h5>
  71. <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>
  72. <button style="margin-top: 0.4em;" filter="request" class="ui fluid basic small button filterbtn"><i class="green exchange icon"></i> Request</button>
  73. <button style="margin-top: 0.4em;" filter="error" class="ui fluid basic small button filterbtn"><i class="red exclamation triangle icon"></i> Error</button>
  74. <button style="margin-top: 0.4em;" filter="all" class="ui fluid basic active small button filterbtn">All</button>
  75. <div class="ui divider"></div>
  76. <div class="ui toggle checkbox">
  77. <input type="checkbox" id="enableAutoScroll" onchange="handleAutoScrollTicker(event, this.checked);">
  78. <label>Auto Refresh<br>
  79. <small>Refresh the viewing log every 10 seconds</small></label>
  80. </div>
  81. <div class="ui divider"></div>
  82. <small>Notes: Some log files might be huge. Make sure you have checked the log file size before opening</small>
  83. </div>
  84. <div class="twelve wide column">
  85. <textarea id="logrender" spellcheck="false" readonly="true">
  86. ← Pick a log file from the left menu to start debugging
  87. </textarea>
  88. <a href="#" onclick="openLogInNewTab();">Open In New Tab</a>
  89. <br><br>
  90. </div>
  91. </div>
  92. </div>
  93. <br>
  94. </body>
  95. <script>
  96. var currentOpenedLogURL = "";
  97. var currentFilter = "all";
  98. var autoscroll = false;
  99. $(".checkbox").checkbox();
  100. function openLogInNewTab(){
  101. if (currentOpenedLogURL != ""){
  102. window.open(currentOpenedLogURL);
  103. }
  104. }
  105. function openLog(object, catergory, filename){
  106. $(".logfile.active").removeClass('active');
  107. $(object).addClass("active");
  108. currentOpenedLogURL = "/api/log/read?file=" + filename;
  109. $.get(currentOpenedLogURL, function(data){
  110. if (data.error !== undefined){
  111. alert(data.error);
  112. return;
  113. }
  114. renderLogWithCurrentFilter(data);
  115. });
  116. }
  117. function initLogList(){
  118. $("#logList").html("");
  119. $.get("/api/log/list", function(data){
  120. //console.log(data);
  121. for (let [key, value] of Object.entries(data)) {
  122. console.log(key, value);
  123. value.reverse(); //Default value was from oldest to newest
  124. var fileItemList = "";
  125. value.forEach(file => {
  126. fileItemList += `<div class="item clickable logfile" onclick="openLog(this, '${key}','${file.Filename}');">
  127. <i class="file outline icon"></i>
  128. <div class="content">
  129. ${file.Title} (${formatBytes(file.Filesize)})
  130. <div class="showing"><i class="green chevron right icon"></i></div>
  131. </div>
  132. </div>`;
  133. })
  134. $("#logList").append(`<div class="title">
  135. <i class="dropdown icon"></i>
  136. ${key}
  137. </div>
  138. <div class="content">
  139. <div class="ui list">
  140. ${fileItemList}
  141. </div>
  142. </div>`);
  143. }
  144. $(".ui.accordion").accordion();
  145. });
  146. }
  147. initLogList();
  148. function formatBytes(x){
  149. var units = ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
  150. let l = 0, n = parseInt(x, 10) || 0;
  151. while(n >= 1024 && ++l){
  152. n = n/1024;
  153. }
  154. return(n.toFixed(n < 10 && l > 0 ? 1 : 0) + ' ' + units[l]);
  155. }
  156. //Filter the log and render it to text area based on current filter choice
  157. function renderLogWithCurrentFilter(data){
  158. if (currentFilter == "all"){
  159. $("#logrender").val(data);
  160. }else{
  161. let filterLines = data.split("\n");
  162. let filteredLogFile = "";
  163. for (var i = 0; i < filterLines.length; i++){
  164. const thisLine = filterLines[i];
  165. if (currentFilter == "system" && thisLine.indexOf("[system:") >= 0){
  166. filteredLogFile += thisLine + "\n";
  167. }else if (currentFilter == "request" && thisLine.indexOf("[router:") >= 0){
  168. filteredLogFile += thisLine + "\n";
  169. }else if (currentFilter == "error" && thisLine.indexOf(":error]") >= 0){
  170. filteredLogFile += thisLine + "\n";
  171. }
  172. }
  173. $("#logrender").val(filteredLogFile);
  174. }
  175. var textarea = document.getElementById('logrender');
  176. textarea.scrollTop = textarea.scrollHeight;
  177. }
  178. /* Filter related functions */
  179. $(".filterbtn").on("click", function(evt){
  180. //Set filter type
  181. let filterType = $(this).attr("filter");
  182. currentFilter = (filterType);
  183. $(".filterbtn.active").removeClass("active");
  184. $(this).addClass('active');
  185. //Reload the log with filter
  186. if (currentOpenedLogURL != ""){
  187. $.get(currentOpenedLogURL, function(data){
  188. if (data.error !== undefined){
  189. alert(data.error);
  190. return;
  191. }
  192. renderLogWithCurrentFilter(data);
  193. });
  194. }
  195. });
  196. /* Auto scroll function */
  197. setInterval(function(){
  198. if (autoscroll){
  199. //Update the log and scroll to bottom
  200. if (currentOpenedLogURL != ""){
  201. $.get(currentOpenedLogURL, function(data){
  202. if (data.error !== undefined){
  203. console.log(data.error);
  204. return;
  205. }
  206. renderLogWithCurrentFilter(data);
  207. });
  208. }
  209. }
  210. }, 10000);
  211. function handleAutoScrollTicker(event, checked){
  212. autoscroll = checked;
  213. }
  214. </script>
  215. </html>