135 lines
3.5 KiB
JavaScript
135 lines
3.5 KiB
JavaScript
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 private type to adosjhgadklrf
|
|
* @property {number} ts
|
|
* @property {include('express').Request} req
|
|
* @property {function(include('express').Response): void} callback
|
|
* @property {boolean} resolved
|
|
*/
|
|
|
|
/**
|
|
* @typedef {Object?} Options
|
|
* @property {boolean} [tty=true]
|
|
* @property {boolean} [logIp=true]
|
|
* @property {NodeJS.WriteStream} [output=process.stderr]
|
|
*/
|
|
|
|
/**
|
|
* @param {Options} options
|
|
* @returns {function(include('express').Request, include('express').Response, include('express').NextFunction): void}
|
|
*/
|
|
const logRequest = (options) => {
|
|
options = options ?? {};
|
|
options.tty = options.tty ?? true;
|
|
options.logIp = options.logIp ?? true;
|
|
options.output = options.output ?? process.stderr;
|
|
|
|
const out = options.output ?? process.stderr;
|
|
const IS_TTY = options.tty && out.isTTY;
|
|
if (IS_TTY) {
|
|
out.cursorTo(0, out.getWindowSize()[1]);
|
|
}
|
|
|
|
/** @type {Logging[]} */
|
|
const loggingRequests = [];
|
|
let minLength = 12; // NOOO MAGIC NUMBERINO
|
|
|
|
/**
|
|
* @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 + (options.logIp ? log.req.ip.length + 3 : 0); // from start
|
|
let timeOffset = log.req.method.length + log.req.url.length + 3; // from end of status
|
|
out.write(
|
|
`\x1b[32m[${time}]\x1b[0m ${
|
|
options.logIp ? "(" + log.req.ip + ") " : ""
|
|
}\x1b[90m...\x1b[0m \x1b[1m${log.req.method}\x1b[0m ${
|
|
log.req.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}] ${options.logIp ? "(" + log.req.ip + ") " : ""}${
|
|
res.statusCode
|
|
} ${log.req.method} ${log.req.url} (${pretty_µs(
|
|
(performance.now() - log.ts) * 1000
|
|
)})\n`
|
|
);
|
|
};
|
|
}
|
|
}
|
|
|
|
return (req, res, next) => {
|
|
let ts = performance.now();
|
|
/** @type {Logging} */
|
|
let log = {
|
|
ts,
|
|
req,
|
|
};
|
|
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;
|