From 5472c2bb06479365dc581ee52f645fd651a3e12f Mon Sep 17 00:00:00 2001 From: Eugene Moon Date: Sun, 2 Aug 2020 16:49:52 -0400 Subject: [PATCH 1/5] added a ColorTransform function --- package-lock.json | 19 ++++--------- package.json | 1 + src/Log.ts | 2 ++ src/transforms/ColorTransform.ts | 49 ++++++++++++++++++++++++++++++++ src/transforms/index.ts | 2 ++ 5 files changed, 60 insertions(+), 13 deletions(-) create mode 100644 src/transforms/ColorTransform.ts diff --git a/package-lock.json b/package-lock.json index c643a61..2d5d9bb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -716,8 +716,7 @@ "@types/color-name": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", - "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", - "dev": true + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==" }, "@types/graceful-fs": { "version": "4.1.3", @@ -938,7 +937,6 @@ "version": "4.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, "requires": { "@types/color-name": "^1.1.1", "color-convert": "^2.0.1" @@ -1264,10 +1262,9 @@ "dev": true }, "chalk": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", - "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", - "dev": true, + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -1345,7 +1342,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "requires": { "color-name": "~1.1.4" } @@ -1353,8 +1349,7 @@ "color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "combined-stream": { "version": "1.0.8", @@ -2074,8 +2069,7 @@ "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" }, "has-symbols": { "version": "1.0.1", @@ -4527,7 +4521,6 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, "requires": { "has-flag": "^4.0.0" } diff --git a/package.json b/package.json index fc553a7..b6d70bf 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ }, "dependencies": { "@nascentdigital/errors": "^1.0.0", + "chalk": "^4.1.0", "string.prototype.matchall": "^4.0.2" }, "devDependencies": { diff --git a/src/Log.ts b/src/Log.ts index a740148..d291938 100644 --- a/src/Log.ts +++ b/src/Log.ts @@ -13,6 +13,8 @@ export const LogLevels: ReadonlyArray = [ ]; export type LogMethod = Exclude; +export type LogColoringOption = "none" | "level" | "namespace" + export type LogParameter = string | number | boolean | ReadonlyArray | Readonly | undefined | null; export type LogFunction = (log: Log, method: LogMethod, message: LogParameter, ...args: ReadonlyArray) => void; export type LogContext = { diff --git a/src/transforms/ColorTransform.ts b/src/transforms/ColorTransform.ts new file mode 100644 index 0000000..f207f9a --- /dev/null +++ b/src/transforms/ColorTransform.ts @@ -0,0 +1,49 @@ +import chalk from 'chalk' +import { LogContext, LogMethod, LogParameter, LogColoringOption } from "../Log"; + +function applyColorBasedOnLogMethod(message: LogParameter, method: LogMethod): LogParameter { + + /** + * TODO: add a check here to see if coloring is possible (if not, then no-op) + */ + + switch (method) { + case "trace": + return chalk.white(message) + case "debug": + return chalk.blue(message) + case "info": + return chalk.yellow(message) + case "warn": + return chalk.rgb(255, 165, 0)(message) + case "error": + return chalk.red(message) + default: + message + } +} + +export function ColorTransform(logColoringOption: LogColoringOption) { + + // create transform + return function (context: LogContext) { + + const { message, method, ...contextData } = context; + + let coloredMessage = message + + switch (logColoringOption) { + case "none": + break; + case "level": + coloredMessage = applyColorBasedOnLogMethod(message, method); + break; + case "namespace": + break; + default: + break; + } + + return { ...contextData, message: coloredMessage, method } + } +} diff --git a/src/transforms/index.ts b/src/transforms/index.ts index bd0eb5e..22c7009 100644 --- a/src/transforms/index.ts +++ b/src/transforms/index.ts @@ -2,3 +2,5 @@ export * from "./IdentityTransform"; export * from "./PrefixTransform"; export * from "./TransformBuilder"; +export * from "./ColorTransform"; + From e9c3adefc922c60ae9bff3610685be1d97d36cf4 Mon Sep 17 00:00:00 2001 From: uoojin1 Date: Fri, 2 Oct 2020 22:59:14 -0400 Subject: [PATCH 2/5] added ability to apply color based on log namespace --- src/Log.ts | 1 + src/Scribe.ts | 7 +++++-- src/transforms/ColorTransform.ts | 23 ++++++++++++++++++++--- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/Log.ts b/src/Log.ts index d291938..daf4a0b 100644 --- a/src/Log.ts +++ b/src/Log.ts @@ -14,6 +14,7 @@ export const LogLevels: ReadonlyArray = [ export type LogMethod = Exclude; export type LogColoringOption = "none" | "level" | "namespace" +export type LogColorRGB = [number, number, number] export type LogParameter = string | number | boolean | ReadonlyArray | Readonly | undefined | null; export type LogFunction = (log: Log, method: LogMethod, message: LogParameter, ...args: ReadonlyArray) => void; diff --git a/src/Scribe.ts b/src/Scribe.ts index b08a079..452f3ae 100644 --- a/src/Scribe.ts +++ b/src/Scribe.ts @@ -9,7 +9,8 @@ import { LogMethod, LogNamespace, LogNamespacePattern, - LogParameter, LogContext, LogTransform + LogParameter, LogContext, LogTransform, + LogColorRGB } from "./Log"; import {ScribeLog} from "./ScribeLog"; import {ConsoleWriter} from "./writers"; @@ -49,10 +50,11 @@ export class Scribe { private static _log: ScribeLog = Scribe.createRootLog(); private static readonly _logs = new Map([[ROOT_NAMESPACE, Scribe._log]]); private static readonly _levelConfigs: Array = [ROOT_LOGLEVEL_CONFIG]; - + private static readonly _logColors = new Map() public static get log() { return Scribe._log; } + public static get logColors() { return Scribe._logColors } public static reset() { @@ -63,6 +65,7 @@ export class Scribe { Scribe._logs.clear(); Scribe._logs.set(ROOT_NAMESPACE, Scribe._log); Scribe._levelConfigs.splice(0, Scribe._levelConfigs.length, ROOT_LOGLEVEL_CONFIG); + Scribe._logColors.clear() } public static getLog(namespace: LogNamespace): Log { diff --git a/src/transforms/ColorTransform.ts b/src/transforms/ColorTransform.ts index f207f9a..6fdd3ef 100644 --- a/src/transforms/ColorTransform.ts +++ b/src/transforms/ColorTransform.ts @@ -1,5 +1,6 @@ import chalk from 'chalk' -import { LogContext, LogMethod, LogParameter, LogColoringOption } from "../Log"; +import { LogContext, LogMethod, LogParameter, LogColoringOption, LogNamespace, LogColorRGB } from "../Log"; +import { Scribe } from '../Scribe'; function applyColorBasedOnLogMethod(message: LogParameter, method: LogMethod): LogParameter { @@ -23,12 +24,27 @@ function applyColorBasedOnLogMethod(message: LogParameter, method: LogMethod): L } } +function applyColorBasedOnLogNamespace(message: LogParameter, namespace: LogNamespace): LogParameter { + let logColorRGB: LogColorRGB = [0, 0, 0] + + if (Scribe.logColors.has(namespace)) { + logColorRGB = Scribe.logColors.get(namespace) as LogColorRGB + } + + else { + logColorRGB = [255*Math.random(), 255*Math.random(), 255*Math.random()] + Scribe.logColors.set(namespace, logColorRGB) + } + + return chalk.rgb(...logColorRGB)(message) +} + export function ColorTransform(logColoringOption: LogColoringOption) { // create transform return function (context: LogContext) { - const { message, method, ...contextData } = context; + const { message, method, log: { namespace } } = context; let coloredMessage = message @@ -39,11 +55,12 @@ export function ColorTransform(logColoringOption: LogColoringOption) { coloredMessage = applyColorBasedOnLogMethod(message, method); break; case "namespace": + coloredMessage = applyColorBasedOnLogNamespace(message, namespace) break; default: break; } - return { ...contextData, message: coloredMessage, method } + return { ...context, message: coloredMessage, method } } } From b6163a64262a7e1daabdd5a6d29aaa27f35cca72 Mon Sep 17 00:00:00 2001 From: Simeon de Dios Date: Mon, 19 Oct 2020 22:38:46 -0400 Subject: [PATCH 3/5] Updates, refactoring, breaking the code for Eugene. --- src/Log.ts | 4 - src/Scribe.ts | 11 +- src/transforms/ColorTransform.ts | 166 +++++++++++++++++++++---------- 3 files changed, 120 insertions(+), 61 deletions(-) diff --git a/src/Log.ts b/src/Log.ts index daf4a0b..0732740 100644 --- a/src/Log.ts +++ b/src/Log.ts @@ -12,10 +12,6 @@ export const LogLevels: ReadonlyArray = [ "silent" ]; export type LogMethod = Exclude; - -export type LogColoringOption = "none" | "level" | "namespace" -export type LogColorRGB = [number, number, number] - export type LogParameter = string | number | boolean | ReadonlyArray | Readonly | undefined | null; export type LogFunction = (log: Log, method: LogMethod, message: LogParameter, ...args: ReadonlyArray) => void; export type LogContext = { diff --git a/src/Scribe.ts b/src/Scribe.ts index 452f3ae..65f62fc 100644 --- a/src/Scribe.ts +++ b/src/Scribe.ts @@ -3,14 +3,15 @@ import matchAll from "string.prototype.matchall"; import {ArgumentError, IllegalStateError} from "@nascentdigital/errors"; import { Log, - LogWriter, + LogContext, LogLevel, LogLevels, LogMethod, LogNamespace, LogNamespacePattern, - LogParameter, LogContext, LogTransform, - LogColorRGB + LogParameter, + LogTransform, + LogWriter, } from "./Log"; import {ScribeLog} from "./ScribeLog"; import {ConsoleWriter} from "./writers"; @@ -50,12 +51,9 @@ export class Scribe { private static _log: ScribeLog = Scribe.createRootLog(); private static readonly _logs = new Map([[ROOT_NAMESPACE, Scribe._log]]); private static readonly _levelConfigs: Array = [ROOT_LOGLEVEL_CONFIG]; - private static readonly _logColors = new Map() public static get log() { return Scribe._log; } - public static get logColors() { return Scribe._logColors } - public static reset() { // reset internals @@ -65,7 +63,6 @@ export class Scribe { Scribe._logs.clear(); Scribe._logs.set(ROOT_NAMESPACE, Scribe._log); Scribe._levelConfigs.splice(0, Scribe._levelConfigs.length, ROOT_LOGLEVEL_CONFIG); - Scribe._logColors.clear() } public static getLog(namespace: LogNamespace): Log { diff --git a/src/transforms/ColorTransform.ts b/src/transforms/ColorTransform.ts index 6fdd3ef..1ecc9aa 100644 --- a/src/transforms/ColorTransform.ts +++ b/src/transforms/ColorTransform.ts @@ -1,66 +1,132 @@ -import chalk from 'chalk' -import { LogContext, LogMethod, LogParameter, LogColoringOption, LogNamespace, LogColorRGB } from "../Log"; -import { Scribe } from '../Scribe'; - -function applyColorBasedOnLogMethod(message: LogParameter, method: LogMethod): LogParameter { - - /** - * TODO: add a check here to see if coloring is possible (if not, then no-op) - */ - - switch (method) { - case "trace": - return chalk.white(message) - case "debug": - return chalk.blue(message) - case "info": - return chalk.yellow(message) - case "warn": - return chalk.rgb(255, 165, 0)(message) - case "error": - return chalk.red(message) - default: - message - } +// imports +import {NotImplementedError, RuntimeError} from "@nascentdigital/errors"; +import Chalk from 'chalk' +import {LogContext, LogMethod, LogParameter, LogNamespace} from "../Log"; +import {Scribe} from '../Scribe'; + + +/** + * Thoughts by Sim: + * + * Interface for the Transform: + * 1. Options should provide some utility - you get something for setting them. "none" as an option seems to have no + * utility, since you might as well just not use the transform. + * 2. These options lack control - Why can't I set the colours, at least for "level" logging? + * + * Encapsulating via Modules: + * 1. You're violating the concept of "modules" - modules encapsulate functionality, not a namespace per-se, but they + * are larger than Classes (Java uses "packages", .NET uses "assemblies", NodeJS uses "modules"" + * - DLL/package/module(.js) -> Functions + Constants + Classes + etc. + * - Allows you to group common things, load them as a whole unit, hide shared / internal variables + functions + * 2. All code + types + state specific to a **module** should be contained by the module + * 3. Violations: + * - `LogColoringOption`, `LogColorRGB` in the "core" Log.ts + * - `Scribe.logColors` map in the global `Scribe` instance + * + * Naming: + * 1. Names seem really long, but clear + specific. You're 80% there - but the elegance comes to the distilling of + * the name. If you can shorten the name to something less than 12 characters - you're going to learn a lot about + * the function + your design. The name is usually too long because: + * a) The you're still too generic on or unclear of what it does + * b) You're trying to do too much with it + * c) You haven't contained it in something more specific (i.e. you have a global function that acutally should + * have prefixes of the name implied by the thing it belongs to - module, class, etc) + * + * + * TODO: + * 1. Fix this `LogColorRGB` type + * 2. Decouple browser vs non-browser handling... maybe later even pulling it out into a separate method + * - What's the best way / most reliable way to tell if you're in Node vs Browser (e.g. if (window), etc.) + * 3. Add some argument validation and custom exceptions (e.g. ArgumentOutOfRangeError if color is out of range) + * 3. Maybe change the `ColorTransform` function to be a factory class? + */ + + +class ArgumentOutOfRangeError extends RuntimeError {} + + +// types +export type LogColorRGB = { + red: number; + green: number; + blue: number; +} +export type LogColorHSL = { + hue: number; + saturation: number; + lightness: number; } +export type LogColor = LogColorRGB | LogColorHSL; -function applyColorBasedOnLogNamespace(message: LogParameter, namespace: LogNamespace): LogParameter { - let logColorRGB: LogColorRGB = [0, 0, 0] +function isRGB(color: LogColor) : color is LogColorRGB { + return Object.prototype.hasOwnProperty.call(color, "red") +} + + +export abstract class ColoringStrategy { + abstract getColor(context: LogContext): LogColor; +} - if (Scribe.logColors.has(namespace)) { - logColorRGB = Scribe.logColors.get(namespace) as LogColorRGB +export class NamespaceColoringStrategy extends ColoringStrategy { + + private readonly _namespaceColors = new Map() + + public getColor(context: LogContext): LogColor { + throw new Error() } +} - else { - logColorRGB = [255*Math.random(), 255*Math.random(), 255*Math.random()] - Scribe.logColors.set(namespace, logColorRGB) +export class LevelColoringStrategy extends ColoringStrategy { + + constructor(private _logMethodColors: Record) { + super(); } - return chalk.rgb(...logColorRGB)(message) + public getColor(context: LogContext): LogColorRGB { + return this._logMethodColors[context.method] + } } -export function ColorTransform(logColoringOption: LogColoringOption) { + +function ColorTransform(strategy: ColoringStrategy) { // create transform return function (context: LogContext) { - const { message, method, log: { namespace } } = context; - - let coloredMessage = message - - switch (logColoringOption) { - case "none": - break; - case "level": - coloredMessage = applyColorBasedOnLogMethod(message, method); - break; - case "namespace": - coloredMessage = applyColorBasedOnLogNamespace(message, namespace) - break; - default: - break; + // get color + const color = strategy.getColor(context) + + // TODO: abstract the transforming from color -> message + // apply color to message + let message = context.message; + const isDesktop = true; + if (isDesktop) { + + // convert the color to a Chalk + const chalk = isRGB(color) + ? Chalk.rgb(color.red, color.green, color.blue) + : Chalk.hsl(color.hue, color.saturation, color.lightness) + + // convert message + message = chalk(message) + } + + // apply for browser + else { + throw new NotImplementedError('Browser support coming soon!'); } - - return { ...context, message: coloredMessage, method } + + // return transfomed context + return Object.assign({}, context, {message}); } } + + +function test() { + + Scribe.transform = ColorTransform(new NamespaceColoringStrategy()) + // Scribe.transform = ColorTransform(new LevelColoringStrategy({ + // debug: + // })) +} + From 9956d56b495a0b89f49a48c811a8a723ccb13428 Mon Sep 17 00:00:00 2001 From: uoojin1 Date: Sun, 25 Oct 2020 19:48:00 -0400 Subject: [PATCH 4/5] feat: addressing sim's feedbacks --- package-lock.json | 10 + package.json | 2 + src/transforms/ColorTransform.ts | 132 ----------- src/transforms/ColorTransformFactory.ts | 297 ++++++++++++++++++++++++ src/transforms/index.ts | 2 +- 5 files changed, 310 insertions(+), 133 deletions(-) delete mode 100644 src/transforms/ColorTransform.ts create mode 100644 src/transforms/ColorTransformFactory.ts diff --git a/package-lock.json b/package-lock.json index 2d5d9bb..f29bce1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -713,6 +713,11 @@ "@babel/types": "^7.3.0" } }, + "@types/browser-or-node": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@types/browser-or-node/-/browser-or-node-1.2.0.tgz", + "integrity": "sha512-hLn4jvpZ804yQDu71YW7qNQDm045XmODoEOZohkH4jWb23AaPodhVM5qztG+XM54Oqw8X1dA4A7z49iNFGbrxA==" + }, "@types/color-name": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", @@ -1187,6 +1192,11 @@ "fill-range": "^7.0.1" } }, + "browser-or-node": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/browser-or-node/-/browser-or-node-1.3.0.tgz", + "integrity": "sha512-0F2z/VSnLbmEeBcUrSuDH5l0HxTXdQQzLjkmBR4cYfvg1zJrKSlmIZFqyFR8oX0NrwPhy3c3HQ6i3OxMbew4Tg==" + }, "browser-process-hrtime": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", diff --git a/package.json b/package.json index b6d70bf..ceea2ed 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,8 @@ }, "dependencies": { "@nascentdigital/errors": "^1.0.0", + "@types/browser-or-node": "^1.2.0", + "browser-or-node": "^1.3.0", "chalk": "^4.1.0", "string.prototype.matchall": "^4.0.2" }, diff --git a/src/transforms/ColorTransform.ts b/src/transforms/ColorTransform.ts deleted file mode 100644 index 1ecc9aa..0000000 --- a/src/transforms/ColorTransform.ts +++ /dev/null @@ -1,132 +0,0 @@ -// imports -import {NotImplementedError, RuntimeError} from "@nascentdigital/errors"; -import Chalk from 'chalk' -import {LogContext, LogMethod, LogParameter, LogNamespace} from "../Log"; -import {Scribe} from '../Scribe'; - - -/** - * Thoughts by Sim: - * - * Interface for the Transform: - * 1. Options should provide some utility - you get something for setting them. "none" as an option seems to have no - * utility, since you might as well just not use the transform. - * 2. These options lack control - Why can't I set the colours, at least for "level" logging? - * - * Encapsulating via Modules: - * 1. You're violating the concept of "modules" - modules encapsulate functionality, not a namespace per-se, but they - * are larger than Classes (Java uses "packages", .NET uses "assemblies", NodeJS uses "modules"" - * - DLL/package/module(.js) -> Functions + Constants + Classes + etc. - * - Allows you to group common things, load them as a whole unit, hide shared / internal variables + functions - * 2. All code + types + state specific to a **module** should be contained by the module - * 3. Violations: - * - `LogColoringOption`, `LogColorRGB` in the "core" Log.ts - * - `Scribe.logColors` map in the global `Scribe` instance - * - * Naming: - * 1. Names seem really long, but clear + specific. You're 80% there - but the elegance comes to the distilling of - * the name. If you can shorten the name to something less than 12 characters - you're going to learn a lot about - * the function + your design. The name is usually too long because: - * a) The you're still too generic on or unclear of what it does - * b) You're trying to do too much with it - * c) You haven't contained it in something more specific (i.e. you have a global function that acutally should - * have prefixes of the name implied by the thing it belongs to - module, class, etc) - * - * - * TODO: - * 1. Fix this `LogColorRGB` type - * 2. Decouple browser vs non-browser handling... maybe later even pulling it out into a separate method - * - What's the best way / most reliable way to tell if you're in Node vs Browser (e.g. if (window), etc.) - * 3. Add some argument validation and custom exceptions (e.g. ArgumentOutOfRangeError if color is out of range) - * 3. Maybe change the `ColorTransform` function to be a factory class? - */ - - -class ArgumentOutOfRangeError extends RuntimeError {} - - -// types -export type LogColorRGB = { - red: number; - green: number; - blue: number; -} -export type LogColorHSL = { - hue: number; - saturation: number; - lightness: number; -} -export type LogColor = LogColorRGB | LogColorHSL; - -function isRGB(color: LogColor) : color is LogColorRGB { - return Object.prototype.hasOwnProperty.call(color, "red") -} - - -export abstract class ColoringStrategy { - abstract getColor(context: LogContext): LogColor; -} - -export class NamespaceColoringStrategy extends ColoringStrategy { - - private readonly _namespaceColors = new Map() - - public getColor(context: LogContext): LogColor { - throw new Error() - } -} - -export class LevelColoringStrategy extends ColoringStrategy { - - constructor(private _logMethodColors: Record) { - super(); - } - - public getColor(context: LogContext): LogColorRGB { - return this._logMethodColors[context.method] - } -} - - -function ColorTransform(strategy: ColoringStrategy) { - - // create transform - return function (context: LogContext) { - - // get color - const color = strategy.getColor(context) - - // TODO: abstract the transforming from color -> message - // apply color to message - let message = context.message; - const isDesktop = true; - if (isDesktop) { - - // convert the color to a Chalk - const chalk = isRGB(color) - ? Chalk.rgb(color.red, color.green, color.blue) - : Chalk.hsl(color.hue, color.saturation, color.lightness) - - // convert message - message = chalk(message) - } - - // apply for browser - else { - throw new NotImplementedError('Browser support coming soon!'); - } - - // return transfomed context - return Object.assign({}, context, {message}); - } -} - - -function test() { - - Scribe.transform = ColorTransform(new NamespaceColoringStrategy()) - // Scribe.transform = ColorTransform(new LevelColoringStrategy({ - // debug: - // })) -} - diff --git a/src/transforms/ColorTransformFactory.ts b/src/transforms/ColorTransformFactory.ts new file mode 100644 index 0000000..83302e7 --- /dev/null +++ b/src/transforms/ColorTransformFactory.ts @@ -0,0 +1,297 @@ +// imports +import {NotImplementedError, RuntimeError} from "@nascentdigital/errors"; +import {isBrowser, isNode} from "browser-or-node" +import Chalk from'chalk' +import {LogContext, LogMethod, LogParameter, LogNamespace} from "../Log"; +import {Scribe} from '../Scribe'; + + +/** + * Thoughts by Sim: + * + * Interface for the Transform: + * 1. Options should provide some utility - you get something for setting them. "none" as an option seems to have no + * utility, since you might as well just not use the transform. + * 2. These options lack control - Why can't I set the colours, at least for "level" logging? + * + * Encapsulating via Modules: + * 1. You're violating the concept of "modules" - modules encapsulate functionality, not a namespace per-se, but they + * are larger than Classes (Java uses "packages", .NET uses "assemblies", NodeJS uses "modules"" + * - DLL/package/module(.js) -> Functions + Constants + Classes + etc. + * - Allows you to group common things, load them as a whole unit, hide shared / internal variables + functions + * 2. All code + types + state specific to a **module** should be contained by the module + * 3. Violations: + * - `LogColoringOption`, `LogColorRGB` in the "core" Log.ts + * - `Scribe.logColors` map in the global `Scribe` instance + * + * Naming: + * 1. Names seem really long, but clear + specific. You're 80% there - but the elegance comes to the distilling of + * the name. If you can shorten the name to something less than 12 characters - you're going to learn a lot about + * the function + your design. The name is usually too long because: + * a) The you're still too generic on or unclear of what it does + * b) You're trying to do too much with it + * c) You haven't contained it in something more specific (i.e. you have a global function that acutally should + * have prefixes of the name implied by the thing it belongs to - module, class, etc) + * + * + * TODO: + * 1. Fix this `LogColorRGB` type + * 2. Decouple browser vs non-browser handling... maybe later even pulling it out into a separate method + * - What's the best way / most reliable way to tell if you're in Node vs Browser (e.g. if (window), etc.) + * 3. Add some argument validation and custom exceptions (e.g. ArgumentOutOfRangeError if color is out of range) + * 3. Maybe change the `ColorTransform` function to be a factory class? + */ + +// error classes +class ArgumentOutOfRangeError extends RuntimeError {} +class UnsupportedEnvError extends RuntimeError {} +class UnsupportedFormatError extends RuntimeError {} + +// log color types +export type LogColorRGB = { + red: number; + green: number; + blue: number; +} +export type LogColorHSL = { + hue: number; + saturation: number; + lightness: number; +} +export type LogColor = LogColorRGB | LogColorHSL; + +// color transformation supported environments +enum SupportedEnvironments { + 'DESKTOP', + 'BROWSER' +} + +// utility functions +function isRGB(color: LogColor) : color is LogColorRGB { + return Object.prototype.hasOwnProperty.call(color, "red") +} + +function isHSL(color: LogColor) : color is LogColorHSL { + return Object.prototype.hasOwnProperty.call(color, "hue") +} + +function getEnvironment() { + if (isNode) { + return SupportedEnvironments.DESKTOP + } + else if (isBrowser) { + return SupportedEnvironments.BROWSER + } + else { + return undefined + } +} + +class ColorValidator { + constructor( + private _color: LogColor + ) {} + + _validateRGB(rgb: LogColorRGB) { + // red should be between 0 ~ 255 + if (rgb.red < 0 || rgb.red > 255) throw new ArgumentOutOfRangeError('value of red should be between 0 to 255') + // green should be between 0 ~ 255 + if (rgb.green < 0 || rgb.green > 255) throw new ArgumentOutOfRangeError('value of green should be between 0 to 255') + // blue + if (rgb.blue < 0 || rgb.blue > 255) throw new ArgumentOutOfRangeError('value of blue should be between 0 to 255') + } + + _validateHSL(hsl: LogColorHSL) { + // hue should be between 0 ~ 360 + if (hsl.hue < 0 || hsl.hue > 360) throw new ArgumentOutOfRangeError('value of hue should be between 0 to 360') + // saturation should be between 0 ~ 1 + if (hsl.saturation < 0 || hsl.saturation > 1) throw new ArgumentOutOfRangeError('value of saturation should be between 0 to 1') + // lightness should be between 0 ~ 360 + if (hsl.lightness < 0 || hsl.lightness > 1) throw new ArgumentOutOfRangeError('value of lightness should be between 0 to 1') + } + + /** + * method that validates if the given LogColor is valid + * @throws {ArgumentOutOfRangeError} if any of the color values are out of range + * @throws {} + */ + validate() { + if (isRGB(this._color)) { + this._validateRGB(this._color) + } + else if (isHSL(this._color)) { + this._validateHSL(this._color) + } + else { + throw new UnsupportedFormatError('The color format should be either RGB or HSL.') + } + } +} + +export abstract class ColoringStrategy { + abstract getColor(context: LogContext): LogColor; +} + +/** + * This strategy can be used to apply color based on the log's namespace. + * a random color is generated for each namespace + */ +export class NamespaceColoringStrategy extends ColoringStrategy { + + private readonly _namespaceColors: Map = new Map() + + public getColor(context: LogContext): LogColor { + + // get namespace from the context + const namespace = context.log.namespace + + // find the log color for this namespace + if (this._namespaceColors.has(namespace)) { + + // return the existing color + return this._namespaceColors.get(namespace) as LogColor + } + + // create a new color for this namespace since it doesn't already exist + const logColor: LogColorRGB = { + red: 255 * Math.random(), + green: 255 * Math.random(), + blue: 255 * Math.random(), + } + + // set the new color as the color for this namespace + this._namespaceColors.set(namespace, logColor) + + // return the new color + return logColor + } +} + +/** + * This strategy can be used to apply color based on the log level + */ +export class LevelColoringStrategy extends ColoringStrategy { + /** + * @param _levelColors a mapping from log method to its RGB color + */ + constructor(private _levelColors: Record) { + super(); + } + + public getColor(context: LogContext): LogColor { + return this._levelColors[context.method] + } +} + +export class ColorTransformFactory { + /** + * @param strategy strategy for coloring the log message + */ + static create(strategy: ColoringStrategy) { + return function (context: LogContext) { + + // get color + const color = strategy.getColor(context) + + // validate color + const colorValidator = new ColorValidator(color) + colorValidator.validate() + + // TODO: abstract the transforming from color -> message + // apply color to message + let message = context.message; + + // get the current environment + const env = getEnvironment() + + // check if the code is running on desktop + if (env === SupportedEnvironments.DESKTOP) { + // convert the color to a Chalk + const chalk = isRGB(color) + ? Chalk.rgb(color.red, color.green, color.blue) + : Chalk.hsl(color.hue, color.saturation*100.0, color.lightness*100.0) + + // convert message + message = chalk(message) + } + + // apply for browser + else if (env == SupportedEnvironments.BROWSER) { + throw new NotImplementedError('Browser support comming soon!') + } + + // any other environments are unsupported + else { + throw new UnsupportedEnvError('Color transform only supports browser and node environment'); + } + + // return transfomed context + return Object.assign({}, context, {message}); + } + } +} + +function test() { + const globalLog = Scribe.log + + Scribe.transform = ColorTransformFactory.create(new LevelColoringStrategy({ + 'trace': { + 'red': 0, + 'green': 0, + 'blue': 0 + } as LogColorRGB, + 'debug': { + 'hue': 30, + 'saturation': 0.8, + 'lightness': 0.5 + } as LogColorHSL, + 'info': { + 'red': 100, + 'green': 23, + 'blue': 160 + } as LogColorRGB, + 'warn': { + 'red': 230, + 'green': 22, + 'blue': 190 + } as LogColorRGB, + 'error': { + 'red': 0, + 'green': 50, + 'blue': 200 + } as LogColorRGB, + })) + + globalLog.trace('trace log') + globalLog.debug('debug log') + globalLog.info('info log') + globalLog.warn('warn log') + globalLog.error('error log') + + const logForModuleA = Scribe.getLog("moduleA") + const logForModuleAMethodFoo = Scribe.getLog("moduleA:foo") + const logForModuleB = Scribe.getLog("moduleB") + const logForModuleC = Scribe.getLog("moduleC") + const logForModuleD = Scribe.getLog("moduleD") + const logForModuleE = Scribe.getLog("moduleE") + const logForModuleF = Scribe.getLog("moduleF") + const logForModuleG = Scribe.getLog("moduleG") + const logForModuleH = Scribe.getLog("moduleH") + + Scribe.transform = ColorTransformFactory.create(new NamespaceColoringStrategy()) + + logForModuleA.debug("debug message from moduleA") + logForModuleA.debug("debug message from moduleA") + logForModuleAMethodFoo.debug("debug message from moduleA:foo") + logForModuleAMethodFoo.debug("debug message from moduleA:foo") + logForModuleB.debug("debug message from moduleB") + logForModuleB.debug("debug message from moduleB") + logForModuleC.debug("debug message from moduleC") + logForModuleD.debug("debug message from moduleD") + logForModuleE.debug("debug message from moduleE") + logForModuleF.debug("debug message from moduleF") + logForModuleG.debug("debug message from moduleG") + logForModuleH.debug("debug message from moduleH") + +} + diff --git a/src/transforms/index.ts b/src/transforms/index.ts index 22c7009..b0db553 100644 --- a/src/transforms/index.ts +++ b/src/transforms/index.ts @@ -2,5 +2,5 @@ export * from "./IdentityTransform"; export * from "./PrefixTransform"; export * from "./TransformBuilder"; -export * from "./ColorTransform"; +export * from "./ColorTransformFactory"; From 7b3f9f106b9f9cb893e1800daefdd69b36395b56 Mon Sep 17 00:00:00 2001 From: uoojin1 Date: Sun, 25 Oct 2020 19:50:46 -0400 Subject: [PATCH 5/5] fix: fixing comments --- src/transforms/ColorTransformFactory.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/transforms/ColorTransformFactory.ts b/src/transforms/ColorTransformFactory.ts index 83302e7..5a5d748 100644 --- a/src/transforms/ColorTransformFactory.ts +++ b/src/transforms/ColorTransformFactory.ts @@ -97,7 +97,7 @@ class ColorValidator { if (rgb.red < 0 || rgb.red > 255) throw new ArgumentOutOfRangeError('value of red should be between 0 to 255') // green should be between 0 ~ 255 if (rgb.green < 0 || rgb.green > 255) throw new ArgumentOutOfRangeError('value of green should be between 0 to 255') - // blue + // blue should be between 0 ~ 255 if (rgb.blue < 0 || rgb.blue > 255) throw new ArgumentOutOfRangeError('value of blue should be between 0 to 255') } @@ -106,7 +106,7 @@ class ColorValidator { if (hsl.hue < 0 || hsl.hue > 360) throw new ArgumentOutOfRangeError('value of hue should be between 0 to 360') // saturation should be between 0 ~ 1 if (hsl.saturation < 0 || hsl.saturation > 1) throw new ArgumentOutOfRangeError('value of saturation should be between 0 to 1') - // lightness should be between 0 ~ 360 + // lightness should be between 0 ~ 1 if (hsl.lightness < 0 || hsl.lightness > 1) throw new ArgumentOutOfRangeError('value of lightness should be between 0 to 1') }