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;