commit 559e100f18c89abeab6c933dd4677ae9d6b5e199 Author: slonkazoid Date: Mon Mar 11 00:24:35 2024 +0300 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d91db27 --- /dev/null +++ b/.gitignore @@ -0,0 +1,135 @@ +# ---> Node +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp +.cache + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +files/ +temp/ +config.json diff --git a/README.md b/README.md new file mode 100644 index 0000000..87cdf61 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# weird-express-logger + +weird express logger diff --git a/index.js b/index.js new file mode 100644 index 0000000..572b18f --- /dev/null +++ b/index.js @@ -0,0 +1,135 @@ +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; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..4322f93 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,13 @@ +{ + "name": "weird-express-logger", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "weird-express-logger", + "version": "1.0.0", + "license": "CC0-1.0" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..4118c75 --- /dev/null +++ b/package.json @@ -0,0 +1,13 @@ +{ + "name": "weird-express-logger", + "version": "1.0.0", + "description": "weird express logger", + "main": "index.js", + "type": "module", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "slonkazoid", + "license": "CC0-1.0" +}