@tinyhttp/logger
Advanced tools
| export declare class FileLogger { | ||
| #private; | ||
| private writableStream; | ||
| constructor(filename: string); | ||
| toFile(stringToLog: string): void; | ||
| } |
| import { accessSync, writeFileSync, createWriteStream, mkdirSync } from 'node:fs'; | ||
| import { dirname as directoryname } from 'node:path'; | ||
| export class FileLogger { | ||
| #filename; | ||
| #dirname; | ||
| writableStream; | ||
| constructor(filename) { | ||
| this.#dirname = directoryname(filename); | ||
| this.#filename = filename; | ||
| this.#_stat(); | ||
| this.#_createWritableStream(); | ||
| this.#_endStream(); | ||
| } | ||
| #fsAccess(filename, mode) { | ||
| try { | ||
| accessSync(filename, mode); | ||
| return true; | ||
| } | ||
| catch (error) { | ||
| return false; | ||
| } | ||
| } | ||
| #_stat() { | ||
| //check if file exists | ||
| if (!this.#fsAccess(this.#filename)) { | ||
| // check if directory exists | ||
| if (!this.#fsAccess(this.#dirname)) { | ||
| // create the directory | ||
| mkdirSync(this.#dirname, { recursive: true }); | ||
| } | ||
| // create the file and write an empty string to it | ||
| writeFileSync(this.#filename, ''); | ||
| return; | ||
| } | ||
| } | ||
| #_createWritableStream() { | ||
| this.writableStream = createWriteStream(this.#filename, { flags: 'a' }); | ||
| } | ||
| toFile(stringToLog) { | ||
| this.writableStream.write(stringToLog + '\n'); | ||
| } | ||
| #_endStream() { | ||
| process.on('exit', () => { | ||
| this.writableStream.close(); | ||
| }); | ||
| process.on('SIGTERM', () => { | ||
| this.writableStream.close(); | ||
| process.exit(0); | ||
| }); | ||
| process.on('SIGINT', () => { | ||
| this.writableStream.close(); | ||
| process.exit(0); | ||
| }); | ||
| process.on('uncaughtException', () => { | ||
| this.writableStream.close(); | ||
| process.exit(1); | ||
| }); | ||
| } | ||
| } |
+10
-1
@@ -1,2 +0,9 @@ | ||
| import { ServerResponse as Response, IncomingMessage as Request } from 'http'; | ||
| import { ServerResponse as Response, IncomingMessage as Request } from 'node:http'; | ||
| export declare enum LogLevel { | ||
| error = "error", | ||
| warn = "warn", | ||
| trace = "trace", | ||
| info = "info", | ||
| log = "log" | ||
| } | ||
| export interface LoggerOptions { | ||
@@ -6,3 +13,5 @@ methods?: string[]; | ||
| color: boolean; | ||
| filename?: string; | ||
| callback: (string: string) => void; | ||
| level?: LogLevel; | ||
| }; | ||
@@ -9,0 +18,0 @@ timestamp?: boolean | { |
+34
-17
@@ -1,14 +0,24 @@ | ||
| import { magenta, bold, red, cyan } from 'colorette'; | ||
| import { cyan, red, magenta, bold } from 'colorette'; | ||
| import statusEmoji from 'http-status-emojis'; | ||
| import dayjs from 'dayjs'; | ||
| import { METHODS } from 'http'; | ||
| import { METHODS } from 'node:http'; | ||
| import { FileLogger } from './filelogger.js'; | ||
| export var LogLevel; | ||
| (function (LogLevel) { | ||
| LogLevel["error"] = "error"; | ||
| LogLevel["warn"] = "warn"; | ||
| LogLevel["trace"] = "trace"; | ||
| LogLevel["info"] = "info"; | ||
| LogLevel["log"] = "log"; | ||
| })(LogLevel || (LogLevel = {})); | ||
| const compileArgs = (args, req, res, options = {}, status, msg) => { | ||
| var _a, _b; | ||
| const { method } = req; | ||
| const { statusCode } = res; | ||
| const url = req.originalUrl || req.url; | ||
| const methods = (_a = options.methods) !== null && _a !== void 0 ? _a : METHODS; | ||
| const timestamp = (_b = options.timestamp) !== null && _b !== void 0 ? _b : false; | ||
| const methods = options.methods ?? METHODS; | ||
| const timestamp = options.timestamp ?? false; | ||
| const emojiEnabled = options.emoji; | ||
| const level = options.output && options.output.level ? options.output.level : null; | ||
| if (level) | ||
| args.push('[' + level.toUpperCase() + ']'); | ||
| if (methods.includes(method) && timestamp) { | ||
@@ -28,15 +38,20 @@ args.push(`${dayjs() | ||
| }; | ||
| const logger = (options = {}) => { | ||
| var _a, _b; | ||
| const methods = (_a = options.methods) !== null && _a !== void 0 ? _a : METHODS; | ||
| const output = (_b = options.output) !== null && _b !== void 0 ? _b : { callback: console.log, color: true }; | ||
| export const logger = (options = {}) => { | ||
| const methods = options.methods ?? METHODS; | ||
| const output = options.output ?? { callback: console.log, color: true, level: null }; | ||
| let filelogger = null; | ||
| if (options.output && options.output.filename) { | ||
| filelogger = new FileLogger(options.output.filename); | ||
| } | ||
| return (req, res, next) => { | ||
| res.on('finish', () => { | ||
| const args = []; | ||
| // every time | ||
| if (methods.includes(req.method)) { | ||
| const s = res.statusCode.toString(); | ||
| let stringToLog = ''; | ||
| if (!output.color) { | ||
| compileArgs(args, req, res, options); | ||
| const m = args.join(' '); | ||
| output.callback(m); | ||
| stringToLog = m; | ||
| } | ||
@@ -47,20 +62,22 @@ else { | ||
| compileArgs(args, req, res, options, cyan(bold(s)), cyan(res.statusMessage)); | ||
| output.callback(args.join(' ')); | ||
| stringToLog = args.join(' '); | ||
| break; | ||
| case '4': | ||
| compileArgs(args, req, res, options, red(bold(s)), red(res.statusMessage)); | ||
| output.callback(args.join(' ')); | ||
| stringToLog = args.join(' '); | ||
| break; | ||
| case '5': | ||
| compileArgs(args, req, res, options, magenta(bold(s)), magenta(res.statusMessage)); | ||
| output.callback(args.join(' ')); | ||
| stringToLog = args.join(' '); | ||
| break; | ||
| } | ||
| } | ||
| output.callback(stringToLog); | ||
| if (filelogger) { | ||
| filelogger.toFile(stringToLog); | ||
| } | ||
| } | ||
| }); | ||
| next === null || next === void 0 ? void 0 : next(); | ||
| next?.(); | ||
| }; | ||
| }; | ||
| export { logger }; |
+33
-25
| { | ||
| "name": "@tinyhttp/logger", | ||
| "version": "1.3.4", | ||
| "version": "2.0.0", | ||
| "type": "module", | ||
@@ -12,6 +12,16 @@ "description": "Minimal and flexible HTTP logger.", | ||
| "engines": { | ||
| "node": ">=12.4 || 14.x || >=16" | ||
| "node": ">=14.18 || >=16.20" | ||
| }, | ||
| "types": "./dist/index.d.ts", | ||
| "exports": "./dist/index.js", | ||
| "module": "./dist/index.js", | ||
| "exports": { | ||
| ".": { | ||
| "import": "./dist/index.js", | ||
| "types": "./dist/index.d.ts" | ||
| }, | ||
| "./filelogger": { | ||
| "import": "./dist/filelogger.js", | ||
| "types": "./dist/filelogger.d.ts" | ||
| } | ||
| }, | ||
| "keywords": [ | ||
@@ -31,31 +41,29 @@ "tinyhttp", | ||
| "colorette": "^2.0.20", | ||
| "dayjs": "^1.11.7", | ||
| "dayjs": "^1.11.10", | ||
| "http-status-emojis": "^2.2.0" | ||
| }, | ||
| "devDependencies": { | ||
| "@commitlint/cli": "17.6.3", | ||
| "@commitlint/config-conventional": "17.6.3", | ||
| "@rollup/plugin-typescript": "^11.1.1", | ||
| "@tinyhttp/app": "2.0.32", | ||
| "@types/node": "^20.1.4", | ||
| "@typescript-eslint/eslint-plugin": "^5.59.5", | ||
| "@typescript-eslint/parser": "^5.59.5", | ||
| "c8": "^7.13.0", | ||
| "eslint": "^8.40.0", | ||
| "eslint-config-prettier": "^8.8.0", | ||
| "eslint-plugin-prettier": "^4.2.1", | ||
| "expect": "^29.5.0", | ||
| "@commitlint/cli": "^18.2.0", | ||
| "@commitlint/config-conventional": "^18.1.0", | ||
| "@tinyhttp/app": "^2.2.0", | ||
| "@types/node": "^20.8.9", | ||
| "@typescript-eslint/eslint-plugin": "^6.9.0", | ||
| "@typescript-eslint/parser": "^6.9.0", | ||
| "bun-types": "^1.0.7", | ||
| "c8": "^8.0.1", | ||
| "eslint": "^8.52.0", | ||
| "eslint-config-prettier": "^9.0.0", | ||
| "eslint-plugin-prettier": "^5.0.1", | ||
| "expect": "^29.7.0", | ||
| "husky": "^8.0.3", | ||
| "prettier": "^2.8.8", | ||
| "rollup": "^3.21.7", | ||
| "prettier": "^3.0.3", | ||
| "supertest-fetch": "^1.5.0", | ||
| "tsm": "^2.3.0", | ||
| "typescript": "^5.0.4", | ||
| "uvu": "^0.5.6" | ||
| "tsx": "^3.14.0", | ||
| "typescript": "^5.2.2" | ||
| }, | ||
| "scripts": { | ||
| "build": "rollup -c", | ||
| "test": "uvu -r tsm tests", | ||
| "test:coverage": "c8 --include=src pnpm test", | ||
| "test:report": "c8 report --reporter=text-lcov > coverage.lcov", | ||
| "build": "tsc", | ||
| "test": "tsx tests/index.test.ts", | ||
| "test:coverage": "c8 tsx --test tests/index.test.ts", | ||
| "test:report": "c8 report --reporter=text-lcov > lcov.info", | ||
| "lint": "eslint . --ext=ts", | ||
@@ -62,0 +70,0 @@ "format": "prettier --check \"./**/*.{ts,md}\"", |
+47
-12
@@ -25,12 +25,12 @@ <div align="center"> | ||
| Returns the middleware to log HTTP requests. | ||
| Returns the middleware for logging HTTP requests. | ||
| #### Options | ||
| - `methods` - a list of HTTP methods to log. Defaults to `http`'s `METHODS` | ||
| - `timestamp.format` - timestamp format. Gets consumed by [dayjs](https://day.js.org) library. If a string specified, used as a format, otherwise just enables it. | ||
| - `output.callback` - function that recieves the log generated by the logger. | ||
| - `output.color` - property that determines if the logger will generate a message with color. usefull for logging into the console, disable it if logging into file / http / etc... | ||
| - `emoji` - enable emojis for HTTP status code. See [http-status-emojis](https://github.com/bendrucker/http-status-emojis/blob/master/index.js) for a full list | ||
| - `ip` - log IP address | ||
| - `methods`: a list of HTTP methods to log. Defaults to `http`'s `METHODS`. | ||
| - `timestamp.format`: timestamp format. It is consumed by the [dayjs](https://day.js.org) library. If a string is specified, it is used as a format; otherwise just enabled. | ||
| - `output.callback`: a function that receives the log generated by the logger. | ||
| - `output.color`: a property that determines whether the logger will generate a message with color. Useful for logging into the console; disable if logging into a file or other colorless environments. | ||
| - `emoji`: enable emojis for HTTP status codes. See [http-status-emojis](https://github.com/bendrucker/http-status-emojis/blob/master/index.js) for a full list. | ||
| - `ip`: log IP address. | ||
@@ -56,9 +56,44 @@ ## Example | ||
| To Log a level, use the enum `LogLevel` | ||
| ```ts | ||
| import { App } from '@tinyhttp/app' | ||
| import { logger, LogLevel } from '@tinyhttp/logger' | ||
| new App() | ||
| .use( | ||
| logger({ | ||
| methods: ['GET', 'POST'], | ||
| timestamp: { format: 'HH:mm:ss' }, | ||
| output: { callback: console.log, color: false, level: LogLevel.warn } | ||
| }) | ||
| ) | ||
| .get('/', (req, res) => res.send('Hello world')) | ||
| .listen(3000) | ||
| ``` | ||
| This also includes a simple file logger. To stream to a file, simply supply the filename in the options. Supported file names innclude | ||
| `./file.log` or `./log/tiny.log` | ||
| ```ts | ||
| import { App } from '@tinyhttp/app' | ||
| import { logger } from '@tinyhttp/logger' | ||
| new App() | ||
| .use( | ||
| logger({ | ||
| methods: ['GET', 'POST'], | ||
| timestamp: { format: 'HH:mm:ss' }, | ||
| output: { callback: console.log, color: false, filename: './log/tiny.log' } | ||
| }) | ||
| ) | ||
| .get('/', (req, res) => res.send('Hello world')) | ||
| .listen(3000) | ||
| ``` | ||
| ## Alternatives | ||
| - [Pino](https://getpino.io) - super fast, all natural json logger. | ||
| - [Zoya](https://github.com/WoLfulus/zoya) - Truly highly composable logging utility. | ||
| - [Morgan](https://github.com/expressjs/morgan) - HTTP request logger middleware for node.js | ||
| - [concurrency-logger](https://github.com/pablosichert/concurrency-logger) - Log HTTP requests/responses separately, visualize their concurrency and report logs/errors in context of a request | ||
| - [Volleyball](https://github.com/glebec/volleyball) - Tiny HTTP logger for Express showing asynchronous requests and responses | ||
| - [@tinyhttp/logger](/mw/logger) - Simple HTTP logger for tinyhttp | ||
| - [Pino HTTP](https://github.com/pinojs/pino-http) - high-speed HTTP logger for Node.js | ||
| - [chrona](https://github.com/xambassador/chrona) - Simple HTTP request logger middleware for express.js inspired from koa-logger, written in typescript. | ||
@@ -65,0 +100,0 @@ [npm-url]: https://npmjs.com/package/@tinyhttp/logger |
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 2 instances in 1 package
11841
42.05%17
-10.53%7
40%169
122.37%104
50.72%1
-66.67%Updated