110 lines
2.7 KiB
JavaScript
110 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;
|