From aabc81216913ed77a8b0cfbdc6688932d22addb9 Mon Sep 17 00:00:00 2001 From: jlenon7 Date: Sat, 10 Feb 2024 18:33:39 +0000 Subject: [PATCH] feat(driver): add lambda driver --- package-lock.json | 54 ++++++++++++++------ package.json | 13 ++--- src/constants/VanillaChannels.ts | 5 ++ src/drivers/LambdaDriver.ts | 37 ++++++++++++++ src/factories/DriverFactory.ts | 2 + src/helpers/AwsLogger.ts | 18 +++++++ src/index.ts | 2 + src/types/Level.ts | 17 ++++++ src/types/index.ts | 10 ++++ tests/fixtures/config/logging.ts | 6 ++- tests/fixtures/transporters/consoleLambda.ts | 31 +++++++++++ tests/unit/drivers/LambdaDriverTest.ts | 27 ++++++++++ tests/unit/factories/DriverFactoryTest.ts | 2 +- 13 files changed, 200 insertions(+), 24 deletions(-) create mode 100644 src/drivers/LambdaDriver.ts create mode 100644 src/helpers/AwsLogger.ts create mode 100644 src/types/Level.ts create mode 100644 src/types/index.ts create mode 100644 tests/fixtures/transporters/consoleLambda.ts create mode 100644 tests/unit/drivers/LambdaDriverTest.ts diff --git a/package-lock.json b/package-lock.json index c4d6f96..daa43f8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,21 +1,22 @@ { "name": "@athenna/logger", - "version": "4.15.0", + "version": "4.16.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@athenna/logger", - "version": "4.15.0", + "version": "4.16.0", "license": "MIT", "dependencies": { + "@aws-lambda-powertools/logger": "^1.18.0", "cls-rtracer": "^2.6.3", "telegraf": "^4.15.3" }, "devDependencies": { - "@athenna/common": "^4.32.0", - "@athenna/config": "^4.14.0", - "@athenna/ioc": "^4.14.0", + "@athenna/common": "^4.33.0", + "@athenna/config": "^4.15.0", + "@athenna/ioc": "^4.15.0", "@athenna/test": "^4.21.0", "@athenna/tsconfig": "^4.12.0", "@typescript-eslint/eslint-plugin": "^6.7.4", @@ -92,9 +93,9 @@ "dev": true }, "node_modules/@athenna/common": { - "version": "4.32.0", - "resolved": "https://registry.npmjs.org/@athenna/common/-/common-4.32.0.tgz", - "integrity": "sha512-wfnkJVJjkHui5ifSGbWbcB7UiesBjsRNaxTy9xXJxR4yLtnkQoW51yr5rxwMtS4FmlgpbimBVI9+lcAdHzQj/Q==", + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/@athenna/common/-/common-4.33.0.tgz", + "integrity": "sha512-N/61/do/o6T6/N2p+O3D2DzF7Z+pTv2GKpBarmRmS4GxhkTKRqtvkJGJwXCy+b3XJGsXLvTOp6n1Uh4oCDEu2Q==", "dev": true, "dependencies": { "@fastify/formbody": "^7.4.0", @@ -147,9 +148,9 @@ } }, "node_modules/@athenna/config": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@athenna/config/-/config-4.14.0.tgz", - "integrity": "sha512-X1zjQmlE/lNG6xoB3zJy/vvb9x3XeVtXYF7l1dugoUT4+Y4k7dHmYgBPIuKBDqbR2FG8XR6pTzwwSNsBTjc9YA==", + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/@athenna/config/-/config-4.15.0.tgz", + "integrity": "sha512-nyyR76Ah5f1YUujn5YGKsPUxECQwmVvC3G5WjfUN1wjGGhXWreh0HhRZLel1gNLcAhnzh8V9PaJYskzEl5Sl3g==", "dev": true, "dependencies": { "dotenv": "^16.3.2", @@ -161,9 +162,9 @@ } }, "node_modules/@athenna/ioc": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@athenna/ioc/-/ioc-4.14.0.tgz", - "integrity": "sha512-EnCk+CrMSZkbUUPKHZwjd0nzCIAQ1oDzws94imehk1jo/e0khPXhFhKJQQiQALrrzCpgyRwArkJv/BC5OGksew==", + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/@athenna/ioc/-/ioc-4.15.0.tgz", + "integrity": "sha512-1j7LkgvPiTbEjhn1CynPrpF1oJfP7hk/dVkt6iZeuvxyr5QpfxK4HvaMkG2+a6RLSmQBQbcmHP0J357uDsocUQ==", "dev": true, "dependencies": { "awilix": "^7.0.3" @@ -203,6 +204,28 @@ "typescript": "^5.2.2" } }, + "node_modules/@aws-lambda-powertools/commons": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/@aws-lambda-powertools/commons/-/commons-1.18.0.tgz", + "integrity": "sha512-oSnST8Wr3WZcT/FgCUzZYUFB+qYHWMAKS0GhWbUqHZMr7I5F75jq/JbeUUF16ShOMGgnEzs5oJjizBYVTI6Oww==" + }, + "node_modules/@aws-lambda-powertools/logger": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/@aws-lambda-powertools/logger/-/logger-1.18.0.tgz", + "integrity": "sha512-oB4FPMYNPjME6xsDfm7rxRBHwaH0dQl+bmO9cDbRfiCsZfQxKtTiSvROGt6AvRo+5rhPZyCdn5eAHqCJ4f5tVQ==", + "dependencies": { + "@aws-lambda-powertools/commons": "^1.18.0", + "lodash.merge": "^4.6.2" + }, + "peerDependencies": { + "@middy/core": ">=3.x" + }, + "peerDependenciesMeta": { + "@middy/core": { + "optional": true + } + } + }, "node_modules/@babel/code-frame": { "version": "7.22.13", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", @@ -7157,8 +7180,7 @@ "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" }, "node_modules/lodash.mergewith": { "version": "4.6.2", diff --git a/package.json b/package.json index 7aebec3..034ac37 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@athenna/logger", - "version": "4.15.0", + "version": "4.16.0", "description": "The Athenna logging solution. Log in stdout, files and buckets.", "license": "MIT", "author": "João Lenon ", @@ -61,13 +61,14 @@ "#tests": "./tests/index.js" }, "dependencies": { - "telegraf": "^4.15.3", - "cls-rtracer": "^2.6.3" + "@aws-lambda-powertools/logger": "^1.18.0", + "cls-rtracer": "^2.6.3", + "telegraf": "^4.15.3" }, "devDependencies": { - "@athenna/common": "^4.32.0", - "@athenna/config": "^4.14.0", - "@athenna/ioc": "^4.14.0", + "@athenna/common": "^4.33.0", + "@athenna/config": "^4.15.0", + "@athenna/ioc": "^4.15.0", "@athenna/test": "^4.21.0", "@athenna/tsconfig": "^4.12.0", "@typescript-eslint/eslint-plugin": "^6.7.4", diff --git a/src/constants/VanillaChannels.ts b/src/constants/VanillaChannels.ts index 5f44c1c..72dea40 100644 --- a/src/constants/VanillaChannels.ts +++ b/src/constants/VanillaChannels.ts @@ -37,6 +37,11 @@ export const VANILLA_CHANNELS = { formatter: 'cli', driver: 'console' }, + lambda: { + level: 'trace', + formatter: 'json', + driver: 'lambda' + }, request: { level: 'trace', formatter: 'request', diff --git a/src/drivers/LambdaDriver.ts b/src/drivers/LambdaDriver.ts new file mode 100644 index 0000000..5047948 --- /dev/null +++ b/src/drivers/LambdaDriver.ts @@ -0,0 +1,37 @@ +/** + * @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 { debug } from '#src/debug' +import type { Level } from '#src/types' +import { Driver } from '#src/drivers/Driver' +import { logger } from '#src/helpers/AwsLogger' + +export class LambdaDriver extends Driver { + public transport(level: Level, message: any): any { + if (!this.couldBeTransported(level)) { + return + } + + const levelMap = { + info: 'info', + warn: 'warn', + trace: 'debug', + debug: 'debug', + error: 'error', + success: 'info', + fatal: 'critical' + } + + const formatted = this.format(levelMap[level], message) + + debug('[%s] Transporting logs using AWS Powertools.', LambdaDriver.name) + + return logger[levelMap[level]](formatted) + } +} diff --git a/src/factories/DriverFactory.ts b/src/factories/DriverFactory.ts index f272d7e..60c7e41 100644 --- a/src/factories/DriverFactory.ts +++ b/src/factories/DriverFactory.ts @@ -14,6 +14,7 @@ import { FileDriver } from '#src/drivers/FileDriver' import { NullDriver } from '#src/drivers/NullDriver' import { SlackDriver } from '#src/drivers/SlackDriver' import { StackDriver } from '#src/drivers/StackDriver' +import { LambdaDriver } from '#src/drivers/LambdaDriver' import { ConsoleDriver } from '#src/drivers/ConsoleDriver' import { DiscordDriver } from '#src/drivers/DiscordDriver' import { FactoryHelper } from '#src/helpers/FactoryHelper' @@ -31,6 +32,7 @@ export class DriverFactory { .set('null', { Driver: NullDriver }) .set('slack', { Driver: SlackDriver }) .set('stack', { Driver: StackDriver }) + .set('lambda', { Driver: LambdaDriver }) .set('console', { Driver: ConsoleDriver }) .set('discord', { Driver: DiscordDriver }) .set('telegram', { Driver: TelegramDriver }) diff --git a/src/helpers/AwsLogger.ts b/src/helpers/AwsLogger.ts new file mode 100644 index 0000000..bdba06f --- /dev/null +++ b/src/helpers/AwsLogger.ts @@ -0,0 +1,18 @@ +/** + * @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 { Logger } from '@aws-lambda-powertools/logger' + +/** + * Expose the AWS logger to be able to make changes on it. + * By default the log level is set to `debug` because the + * responsible of deciding if the message should be logged + * or not is from Athenna. + */ +export const logger = new Logger({ logLevel: 'debug' }) diff --git a/src/index.ts b/src/index.ts index b418e11..71d1158 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,9 +7,11 @@ * file that was distributed with this source code. */ +export * from '#src/types' export * from '#src/facades/Log' export * from '#src/logger/Logger' export * from '#src/drivers/Driver' +export * from '#src/helpers/AwsLogger' export * from '#src/formatters/Formatter' export * from '#src/helpers/FactoryHelper' export * from '#src/factories/DriverFactory' diff --git a/src/types/Level.ts b/src/types/Level.ts new file mode 100644 index 0000000..1117184 --- /dev/null +++ b/src/types/Level.ts @@ -0,0 +1,17 @@ +/** + * @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. + */ + +export type Level = + | 'trace' + | 'debug' + | 'info' + | 'success' + | 'warn' + | 'error' + | 'fatal' diff --git a/src/types/index.ts b/src/types/index.ts new file mode 100644 index 0000000..f91bab9 --- /dev/null +++ b/src/types/index.ts @@ -0,0 +1,10 @@ +/** + * @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. + */ + +export * from '#src/types/Level' diff --git a/tests/fixtures/config/logging.ts b/tests/fixtures/config/logging.ts index 0436084..2dab208 100644 --- a/tests/fixtures/config/logging.ts +++ b/tests/fixtures/config/logging.ts @@ -32,7 +32,7 @@ export default { | Here you may configure the log channels for your application. | | Available Drivers: - | "console", "discord", "file", "null", "slack", "telegram". + | "console", "discord", "file", "null", "slack", "telegram", "lambda". | Available Formatters: | "cli", "simple", "json", "request", "message", "none". | @@ -48,6 +48,10 @@ export default { formatter: 'simple', level: 'debug' }, + lambda: { + driver: 'lambda', + formatter: 'json' + }, request: { driver: 'console', formatter: 'request', diff --git a/tests/fixtures/transporters/consoleLambda.ts b/tests/fixtures/transporters/consoleLambda.ts new file mode 100644 index 0000000..61bba36 --- /dev/null +++ b/tests/fixtures/transporters/consoleLambda.ts @@ -0,0 +1,31 @@ +/** + * @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 { Config } from '@athenna/config' +import { Log, LoggerProvider } from '#src' +import { Folder, Path } from '@athenna/common' + +await new Folder(Path.fixtures('config')).copy(Path.config()) +await Config.safeLoad(Path.config('logging.ts')) + +new LoggerProvider().register() + +const simpleLogger = Log.channel('discard', 'lambda') +const noneLogger = Log.config({ formatter: 'none' }).channel('discard', 'lambda') + +simpleLogger.trace('hello') +simpleLogger.debug('%s', 'hello') +simpleLogger.info('hello') +noneLogger.success('%s', 'hello') +noneLogger.warn('({yellow,notFound,bold} hello) hello') +noneLogger.error('%s', 'hello') +noneLogger.fatal('hello') + +await Folder.safeRemove(Path.config()) +await Folder.safeRemove(Path.storage()) diff --git a/tests/unit/drivers/LambdaDriverTest.ts b/tests/unit/drivers/LambdaDriverTest.ts new file mode 100644 index 0000000..73188c5 --- /dev/null +++ b/tests/unit/drivers/LambdaDriverTest.ts @@ -0,0 +1,27 @@ +/** + * @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 { Path, Exec } from '@athenna/common' +import { Test, type Context } from '@athenna/test' + +export default class LambdaDriverTest { + @Test() + public async shouldBeAbleToLogInConsoleWithLambdaLib({ assert }: Context) { + const { stdout, stderr } = await Exec.node(Path.fixtures('transporters/consoleLambda.ts')) + + const logs = [...stdout.split('\n').filter(l => l !== ''), ...stderr.split('\n').filter(l => l !== '')].filter( + v => !v.startsWith('(') + ) + + logs.forEach(log => { + assert.isFalse(log.includes('DEBUG')) + assert.isTrue(log.includes('hello')) + }) + } +} diff --git a/tests/unit/factories/DriverFactoryTest.ts b/tests/unit/factories/DriverFactoryTest.ts index 01eb3cd..cfc8a4a 100644 --- a/tests/unit/factories/DriverFactoryTest.ts +++ b/tests/unit/factories/DriverFactoryTest.ts @@ -38,7 +38,7 @@ export default class DriverFactoryTest { public async shouldBeAbleToListAllAvailableDrivers({ assert }: Context) { const drivers = DriverFactory.availableDrivers() - assert.deepEqual(drivers, ['file', 'null', 'slack', 'stack', 'console', 'discord', 'telegram']) + assert.deepEqual(drivers, ['file', 'null', 'slack', 'stack', 'lambda', 'console', 'discord', 'telegram']) } @Test()