From 3c49453439dc279956e9d852e54f49b1565f6999 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Lenon?= Date: Sun, 5 Feb 2023 23:55:34 -0300 Subject: [PATCH] fix(vanilla): use vanilla logger in the main instance --- package-lock.json | 4 +- package.json | 2 +- src/Constants/VanillaChannels.ts | 43 +++++++ .../NotImplementedConfigException.ts | 8 +- src/Factories/DriverFactory.ts | 2 +- src/Formatters/Formatter.ts | 4 +- src/Formatters/NoneFormatter.ts | 2 +- src/Logger/Logger.ts | 97 +++++++++++---- src/Logger/VanillaLogger.ts | 112 ------------------ src/index.ts | 1 - tests/Stubs/config/logging.ts | 10 +- tests/Stubs/transporters/vanillaLogger.ts | 3 +- tests/Unit/Formatters/NoneFormatterTest.ts | 48 ++++++++ tests/Unit/Formatters/RequestFormatterTest.ts | 8 ++ tests/Unit/Logger/VanillaLoggerTest.ts | 2 +- 15 files changed, 196 insertions(+), 150 deletions(-) create mode 100644 src/Constants/VanillaChannels.ts delete mode 100644 src/Logger/VanillaLogger.ts diff --git a/package-lock.json b/package-lock.json index 4010c08..d4581c8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@athenna/logger", - "version": "3.1.1", + "version": "3.1.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@athenna/logger", - "version": "3.1.1", + "version": "3.1.2", "license": "MIT", "dependencies": { "telegraf": "^4.11.2" diff --git a/package.json b/package.json index a8c198e..2137098 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@athenna/logger", - "version": "3.1.1", + "version": "3.1.2", "description": "The Athenna logging solution. Log in stdout, files and buckets.", "license": "MIT", "author": "João Lenon ", diff --git a/src/Constants/VanillaChannels.ts b/src/Constants/VanillaChannels.ts new file mode 100644 index 0000000..05a6e7c --- /dev/null +++ b/src/Constants/VanillaChannels.ts @@ -0,0 +1,43 @@ +/** + * @athenna/logger + * + * (c) João Lenon + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Athenna default vanilla channels. This configurations + * will be used by the "channelOrVanilla" method. If the + * configuration does not exist, than the vanilla will be set. + */ +export const VANILLA_CHANNELS = { + default: { + driver: 'stack', + channels: ['application'], + }, + stack: { + driver: 'stack', + channels: ['application'], + }, + discard: { + driver: 'null', + }, + console: { + level: 'trace', + formatter: 'cli', + driver: 'console', + }, + exception: { + level: 'trace', + formatter: 'none', + driver: 'console', + streamType: 'stderr', + }, + application: { + level: 'trace', + driver: 'console', + formatter: 'simple', + }, +} diff --git a/src/Exceptions/NotImplementedConfigException.ts b/src/Exceptions/NotImplementedConfigException.ts index 6a326ae..8039244 100644 --- a/src/Exceptions/NotImplementedConfigException.ts +++ b/src/Exceptions/NotImplementedConfigException.ts @@ -7,13 +7,15 @@ * file that was distributed with this source code. */ -import { Path, Exception } from '@athenna/common' +import { Path, Exception, Is } from '@athenna/common' export class NotImplementedConfigException extends Exception { - constructor(channelName: string, channels?: any[]) { + constructor(channelName: string) { let help = '' - if (channels && channels.length) { + const channels = Config.get('logging.channels') + + if (channels && !Is.Empty(channels)) { const availableConfigs = Object.keys(channels).join(', ') help += `Available configurations are: ${availableConfigs}.` diff --git a/src/Factories/DriverFactory.ts b/src/Factories/DriverFactory.ts index 4dd3181..f3fbcc8 100644 --- a/src/Factories/DriverFactory.ts +++ b/src/Factories/DriverFactory.ts @@ -95,7 +95,7 @@ export class DriverFactory { */ public static getChannelConfig(channelName: string): any { if (channelName === 'default') { - channelName = Config.get('logging.default') || channelName + channelName = Config.get('logging.default', channelName) } const channelConfig = Config.get(`logging.channels.${channelName}`) diff --git a/src/Formatters/Formatter.ts b/src/Formatters/Formatter.ts index 9490c34..e7757a4 100644 --- a/src/Formatters/Formatter.ts +++ b/src/Formatters/Formatter.ts @@ -141,7 +141,7 @@ export abstract class Formatter { const level = this.configs.level if (!Color[level]) { - return level + return Color.bold(`[ ${level} ]`) } return Color[level].bold(`[ ${level} ]`) @@ -154,7 +154,7 @@ export abstract class Formatter { const level = this.configs.level if (!Color[level]) { - return level + return Color.bold(`[${level.toUpperCase()}]`) } return Color[level].bold(`[${level.toUpperCase()}]`) diff --git a/src/Formatters/NoneFormatter.ts b/src/Formatters/NoneFormatter.ts index 70c8aaf..80df6c0 100644 --- a/src/Formatters/NoneFormatter.ts +++ b/src/Formatters/NoneFormatter.ts @@ -11,6 +11,6 @@ import { Formatter } from '#src/Formatters/Formatter' export class NoneFormatter extends Formatter { public format(message: string): string { - return this.clean(this.toString(message)) + return this.clean(this.applyColorsByChalk(this.toString(message))) } } diff --git a/src/Logger/Logger.ts b/src/Logger/Logger.ts index d7a83d8..9e48ae6 100644 --- a/src/Logger/Logger.ts +++ b/src/Logger/Logger.ts @@ -10,8 +10,8 @@ import { Color } from '@athenna/common' import { Config } from '@athenna/config' import { Driver } from '#src/Drivers/Driver' -import { VanillaLogger } from '#src/Logger/VanillaLogger' import { DriverFactory } from '#src/Factories/DriverFactory' +import { VANILLA_CHANNELS } from '../Constants/VanillaChannels.js' export class Logger { /** @@ -25,18 +25,27 @@ export class Logger { private runtimeConfigs = {} public constructor() { - if (!Config.exists(`logging.channels.${Config.get('logging.default')}`)) { - return this - } + this.channelOrVanilla(Config.get('logging.default')) + } + + /** + * Create a new standalone logger instance. Very + * useful to create new loggers without changing the + * channels that are already defined in the main instance. + */ + public static standalone(...configs: any[]) { + const logger = new Logger() - this.drivers.push(DriverFactory.fabricate('default', this.runtimeConfigs)) + logger.vanilla(...configs) + + return logger } /** * Set runtime configurations for drivers and * formatters. */ - public config(runtimeConfigs: any) { + public config(runtimeConfigs: any): Logger { this.runtimeConfigs = runtimeConfigs return this @@ -45,27 +54,66 @@ export class Logger { /** * Change the log channel. */ - public channel(...channels: string[]) { + public channel(...channels: string[]): Logger { this.drivers = [] - channels.forEach(c => { - this.drivers.push(DriverFactory.fabricate(c, this.runtimeConfigs)) + channels.forEach(channel => { + this.drivers.push(DriverFactory.fabricate(channel, this.runtimeConfigs)) }) return this } /** - * Call drivers to transport the log. + * Change the log drivers using vanilla configurations. + * This method does not depend in Athenna configuration + * files to be executed. */ - private async log(level: string, ...args: any[]): Promise { - const message = Color.apply(...args) + public vanilla(...configs: any[]): Logger { + this.drivers = [] - const promises = this.drivers.map((driver: Driver) => - driver.transport(level, message), - ) + if (!configs.length) { + this.drivers.push(DriverFactory.fabricateVanilla()) - return Promise.all(promises) + return this + } + + configs.forEach(config => { + this.drivers.push(DriverFactory.fabricateVanilla(config)) + }) + + return this + } + + /** + * Verify if channel configuration exists. If not, Athenna will + * use the default vanilla configurations as drivers. + */ + public channelOrVanilla(channel: string, configs = {}): Logger { + if (channel === 'default') { + channel = Config.get( + `logging.channels.${Config.get('logging.default')}`, + 'default', + ) + } + + if (Config.exists(`logging.channels.${channel}`)) { + return this.channel(channel) + } + + return this.vanilla({ + ...VANILLA_CHANNELS[channel], + ...configs, + }) + } + + /** + * Create a new standalone logger instance. Very + * useful to create new loggers without changing the + * channels that are already defined in the main instance. + */ + public standalone(...configs: any[]) { + return Logger.standalone(configs) } /** @@ -78,7 +126,7 @@ export class Logger { /** * Creates a log of type debug in channel. */ - public debug(...args): any | Promise { + public debug(...args: any[]): any | Promise { return this.log('debug', ...args) } @@ -118,12 +166,15 @@ export class Logger { } /** - * Get a new instance of any log driver - * with vanilla configurations. By default, - * vanilla logger will use the "console" driver - * and "none" formatter. + * Call drivers to transport the log. */ - public static getVanillaLogger(configs: any = {}): VanillaLogger { - return new VanillaLogger(configs) + private async log(level: string, ...args: any[]): Promise { + const message = Color.apply(...args) + + const promises = this.drivers.map((driver: Driver) => + driver.transport(level, message), + ) + + return Promise.all(promises) } } diff --git a/src/Logger/VanillaLogger.ts b/src/Logger/VanillaLogger.ts deleted file mode 100644 index cce0d60..0000000 --- a/src/Logger/VanillaLogger.ts +++ /dev/null @@ -1,112 +0,0 @@ -/** - * @athenna/logger - * - * (c) João Lenon - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -import { Color } from '@athenna/common' -import { Driver } from '#src/Drivers/Driver' -import { DriverFactory } from '#src/Factories/DriverFactory' - -export class VanillaLogger { - /** - * The driver responsible for transporting the logs. - */ - public drivers = [] - - public constructor(configs: any) { - this.drivers.push(DriverFactory.fabricateVanilla(configs)) - } - - /** - * Log a simple log message without using the log driver - * but using the Color engine. - */ - public simple(...args: any[]): void { - process.stdout.write(Color.apply(...args).concat('\n')) - } - - /** - * Set runtime configurations for drivers and - * formatters. - */ - public config(): VanillaLogger { - return this - } - - /** - * Change the log channel. - */ - public channel(): VanillaLogger { - return this - } - - /** - * Call drivers to transport the log. - * - * @param {string} level - * @param {string} args - * @return {any | Promise} - */ - private log(level: string, ...args: any[]): any | Promise { - const message = Color.apply(...args) - - const promises = this.drivers.map((driver: Driver) => - driver.transport(level, message), - ) - - return Promise.all(promises) - } - - /** - * Creates a log of type trace in channel. - */ - public trace(...args: any[]): any | Promise { - return this.log('trace', ...args) - } - - /** - * Creates a log of type debug in channel. - */ - public debug(...args: any[]): any | Promise { - return this.log('debug', ...args) - } - - /** - * Creates a log of type info in channel. - */ - public info(...args: any[]): any | Promise { - return this.log('info', ...args) - } - - /** - * Creates a log of type success in channel. - */ - public success(...args: any[]): any | Promise { - return this.log('success', ...args) - } - - /** - * Creates a log of type warn in channel. - */ - public warn(...args: any[]): any | Promise { - return this.log('warn', ...args) - } - - /** - * Creates a log of type error in channel. - */ - public error(...args: any[]): any | Promise { - return this.log('error', ...args) - } - - /** - * Creates a log of type fatal in channel. - */ - public fatal(...args: any[]): any | Promise { - return this.log('fatal', ...args) - } -} diff --git a/src/index.ts b/src/index.ts index decb0e8..5eb6546 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,7 +11,6 @@ export * from './Facades/Log.js' export * from './Logger/Logger.js' export * from './Drivers/Driver.js' export * from './Formatters/Formatter.js' -export * from './Logger/VanillaLogger.js' export * from './Helpers/FactoryHelper.js' export * from './Factories/DriverFactory.js' export * from './Providers/LoggerProvider.js' diff --git a/tests/Stubs/config/logging.ts b/tests/Stubs/config/logging.ts index 7921888..0ad1fdf 100644 --- a/tests/Stubs/config/logging.ts +++ b/tests/Stubs/config/logging.ts @@ -1,5 +1,5 @@ import { Env } from '@athenna/config' -import { Path } from '@athenna/common' +import { Color, Path } from '@athenna/common' export default { /* @@ -51,6 +51,14 @@ export default { discard: { driver: 'null', }, + chalk: { + driver: 'console', + formatter: 'none', + formatterConfig: { + clean: false, + chalk: Color.dim, + }, + }, custom: { driver: 'custom', }, diff --git a/tests/Stubs/transporters/vanillaLogger.ts b/tests/Stubs/transporters/vanillaLogger.ts index 70e88fb..5ca662f 100644 --- a/tests/Stubs/transporters/vanillaLogger.ts +++ b/tests/Stubs/transporters/vanillaLogger.ts @@ -1,8 +1,7 @@ import { Logger } from '#src' -const vanillaLogger = Logger.getVanillaLogger() +const vanillaLogger = new Logger().standalone() -vanillaLogger.simple('({yellow,notFound,bold} hello) hello') vanillaLogger.trace('hello') vanillaLogger.debug('%s', 'hello') vanillaLogger.info('hello') diff --git a/tests/Unit/Formatters/NoneFormatterTest.ts b/tests/Unit/Formatters/NoneFormatterTest.ts index abdb395..c223593 100644 --- a/tests/Unit/Formatters/NoneFormatterTest.ts +++ b/tests/Unit/Formatters/NoneFormatterTest.ts @@ -9,6 +9,7 @@ import { test } from '@japa/runner' import { NoneFormatter } from '#src/Formatters/NoneFormatter' +import { Color } from '@athenna/common' test.group('NoneFormatterTest', () => { test('should be able to format logs to none format', async ({ assert }) => { @@ -18,4 +19,51 @@ test.group('NoneFormatterTest', () => { assert.equal(message, 'hello') }) + + test('should be able to apply chalk colors in message', async ({ assert }) => { + const formatter = new NoneFormatter().config({ level: 'info', clean: false, chalk: Color.gray }) + + const message = formatter.format('hello') + + assert.equal(message, Color.gray('hello')) + }) + + test('should return the cli level without any color if the level does not exist', async ({ assert }) => { + const formatter = new NoneFormatter().config({ level: 'not-found' }) + + const level = formatter.cliLevel() + + assert.equal(level, Color.bold('[ not-found ]')) + }) + + test('should return the simple level without any color if the level does not exist', async ({ assert }) => { + const formatter = new NoneFormatter().config({ level: 'not-found' }) + + const level = formatter.simpleLevel() + + assert.equal(level, Color.bold('[NOT-FOUND]')) + }) + + test('should return the custom emoji if it exists', async ({ assert }) => { + const formatter = new NoneFormatter().config({ level: 'info' }) + const emoji = formatter.getEmojiByLevel('info', '\u{1F43E}') + + assert.equal(emoji, '\u{1F43E}') + }) + + test('should return an empty string if the level does not exist in the dictionary', async ({ assert }) => { + const formatter = new NoneFormatter().config({ level: 'info' }) + const emoji = formatter.getEmojiByLevel('not-found') + + assert.equal(emoji, '') + }) + + test('should return the message without any color if the level does not exist in the dictionary', async ({ + assert, + }) => { + const formatter = new NoneFormatter().config({ level: 'info' }) + const message = formatter.paintMessageByLevel('not-found', 'hello') + + assert.equal(message, 'hello') + }) }) diff --git a/tests/Unit/Formatters/RequestFormatterTest.ts b/tests/Unit/Formatters/RequestFormatterTest.ts index ceb01cf..9b3c967 100644 --- a/tests/Unit/Formatters/RequestFormatterTest.ts +++ b/tests/Unit/Formatters/RequestFormatterTest.ts @@ -83,4 +83,12 @@ test.group('RequestFormatterTest', () => { assert.equal(message.metadata.path, ctx.request.baseUrl) assert.isDefined(message.metadata.createdAt) }) + + test('should be able to set a string as ctx and dont get errors', async ({ assert }) => { + const formatter = new RequestFormatter().config({ level: 'info', asJson: true }) + + const ctx = 'hello' + + assert.equal(formatter.format(ctx), 'hello') + }) }) diff --git a/tests/Unit/Logger/VanillaLoggerTest.ts b/tests/Unit/Logger/VanillaLoggerTest.ts index 90331a1..ed0a233 100644 --- a/tests/Unit/Logger/VanillaLoggerTest.ts +++ b/tests/Unit/Logger/VanillaLoggerTest.ts @@ -11,7 +11,7 @@ import { test } from '@japa/runner' import { Exec, Path } from '@athenna/common' test.group('VanillaLoggerTest', () => { - test('should be able to log in console using console logger', async ({ assert }) => { + test('should be able to log in console using vanilla logger', async ({ assert }) => { const { stdout, stderr } = await Exec.command(`ts-node --esm ${Path.stubs('transporters/vanillaLogger.ts')}`) const logs = [...stdout.split('\n').filter(l => l !== ''), ...stderr.split('\n').filter(l => l !== '')]