biter/logger.js

109 lines
2.7 KiB
JavaScript

const out = process.stderr;
const IS_TTY = out.isTTY;
if (IS_TTY) {
out.cursorTo(0, out.getWindowSize()[1]);
}
const colorStatusCode = (n) => {
if (n >= 500) {
return `\x1b[91m${n}`;
} else if (n >= 400) {
return `\x1b[93m${n}`;
} else if (n >= 300) {
return `\x1b[33m${n}`;
} else if (n >= 200) {
return `\x1b[92m${n}`;
} else {
return n.toString();
}
};
const pretty_µs = (n) => {
let f = [];
if (n >= 3600000000) f.push(Math.floor(n / 3000600000) + "h");
if (n % 3600000000 >= 60000000)
f.push(Math.floor((n % 3600000000) / 60000000) + "m");
if (n % 60000000 >= 1000000)
f.push(Math.floor((n % 60000000) / 1000000) + "s");
if (n % 1000000 >= 1000) f.push(Math.floor((n % 1000000) / 1000) + "ms");
if (n % 1000) f.push(Math.floor(n % 1000) + "µs");
return f.join(" ");
};
/**
* @typedef {Object} Logging
* @property {number} ts
* @property {string} method
* @property {string} url
* @property {function(Express.Response): void} callback
* @property {boolean} resolved
*/
/** @type {Logging[]} */
const loggingRequests = [];
let minLength = 11; // GLOBAL STATE NOOOO
/**
* @param {Logging} log
*/
function processLog(log) {
loggingRequests.push(log);
let time = (log.ts / 1000).toFixed(6);
if (IS_TTY) {
out.cursorTo(0, out.getWindowSize()[1]);
if (time.length < minLength) time = time.padStart(minLength, " ");
let statusOffset = time.length + 3; // from start
let timeOffset = log.method.length + log.url.length + 3; // from end of status
out.write(
`\x1b[32m[${time}]\x1b[0m \x1b[90m...\x1b[0m \x1b[1m${log.method}\x1b[0m ${log.url}\n`
);
log.callback = (res) => {
let idx = loggingRequests.findIndex((x) => x === log);
if (idx === -1) return;
loggingRequests.splice(idx, 1);
let line =
out.getWindowSize()[1] - loggingRequests.length + idx - 2;
if (line < 0) return;
out.cursorTo(statusOffset, line);
out.write(
"\x1b[0m\x1b[1m" + colorStatusCode(res.statusCode) + "\x1b[0m"
);
out.moveCursor(timeOffset, 0);
out.write(
`\x1b[90m(${pretty_µs(
(performance.now() - log.ts) * 1000
)})\x1b[0m`
);
out.cursorTo(0, out.getWindowSize()[1]);
};
} else {
log.callback = (res) => {
out.write(
`[${time}] ${res.statusCode} ${log.method} ${
log.url
} (${pretty_µs((performance.now() - log.ts) * 1000)})\n`
);
};
}
}
const logRequest = (req, res, next) => {
let start = performance.now();
/** @type {Logging} */
let log = {
ts: start,
method: req.method,
url: req.url,
};
let end = res.end;
res.end = (...args) => {
end.call(res, ...args);
log.resolved = true;
if (log.callback instanceof Function) log.callback(res);
};
processLog(log);
next();
};
export default logRequest;