2023-11-04 13:27:25 +00:00
|
|
|
<!DOCTYPE html>
|
|
|
|
<html lang="en">
|
|
|
|
<head>
|
|
|
|
<meta charset="UTF-8" />
|
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
|
|
<title>Online log viewer</title>
|
|
|
|
<style>
|
|
|
|
@import url("https://rsms.me/inter/inter.css");
|
|
|
|
@import url("https://iosevka-webfonts.github.io/iosevka/iosevka.css");
|
|
|
|
:root {
|
|
|
|
--site-bg: #171717;
|
|
|
|
--white: #ededed;
|
|
|
|
background-color: var(--site-bg);
|
|
|
|
color: var(--white);
|
|
|
|
font-family: "Inter", "Gill Sans", "Gill Sans MT", "Segoe UI",
|
|
|
|
sans-serif;
|
|
|
|
}
|
|
|
|
html,
|
|
|
|
body {
|
|
|
|
margin: 0;
|
|
|
|
}
|
|
|
|
main {
|
|
|
|
display: flex;
|
|
|
|
}
|
|
|
|
#starting {
|
|
|
|
height: 100vh;
|
|
|
|
flex: 1;
|
|
|
|
display: flex;
|
|
|
|
flex-direction: column;
|
|
|
|
align-items: center;
|
|
|
|
justify-content: center;
|
|
|
|
}
|
|
|
|
code {
|
|
|
|
display: inline-block;
|
|
|
|
font-family: "Iosevka", monospace;
|
|
|
|
background-color: #222;
|
|
|
|
border-radius: 3px;
|
|
|
|
padding: 3px 6px;
|
|
|
|
}
|
|
|
|
.hidden {
|
|
|
|
display: none !important;
|
|
|
|
}
|
|
|
|
</style>
|
|
|
|
</head>
|
|
|
|
<body>
|
|
|
|
<main id="app">
|
|
|
|
<section id="starting">
|
|
|
|
<h1>Online log viewer</h1>
|
|
|
|
<p>
|
|
|
|
Drag a <code>strimertul.log</code> in this window to view it (or click
|
|
|
|
anywhere to open a file dialog).<br />
|
|
|
|
The contents will not be uploaded anywhere, this webpage is fully
|
|
|
|
local.
|
|
|
|
</p>
|
|
|
|
</section>
|
|
|
|
<table id="logs" class="hidden">
|
|
|
|
<thead>
|
|
|
|
<tr>
|
|
|
|
<th>Level</th>
|
|
|
|
<th>Time</th>
|
|
|
|
<th>Caller</th>
|
|
|
|
<th>Message</th>
|
|
|
|
<th>Data</th>
|
|
|
|
</tr>
|
|
|
|
</thead>
|
|
|
|
<tbody></tbody>
|
|
|
|
</table>
|
|
|
|
</main>
|
|
|
|
<script>
|
|
|
|
// @ts-check
|
|
|
|
|
|
|
|
const mainEl = document.getElementById("app");
|
|
|
|
const startingEl = document.getElementById("starting");
|
|
|
|
const tableEl = document.getElementById("logs");
|
|
|
|
|
2023-11-05 12:23:00 +00:00
|
|
|
if (!mainEl || !startingEl || !tableEl) {
|
|
|
|
throw new Error("Missing elements");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reads a log file and populates a table with the parsed logs.
|
|
|
|
*
|
|
|
|
* @param {File} file - The log file to be read.
|
|
|
|
* @return {void}
|
|
|
|
*/
|
|
|
|
function readLogFile(file) {
|
|
|
|
const reader = new FileReader();
|
|
|
|
reader.addEventListener("load", (ev) => {
|
|
|
|
const logs =
|
|
|
|
ev.target?.result
|
|
|
|
?.toString()
|
|
|
|
.split("\n")
|
|
|
|
.map((line) => line.trim())
|
|
|
|
.filter((line) => line.length > 0)
|
|
|
|
.map((line) => {
|
|
|
|
return makeLog(JSON.parse(line));
|
|
|
|
}) || [];
|
|
|
|
tableEl?.querySelector("tbody")?.replaceChildren(...logs);
|
|
|
|
tableEl?.classList.remove("hidden");
|
|
|
|
startingEl?.classList.add("hidden");
|
|
|
|
});
|
|
|
|
reader.readAsText(file);
|
|
|
|
}
|
|
|
|
|
2023-11-04 13:27:25 +00:00
|
|
|
// Click handler (open file select)
|
2023-11-05 12:23:00 +00:00
|
|
|
startingEl.addEventListener("click", (ev) => {
|
2023-11-04 13:27:25 +00:00
|
|
|
// Create temporary file input and click it
|
|
|
|
const fileSelect = document.createElement("input");
|
|
|
|
fileSelect.type = "file";
|
|
|
|
fileSelect.accept = ".log";
|
|
|
|
fileSelect.addEventListener("change", (ev) => {
|
|
|
|
if (ev.target instanceof HTMLInputElement && ev.target.files) {
|
|
|
|
const file = ev.target.files[0];
|
|
|
|
startingEl.innerHTML = "Loading...";
|
|
|
|
if (file) {
|
2023-11-05 12:23:00 +00:00
|
|
|
readLogFile(file);
|
2023-11-04 13:27:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
fileSelect.click();
|
|
|
|
});
|
|
|
|
|
2023-11-05 12:23:00 +00:00
|
|
|
// Drag and drop handler
|
|
|
|
mainEl.addEventListener("dragover", (ev) => {
|
|
|
|
ev.preventDefault();
|
|
|
|
});
|
|
|
|
mainEl.addEventListener("drop", (ev) => {
|
|
|
|
ev.preventDefault();
|
|
|
|
if (ev.dataTransfer?.files) {
|
|
|
|
const file = ev.dataTransfer.files[0];
|
|
|
|
startingEl.innerHTML = "Loading...";
|
|
|
|
if (file) {
|
|
|
|
readLogFile(file);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a log entry as a table row element.
|
|
|
|
*
|
|
|
|
* @param log - An object representing a log entry with properties level, ts, caller, msg, and data.
|
|
|
|
*
|
|
|
|
* @returns A table row element representing the log entry.
|
|
|
|
*/
|
2023-11-04 13:27:25 +00:00
|
|
|
function makeLog(log) {
|
|
|
|
const tr = document.createElement("tr");
|
|
|
|
const { level, ts, caller, msg, ...data } = log;
|
|
|
|
tr.appendChild(makeCell(level));
|
2023-11-05 12:23:00 +00:00
|
|
|
tr.appendChild(makeCell(makeDateElement(new Date(ts * 1000))));
|
2023-11-04 13:27:25 +00:00
|
|
|
tr.appendChild(makeCell(caller));
|
|
|
|
tr.appendChild(makeCell(msg));
|
|
|
|
tr.appendChild(makeCell(JSON.stringify(data)));
|
|
|
|
return tr;
|
|
|
|
}
|
|
|
|
|
2023-11-05 12:23:00 +00:00
|
|
|
/**
|
|
|
|
* Creates a new date element with the given date.
|
|
|
|
*
|
|
|
|
* @param {Date} date - The date to be used for the new date element.
|
|
|
|
* @returns {HTMLTimeElement} The newly created date element.
|
|
|
|
*/
|
|
|
|
function makeDateElement(date) {
|
|
|
|
const time = document.createElement("time");
|
|
|
|
time.setAttribute("datetime", date.toISOString());
|
|
|
|
time.appendChild(document.createTextNode(date.toLocaleTimeString()));
|
|
|
|
return time;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a table cell element with the given text as its content.
|
|
|
|
*
|
|
|
|
* @param {string | HTMLElement} el - A string or a DOM element to be used as the content of the cell.
|
|
|
|
* @returns {HTMLTableCellElement} - A table cell element with the given text as its content.
|
|
|
|
*/
|
|
|
|
function makeCell(el) {
|
2023-11-04 13:27:25 +00:00
|
|
|
const td = document.createElement("td");
|
2023-11-05 12:23:00 +00:00
|
|
|
if (typeof el === "string") {
|
|
|
|
td.appendChild(document.createTextNode(el));
|
|
|
|
} else {
|
|
|
|
td.appendChild(el);
|
|
|
|
}
|
2023-11-04 13:27:25 +00:00
|
|
|
return td;
|
|
|
|
}
|
|
|
|
</script>
|
|
|
|
</body>
|
|
|
|
</html>
|