Skip to content

Commit

Permalink
fix: use timer to filter out logs unrelated to errors (#1585)
Browse files Browse the repository at this point in the history
* fix: use timer to filter out logs unrelated to errors

* show URLs in dev admin menu

* log proxy output with ConsoleIO

* fix: optimize app logs viewer

* add link to dev dashboard in log viewer

* use setTimeout to scroll properly

* no need to slice

* update log viewer title
  • Loading branch information
jchip authored Apr 6, 2020
1 parent 4377121 commit 5ab2e90
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 52 deletions.
76 changes: 55 additions & 21 deletions packages/xarc-app-dev/lib/dev-admin/admin-server.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,16 +75,24 @@ class AdminServer {
}

showMenu() {
const reporterUrl = formUrl({ ...fullDevServer, path: controlPaths.reporter });
const logUrl = formUrl({ ...fullDevServer, path: controlPaths.appLog });
const devurl = formUrl({ ...fullDevServer, path: controlPaths.dev });
const proxyItem = DEV_PROXY_ENABLED ? "<magenta>P</> - Restart Dev Proxy " : "";
const menu = ck` <green.inverse> Electrode Dev Admin Console </>
<white.inverse>For your app server</>
<magenta>A</> - Restart <magenta>D</> - <cyan>inspect-brk</> mode <magenta>I</> - <cyan>inspect</> mode <magenta>K</> - Kill&nbsp;
<white.inverse>For Electrode webpack dev server</> ${this._wds}
<magenta>W</> - Restart <magenta>E</> - <cyan>inspect-brk</> mode <magenta>R</> - <cyan>inspect</> mode <magenta>X</> - Kill&nbsp;
<magenta>L</> - Show All Logs <magenta>0-6</> - Show Logs by Winston level
${proxyItem}<magenta>M</> - Show this menu <magenta>Q</> - Shutdown`;
this._io.show("\n" + boxen(menu, { margin: { left: 5 } }));
const menu = ck` <green.inverse> Electrode Dev Admin Console </>
<white.inverse>For your app server</> ${this._app}
<magenta>A</> - Restart <magenta>D</> - <cyan>inspect-brk</> mode <magenta>I</> - <cyan>inspect</> mode <magenta>K</> - Kill&nbsp;
<white.inverse>For Electrode webpack dev server</> ${this._wds}
<magenta>W</> - Restart <magenta>E</> - <cyan>inspect-brk</> mode <magenta>R</> - <cyan>inspect</> mode <magenta>X</> - Kill&nbsp;
<magenta>L</> - Show All Logs <magenta>0-6</> - Show Logs by Winston level
${proxyItem}<magenta>M</> - Show this menu <magenta>Q</> - Shutdown
<green> App URL: <cyan.underline>${formUrl(fullDevServer)}</></>
<green> App Log URL: <cyan.underline>${logUrl}</></>
<green> DEV dashboard: <cyan.underline>${devurl}</></>
<green>WebPack reporter: <cyan.underline>${reporterUrl}</></>`;
this._io.show("\n" + boxen(menu, { margin: { left: 5 }, padding: { right: 3, left: 3 } }));
}

getServer(name) {
Expand Down Expand Up @@ -364,30 +372,49 @@ class AdminServer {
if (context._deferTimer) {
clearTimeout(context._deferTimer);
} else {
context._deferIx = store.length - 1;
context._deferIx = [];
}

if (_.isEmpty(context._deferIx) || store.length - _.last(context._deferIx) > 1) {
context._deferIx.push(store.length - 1);
}

if (!context._showFullLink) {
context._showFullLink = showFullLink;
}

context._deferTimestamp = Date.now();
context._deferTimer = setTimeout(() => {
context._deferTimer = undefined;
for (let ix = context._deferIx; ix < store.length; ix++) {
if (typeof store[ix] === "string") {
this._io.write(tag + store[ix] + "\n");
} else if (store[ix]) {
const json = store[ix];
this._io.write(tag + (json.msg || json.message || JSON.stringify(json)) + "\n");
let currentIx = 0;
context._deferIx.forEach(deferIx => {
if (currentIx > deferIx) {
return;
}
}
let ix = deferIx;
for (; ix < store.length; ix++) {
if (store[ix] === false) {
break;
}

if (typeof store[ix] === "string") {
this._io.write(tag + store[ix] + "\n");
} else if (store[ix]) {
const json = store[ix];
this._io.write(tag + (json.msg || json.message || JSON.stringify(json)) + "\n");
}
}
currentIx = ix + 1;
});
context._deferIx = [];
if (context._showFullLink === true) {
context._toggle = true;
this.showFullLogUrlMessage(tag, context.fullLogUrl);
}
context._showFullLink = undefined;
if (store.length > 19999) {
context.store = store.slice(store.length - 9999);
if (store.length > 25000) {
const cleanup = store.filter(x => x !== false);
context.store = cleanup.slice(cleanup.length - 9999);
}
}, delay);
}
Expand All @@ -396,6 +423,13 @@ class AdminServer {
const { inputs, store } = context;

const handler = data => {
const timeDiff = Date.now() - context._deferTimestamp;
// if an error line has been detected, then only consider other lines following it
// within 30 milliseconds as part of it.
if (context._deferTimer && timeDiff > 30) {
store.push(false);
}

let str = data.toString();
if (!str.trim()) {
store.push("");
Expand Down Expand Up @@ -458,8 +492,8 @@ class AdminServer {
debug,
exec: Path.join(__dirname, "redbird-spawn"),
waitStart: async info => {
this.passThruLineOutput(this._proxy, info._child.stdout, process.stdout);
this.passThruLineOutput(this._proxy, info._child.stderr, process.stderr);
this.passThruLineOutput(this._proxy, info._child.stdout, this._io);
this.passThruLineOutput(this._proxy, info._child.stderr, this._io);
}
});
}
Expand Down
92 changes: 61 additions & 31 deletions packages/xarc-app-dev/lib/dev-admin/log.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,53 @@
<html>
<head>
<title>LogViewer: Electrode X application development logs</title>
<style>
/* styles from https://www.w3schools.com/howto/howto_css_fixed_menu.asp */
/* The navigation bar */
.navbar {
overflow: hidden;
background-color: #ffffff;
position: fixed; /* Set the navbar to fixed position */
top: 0; /* Position the navbar at the top of the page */
width: 100%; /* Full width */
}

/* Links inside the navbar */
.navbar a {
/* color: #f2f2f2; */
text-align: center;
padding-left: 10px;
padding-right: 10px;
}

/* Change background on mouse-over */
.navbar a:hover {
background: #1e9ae2;
color: black;
}

/* Main content */
.main {
margin-top: 30px; /* Add a top margin to avoid content overlay */
border-radius: 10px;
background: black;
color: gray;
padding: 10px;
}

.logs {
overflow-x: auto;
white-space: pre-wrap;
white-space: -moz-pre-wrap;
white-space: -pre-wrap;
white-space: -o-pre-wrap;
word-wrap: break-word;
}
</style>
</head>
<body>
<div style="margin-bottom: 10px">
<button type="button" onclick="displayLogs();">Refresh</button>
<div id="controls" class="navbar">
<button type="button" onclick="displayLogs();">Refresh Logs</button>
<label>
<input type="checkbox" id="level.error" checked onclick="levelChangeHandler();" />
Error
Expand Down Expand Up @@ -30,56 +76,40 @@
<input type="checkbox" id="level.silly" checked onclick="levelChangeHandler();" />
Silly
</label>
<a href="/__electrode_dev" class="button">Dev Dashboard</a>
</div>
<div style="border-radius: 10px; background: black; color: gray; padding: 10px;">
<pre style="white-space: pre-wrap;" id="logs"></pre>
<div class="main">
<pre class="logs" id="logs"></pre>
</div>
<script>
let logs = [];
const el = document.getElementById("logs");
let lastLevelSelections;

function getLevelSelections() {
const levels = ["error", "warn", "info", "http", "verbose", "debug", "silly"];
const levelSelections = {};
levels.forEach((level) => {
levels.forEach(level => {
const c = document.getElementById("level." + level);
levelSelections[level] = c.checked;
});
return levelSelections;
}

function resetLogView() {
logs = [];
el.innerHTML = "";
}

function appendLogView(message) {
el.innerHTML += message + "\n";
}

function levelChangeHandler() {
resetLogView();
displayLogs(getLevelSelections());
displayLogs(getLevelSelections(), false);
}

async function displayLogs(levelSelections) {
async function displayLogs(levelSelections, scrollToEnd = true) {
levelSelections = levelSelections || getLevelSelections();
const logResponse = await fetch("/__electrode_dev/log-events");
let newLogs = await logResponse.json();
if (lastLevelSelections || !newLogs[0] || (logs[0] && newLogs[0].message !== logs[0].message)) {
resetLogView();
const filteredLogs = newLogs.filter(event => levelSelections[event.level]);
el.innerHTML = filteredLogs.map(event => event.message).join("\n") + "\n\n";
// scroll to bottom
if (scrollToEnd) {
setTimeout(() => window.scrollTo(0, document.body.scrollHeight), 350);
}
lastLevelSelections = levelSelections;
newLogs = newLogs.slice(logs.length);
newLogs.forEach((event) => {
if (levelSelections && !levelSelections[event.level]) {
return;
}
appendLogView(event.message);
});
logs = logs.concat(newLogs);
}
displayLogs();
setTimeout(displayLogs(), 10);
</script>
</body>
</html>

0 comments on commit 5ab2e90

Please sign in to comment.