diff --git a/changelog b/changelog new file mode 100644 index 0000000..3ca98a1 --- /dev/null +++ b/changelog @@ -0,0 +1,8 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [1.1.0] - 29-09-2024 \ No newline at end of file diff --git a/index.ts b/index.ts index 9d699de..496ee9b 100644 --- a/index.ts +++ b/index.ts @@ -1,10 +1,15 @@ import chalk from "chalk"; import {DateTime} from "luxon"; import _ from "lodash"; +import {config} from "dotenv"; +config(); + +type ILevel = "debug" | "info" | "warn" | "error" | "fatal"; class Logger { nameService: string | null = null; instanceLogger: Function = console.log; + level: ILevel = "info"; constructor(nameService: string, instanceLogger?: Function){ this.nameService = nameService.toUpperCase(); @@ -12,29 +17,55 @@ class Logger { if (!_.isNil(instanceLogger)){ this.instanceLogger = instanceLogger; } + + const level = (process.env.LOG_LEVEL).toLowerCase() as ILevel; + switch(level){ + case "debug": + case "error": + case "info": + case "warn": + case "fatal": + this.level = level; + break; + default: + if(!_.isNil(level) && level !== "null" && level !== "undefined" && level !== ""){ + this.#baseLog("fatal", "Not exist current level:", `"${level}"`, "select one of this: [debug|error|info|warn|fatal]") + process.exit(1); + } + } } debug(...args: Array){ - this.#baseLog("debug", ...args); + if(this.level === "debug"){ + this.#baseLog("debug", ...args); + } } info(...args: Array){ - this.#baseLog("info", ...args); + if((["info", "debug"] as Array).includes(this.level)){ + this.#baseLog("info", ...args); + } } warn(...args: Array){ - this.#baseLog("warning", ...args); + if((["info", "debug", "warn"] as Array).includes(this.level)){ + this.#baseLog("warn", ...args); + } } error(...args: Array){ - this.#baseLog("error", ...args); + if((["info", "debug", "warn", "error"] as Array).includes(this.level)){ + this.#baseLog("error", ...args); + } } fatal(...args: Array){ - this.#baseLog("fatal", ...args); + if((["info", "debug", "warn", "error", "fatal"] as Array).includes(this.level)){ + this.#baseLog("fatal", ...args); + } } - #baseLog(type: "debug" | "info" | "warning" | "error" | "fatal", ...args: Array){ + #baseLog(type: ILevel, ...args: Array){ let message = `[${DateTime.now().toFormat("dd-MM-yyyy hh:mm:ssZZ")}] [${type.toUpperCase()}] [${this.nameService}]`; args = args.map((message) => { @@ -52,7 +83,7 @@ class Logger { case "info": message = chalk.blue(message, ...args); break; - case "warning": + case "warn": message = chalk.yellow(message, ...args); break; case "error": @@ -67,4 +98,8 @@ class Logger { } } -export default Logger; \ No newline at end of file +export default Logger; + +export { + ILevel +} \ No newline at end of file diff --git a/package.json b/package.json index 6895bcd..e0642b6 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "typecheck": "tsc --noEmit", "eslint": "eslint", "eslint-fix": "eslint --fix", - "test_dev": "cd ./test && vitest", + "test-dev": "cd ./test && vitest", "test": "cd ./test && FORCE_COLOR=1 vitest run", "build": "tsc" }, @@ -22,6 +22,7 @@ "@types/luxon": "^3.4.2", "@types/node": "^22.7.4", "chalk": "^5.3.0", + "dotenv": "^16.4.5", "eslint": "^9.11.1", "lodash": "^4.17.21", "luxon": "^3.5.0", diff --git a/test/index.test.ts b/test/index.test.ts index 9c4a379..65f88fe 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -1,58 +1,178 @@ -import Logger from "../index"; -import { describe, it, expect, assert } from "vitest"; -let output = ""; +import { describe, it, expect, assert, vi } from "vitest"; -function captureLog(message: any){ +import Logger, { ILevel } from "../index"; +import {alternateCase, listLevel} from "./utils/utils" + +let output: any = null; + +function captureLog(message: string){ + //@ts-ignore output = JSON.stringify(message).replaceAll("\"", ""); } +process.env.LOG_LEVEL = "debug"; + describe("Test format", () => { - const logRegex = /\\u001b\[\d{1,3}m\[\d{2}-\d{2}-\d{4} \d{2}:\d{2}:\d{2}\+\d{2}:\d{2}\] \[(DEBUG|INFO|WARNING|ERROR|FATAL)\] \[TEST] .+?\\u001b\[\d{1,3}m/; - const logger = new Logger("test", captureLog); - - it("check format debug", () => { - logger.debug("Hi!"); - assert.match(output, logRegex, "Format isn't correct"); - }); - it("check format error", () => { - - logger.error("Hi!"); - assert.match(output, logRegex, "Format isn't correct"); - }); - it("check format fatal", () => { - logger.fatal("Hi!"); - assert.match(output, logRegex, "Format isn't correct"); - }); - it("check format info", () => { - logger.info("Hi!"); - assert.match(output, logRegex, "Format isn't correct"); - }); - it("check format warning", () => { - logger.warn("Hi!"); - assert.match(output, logRegex, "Format isn't correct"); - }); + for (const objectLevel of listLevel) { + it(`check format ${objectLevel.level}`, () => { + output = null; + let logRegex = `\\\\u001b\\[\\d{1,3}m\\[\\d{2}-\\d{2}-\\d{4} \\d{2}:\\d{2}:\\d{2}\\+\\d{2}:\\d{2}\\] \\[${objectLevel.level.toUpperCase()}\\] \\[TEST] .+?\\\\u001b\\[\\d{1,3}m`; + + const logger = new Logger("test", captureLog); + + logger[objectLevel.level]("Hi!"); + + assert.match(output, new RegExp(logRegex), `Format isn't correct for level --> ${objectLevel.level}`); + }); + } }); describe("Test colors", () => { - const logger = new Logger("test", captureLog); - it("check color debug", () => { - logger.debug("Hi!"); - expect(output.startsWith("\\u001b[90m") && output.endsWith("\\u001b[39m")).eq(true); - }); - it("check color error", () => { - logger.error("Hi!"); - expect(output.startsWith("\\u001b[31m") && output.endsWith("\\u001b[39m")).eq(true); - }); - it("check color fatal", () => { - logger.fatal("Hi!"); - expect(output.startsWith("\\u001b[91m") && output.endsWith("\\u001b[39m")).eq(true); - }); - it("check color info", () => { - logger.info("Hi!"); - expect(output.startsWith("\\u001b[34m") && output.endsWith("\\u001b[39m")).eq(true); - }); - it("check color warning", () => { - logger.warn("Hi!"); - expect(output.startsWith("\\u001b[33m") && output.endsWith("\\u001b[39m")).eq(true); - }); + for (const objectLevel of listLevel) { + it(`check color ${objectLevel.level}`, () => { + output = null; + + const logger = new Logger("test", captureLog); + logger[objectLevel.level]("Hi!"); + expect(output.startsWith(objectLevel.color) && output.endsWith("\\u001b[39m")).eq(true); + }); + } +}); + +describe("Test log level from ENV", () => { + it("Log level: (empty)", () => { + output = null; + process.env.LOG_LEVEL = ""; + + const logger = new Logger("test", captureLog); + output = ""; + logger.debug("Hi! custom"); + + expect(output).empty + }) + + it("Log level: null", () => { + output = null; + //@ts-ignore + process.env.LOG_LEVEL = null; + + const logger = new Logger("test", captureLog); + output = ""; + logger.debug("Hi! custom"); + + expect(output).empty + }) + + it("Log level: undefined", () => { + output = null; + process.env.LOG_LEVEL = undefined; + + const logger = new Logger("test", captureLog); + output = ""; + logger.debug("Hi! custom"); + + expect(output).empty + }) + + for (const objectLevel of listLevel) { + it(`Log level: ${objectLevel.level}`, () => { + output = null; + process.env.LOG_LEVEL = objectLevel.level; + + const logger = new Logger("test", captureLog); + logger[objectLevel.level]("Hi!"); + expect(output).not.null + }); + } + + for (const objectLevel of listLevel) { + it(`Log level: ${objectLevel.level.toUpperCase()}`, () => { + output = null; + process.env.LOG_LEVEL = objectLevel.level.toUpperCase(); + + const logger = new Logger("test", captureLog); + logger[objectLevel.level]("Hi!"); + expect(output).not.null + }); + } + + for (const objectLevel of listLevel) { + it(`Log level: ${alternateCase(objectLevel.level)}`, () => { + output = null; + process.env.LOG_LEVEL = alternateCase(objectLevel.level); + + const logger = new Logger("test", captureLog); + logger[objectLevel.level]("Hi!"); + expect(output).not.null + }); + } + + it("Log level: debrn (not exist)", () => { + process.env.LOG_LEVEL = "debrn"; + const exitCode = vi.spyOn(process, 'exit'); + + try{ + new Logger("test", captureLog); + }catch{ + //ignore + } + + expect(exitCode).toHaveBeenCalledWith(1); + }) + + it("Log level: 1234 (not exist)", () => { + //@ts-ignore + process.env.LOG_LEVEL = 1234; + const exitCode = vi.spyOn(process, 'exit'); + + try{ + new Logger("test", captureLog); + }catch{ + //ignore + } + + expect(exitCode).toHaveBeenCalledWith(1); + }) +}) + + +describe("Test print log level", () => { + for (const objectLevel of listLevel) { + it(`Log level: ${objectLevel.level}`, () => { + for (const singleLevel of listLevel.map((singleObjectlevel) => singleObjectlevel.level)) { + output = null; + process.env.LOG_LEVEL = singleLevel; + + const logger = new Logger("test", captureLog); + logger[singleLevel]("Hi!"); + + if(objectLevel.level === "debug"){ + expect(output).not.null + continue; + } + + if((["debug", "info"] as Array)){ + expect(output).not.null + continue; + } + + if((["debug", "info", "warn"] as Array)){ + expect(output).not.null + continue; + } + + if((["debug", "info", "error"] as Array)){ + expect(output).not.null + continue; + } + + if((["debug", "info", "error", "fatal"] as Array)){ + expect(output).not.null + continue; + } + + expect(output).null; + } + }); + } }); \ No newline at end of file diff --git a/test/utils/utils.ts b/test/utils/utils.ts new file mode 100644 index 0000000..0c2f382 --- /dev/null +++ b/test/utils/utils.ts @@ -0,0 +1,32 @@ +import { ILevel } from "../.."; + +const listLevel: Array<{level: ILevel, color: string}> = [{ + level: "debug", + color: "\\u001b[90m" +},{ + level: "error", + color: "\\u001b[31m" +},{ + level: "fatal", + color: "\\u001b[91m" +},{ + level: "info", + color: "\\u001b[34m" +},{ + level: "warn", + color: "\\u001b[33m" +}]; + +function alternateCase(text: string){ + return text + .split('') + .map((char, index) => + index % 2 === 0 ? char.toLowerCase() : char.toUpperCase() + ) + .join(''); +} + +export { + alternateCase, + listLevel +} \ No newline at end of file