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;