<!DOCTYPE html> <html ng-app="App"> <head> <title>System Logs</title> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0 user-scalable=no"> <link rel="stylesheet" href="../script/semantic/semantic.min.css"> <script type="text/javascript" src="../script/jquery-3.6.0.min.js"></script> <script type="text/javascript" src="../script/semantic/semantic.min.js"></script> <style> .clickable{ cursor: pointer; } .clickable:hover{ opacity: 0.7; } .logfile{ padding-left: 1em !important; position: relative; padding-right: 1em !important; } .loglist{ background-color: rgb(250, 250, 250); } .logfile .showing{ position: absolute; top: 0.18em; right: 0em; margin-right: -0.4em; opacity: 0; } .logfile.active .showing{ opacity: 1; } #logrender{ width: 100% !important; height: calc(100% - 1.2em); min-height: calc(90vh - 1.2em) !important; border: 0px solid transparent !important; background-color: #252630; color: white; font-family: monospace; overflow-x: scroll !important; white-space: pre; resize: none; scrollbar-width: thin; font-size: 1.2em; } #logrender::selection{ background:#3643bb; color:white; } body.darkTheme .loglist { background-color: #1b1c1d; color: #ffffff; } body.darkTheme .loglist .ui.header .content .sub.header { color: #bbbbbb; } body.darkTheme .loglist .ui.divider { border-color: #333333; } body.darkTheme .loglist .ui.accordion .title, body.darkTheme .loglist .ui.accordion .content { background-color: #1b1c1d; color: #ffffff; } body.darkTheme .loglist .ui.accordion .title:hover { background-color: #333333; } body.darkTheme .loglist .ui.list .item { color: #ffffff; } body.darkTheme .loglist .ui.list .item .content { color: #ffffff; } body.darkTheme .loglist .ui.list .item .showing { color: #ffffff; } body.darkTheme .loglist .ui.button.filterbtn { background-color: #333333; color: #ffffff; } body.darkTheme .loglist .ui.button.filterbtn:hover { background-color: #555555; } body.darkTheme .loglist .ui.toggle.checkbox label { color: #ffffff; } body.darkTheme .loglist small { color: #bbbbbb; } </style> </head> <body> <link rel="stylesheet" href="../darktheme.css"> <script src="../script/darktheme.js"></script> <br> <div class="ui container"> <div class="ui stackable grid"> <div class="four wide column loglist"> <h3 class="ui header" style="padding-top: 1em;"> <div class="content"> Log View <div class="sub header">Check System Log in Real Time</div> </div> </h3> <div class="ui divider"></div> <div id="logList" class="ui accordion"> </div> <div class="ui divider"></div> <h5>Filters</h5> <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> <button style="margin-top: 0.4em;" filter="request" class="ui fluid basic small button filterbtn"><i class="green exchange icon"></i> Request</button> <button style="margin-top: 0.4em;" filter="error" class="ui fluid basic small button filterbtn"><i class="red exclamation triangle icon"></i> Error</button> <button style="margin-top: 0.4em;" filter="all" class="ui fluid basic active small button filterbtn">All</button> <div class="ui divider"></div> <div class="ui toggle checkbox"> <input type="checkbox" id="enableAutoScroll" onchange="handleAutoScrollTicker(event, this.checked);"> <label>Auto Refresh<br> <small>Refresh the viewing log every 10 seconds</small></label> </div> <div class="ui divider"></div> <small>Notes: Some log files might be huge. Make sure you have checked the log file size before opening</small> </div> <div class="twelve wide column"> <textarea id="logrender" spellcheck="false" readonly="true"> ← Pick a log file from the left menu to start debugging </textarea> <a href="#" onclick="openLogInNewTab();">Open In New Tab</a> <br><br> </div> </div> </div> <br> </body> <script> var currentOpenedLogURL = ""; var currentFilter = "all"; var autoscroll = false; $(".checkbox").checkbox(); function openLogInNewTab(){ if (currentOpenedLogURL != ""){ window.open(currentOpenedLogURL); } } function openLog(object, catergory, filename){ $(".logfile.active").removeClass('active'); $(object).addClass("active"); currentOpenedLogURL = "/api/log/read?file=" + filename; $.get(currentOpenedLogURL, function(data){ if (data.error !== undefined){ alert(data.error); return; } renderLogWithCurrentFilter(data); }); } function initLogList(){ $("#logList").html(""); $.get("/api/log/list", function(data){ //console.log(data); for (let [key, value] of Object.entries(data)) { console.log(key, value); value.reverse(); //Default value was from oldest to newest var fileItemList = ""; value.forEach(file => { fileItemList += `<div class="item clickable logfile" onclick="openLog(this, '${key}','${file.Filename}');"> <i class="file outline icon"></i> <div class="content"> ${file.Title} (${formatBytes(file.Filesize)}) <div class="showing"><i class="green chevron right icon"></i></div> </div> </div>`; }) $("#logList").append(`<div class="title"> <i class="dropdown icon"></i> ${key} </div> <div class="content"> <div class="ui list"> ${fileItemList} </div> </div>`); } $(".ui.accordion").accordion(); }); } initLogList(); function formatBytes(x){ var units = ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; let l = 0, n = parseInt(x, 10) || 0; while(n >= 1024 && ++l){ n = n/1024; } return(n.toFixed(n < 10 && l > 0 ? 1 : 0) + ' ' + units[l]); } //Filter the log and render it to text area based on current filter choice function renderLogWithCurrentFilter(data){ if (currentFilter == "all"){ $("#logrender").val(data); }else{ let filterLines = data.split("\n"); let filteredLogFile = ""; for (var i = 0; i < filterLines.length; i++){ const thisLine = filterLines[i]; if (currentFilter == "system" && thisLine.indexOf("[system:") >= 0){ filteredLogFile += thisLine + "\n"; }else if (currentFilter == "request" && thisLine.indexOf("[router:") >= 0){ filteredLogFile += thisLine + "\n"; }else if (currentFilter == "error" && thisLine.indexOf(":error]") >= 0){ filteredLogFile += thisLine + "\n"; } } $("#logrender").val(filteredLogFile); } var textarea = document.getElementById('logrender'); textarea.scrollTop = textarea.scrollHeight; } /* Filter related functions */ $(".filterbtn").on("click", function(evt){ //Set filter type let filterType = $(this).attr("filter"); currentFilter = (filterType); $(".filterbtn.active").removeClass("active"); $(this).addClass('active'); //Reload the log with filter if (currentOpenedLogURL != ""){ $.get(currentOpenedLogURL, function(data){ if (data.error !== undefined){ alert(data.error); return; } renderLogWithCurrentFilter(data); }); } }); /* Auto scroll function */ setInterval(function(){ if (autoscroll){ //Update the log and scroll to bottom if (currentOpenedLogURL != ""){ $.get(currentOpenedLogURL, function(data){ if (data.error !== undefined){ console.log(data.error); return; } renderLogWithCurrentFilter(data); }); } } }, 10000); function handleAutoScrollTicker(event, checked){ autoscroll = checked; } </script> </html>