From 0c615da38043bac01d30a3480742746318a43114 Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Wed, 20 Mar 2024 10:01:06 -0400 Subject: [PATCH] feat: Add log base dir extras options Makes it easier to parse user logging options with a different, default, base dir for log files --- package-lock.json | 4 +- package.json | 2 +- src/loggers.ts | 8 ++-- src/types.ts | 14 +++++- tests/index.test.ts | 102 +++++++++++++++++++++++++------------------- 5 files changed, 79 insertions(+), 51 deletions(-) diff --git a/package-lock.json b/package-lock.json index 49dc3ab..a79a272 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@foxxmd/logging", - "version": "0.1.6", + "version": "0.1.8", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@foxxmd/logging", - "version": "0.1.6", + "version": "0.1.8", "license": "MIT", "dependencies": { "@foxxmd/pino-pretty": "^10.3.2", diff --git a/package.json b/package.json index f0967da..dab0963 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@foxxmd/logging", "type": "module", - "version": "0.1.7", + "version": "0.1.8", "repository": "https://github.com/foxxmd/logging", "description": "A typed, opinionated, batteries-included, Pino-based logging solution for backend TS/JS projects", "scripts": { diff --git a/src/loggers.ts b/src/loggers.ts index c2bd03e..41bbd0d 100644 --- a/src/loggers.ts +++ b/src/loggers.ts @@ -98,9 +98,10 @@ export const loggerApp = (config: LogOptions | object = {}, extras?: LoggerAppEx pretty = {}, destinations = [], pino, + logBaseDir } = extras || {}; - const options = parseLogOptions(config); + const options = parseLogOptions(config, logBaseDir); const streams: LogLevelStreamEntry[] = [ buildDestinationStdout(options.console, pretty), ...destinations @@ -133,10 +134,11 @@ export const loggerAppRolling = async (config: LogOptions | object = {}, extras? const { pretty = {}, destinations = [], - pino + pino, + logBaseDir } = extras || {}; - const options = parseLogOptions(config); + const options = parseLogOptions(config, logBaseDir); const streams: LogLevelStreamEntry[] = [ buildDestinationStdout(options.console, pretty), ...destinations diff --git a/src/types.ts b/src/types.ts index 4c10a9b..9475416 100644 --- a/src/types.ts +++ b/src/types.ts @@ -129,9 +129,10 @@ export interface FileOptions extends PinoRollOptions, RollOptions { * * May also be specified using env LOG_PATH or a function that returns a string. * - * If path is relative the absolute path will be derived from the current working directory. + * If path is relative the absolute path will be derived from `logBaseDir` (in `LoggerAppExtras`) which defaults to CWD * - * @default 'CWD/logs/app.log' + * @default './logs/app.log' + * @see LoggerAppExtras * */ path?: string | (() => string) } @@ -170,6 +171,15 @@ export interface LoggerAppExtras { * Additional [Pino Log options](https://getpino.io/#/docs/api?id=options) that are passed to `pino()` on logger creation * */ pino?: PinoLoggerOptions + + /** + * The base path to use when parsing file logging options. + * + * @see FileOptions + * + * @default 'CWD' + * */ + logBaseDir?: string } /** diff --git a/tests/index.test.ts b/tests/index.test.ts index 779d846..fdef798 100644 --- a/tests/index.test.ts +++ b/tests/index.test.ts @@ -44,8 +44,8 @@ const testConsoleLogger = (config?: object): [Logger, Transform, Transform] => { return [logger, testStream, rawStream]; } -const testFileRollingLogger = async (config?: object) => { - const opts = parseLogOptions(config, process.cwd()); +const testFileRollingLogger = async (config?: object, logBaseDir = process.cwd()) => { + const opts = parseLogOptions(config, logBaseDir); const { file: { level, @@ -68,8 +68,8 @@ const testFileRollingLogger = async (config?: object) => { ]); }; -const testFileLogger = async (config?: object) => { - const opts = parseLogOptions(config, process.cwd()); +const testFileLogger = async (config?: object, logBaseDir = process.cwd()) => { + const opts = parseLogOptions(config, logBaseDir); const { file: { path: logPath, @@ -91,7 +91,8 @@ const testFileLogger = async (config?: object) => { }; const testRollingAppLogger = async (config: LogOptions | object = {}, extras: LoggerAppExtras = {}): Promise<[Logger, Transform, Transform]> => { - const opts = parseLogOptions(config, process.cwd()); + const {destinations = [], pretty, logBaseDir = process.cwd(), ...restExtras} = extras; + const opts = parseLogOptions(config, logBaseDir); const testStream = new PassThrough(); const rawStream = new PassThrough(); const streams: LogLevelStreamEntry[] = [ @@ -108,16 +109,17 @@ const testRollingAppLogger = async (config: LogOptions | object = {}, extras: Lo stream: rawStream } ]; - const {destinations = [], pretty, ...restExtras} = extras; - const logger = await loggerAppRolling({...opts, console: 'silent'}, {destinations: [...destinations, ...streams], pretty, ...restExtras}); + const logger = await loggerAppRolling({...config, console: 'silent'}, {destinations: [...destinations, ...streams], pretty, logBaseDir, ...restExtras}); return [logger, testStream, rawStream]; } const testAppLogger = (config: LogOptions | object = {}, extras: LoggerAppExtras = {}): [Logger, Transform, Transform] => { - const opts = parseLogOptions(config, process.cwd()); + const {destinations = [], pretty = {}, logBaseDir = process.cwd(), ...restExtras} = extras; + + const opts = parseLogOptions(config, logBaseDir); const testStream = new PassThrough(); const rawStream = new PassThrough(); - const {destinations = [], pretty = {}, ...restExtras} = extras; + const streams = [ buildDestinationStream( @@ -134,8 +136,7 @@ const testAppLogger = (config: LogOptions | object = {}, extras: LoggerAppExtras stream: rawStream }, ]; - - const logger = loggerApp({...opts, console: 'silent'}, {destinations: [...destinations, ...streams], pretty, ...restExtras}); + const logger = loggerApp({...config, console: 'silent'}, {destinations: [...destinations, ...streams], pretty, logBaseDir, ...restExtras}); return [logger, testStream, rawStream]; } @@ -219,6 +220,38 @@ describe('Transports', function () { }); }); + describe('File', async function () { + + it('Does NOT write to file when file is false', async function () { + await withLocalTmpDir(async () => { + const logger = await testFileLogger({file: false}); + logger.debug('Test'); + await sleep(20); + expect(readdirSync('.').length).eq(0); + }, {unsafeCleanup: false}); + }); + + it('Writes to file when file level is valid', async function () { + await withLocalTmpDir(async () => { + const logger = await testFileLogger({file: 'debug'}); + logger.debug('Test'); + await sleep(20); + expect(readdirSync('./logs').length).eq(1); + }, {unsafeCleanup: true}); + }); + + it('Writes to specified file path', async function () { + await withLocalTmpDir(async () => { + const logger = await testFileLogger({file: { path: './myLogs.log' }}); + logger.debug('Test'); + await sleep(20); + const files = readdirSync('.'); + expect(files.length).eq(1); + expect(files[0]).eq('myLogs.log') + }, {unsafeCleanup: true}); + }); + }); + describe('Rolling File', async function () { it('Does NOT write to file when file is false', async function () { @@ -324,38 +357,6 @@ describe('Transports', function () { }); }); - describe('File', async function () { - - it('Does NOT write to file when file is false', async function () { - await withLocalTmpDir(async () => { - const logger = await testFileLogger({file: false}); - logger.debug('Test'); - await sleep(20); - expect(readdirSync('.').length).eq(0); - }, {unsafeCleanup: false}); - }); - - it('Writes to file when file level is valid', async function () { - await withLocalTmpDir(async () => { - const logger = await testFileLogger({file: 'debug'}); - logger.debug('Test'); - await sleep(20); - expect(readdirSync('./logs').length).eq(1); - }, {unsafeCleanup: true}); - }); - - it('Writes to specified file path', async function () { - await withLocalTmpDir(async () => { - const logger = await testFileLogger({file: { path: './myLogs.log' }}); - logger.debug('Test'); - await sleep(20); - const files = readdirSync('.'); - expect(files.length).eq(1); - expect(files[0]).eq('myLogs.log') - }, {unsafeCleanup: true}); - }); - }); - describe('Combined', function() { it('It writes to rolling file and console', async function () { await withLocalTmpDir(async () => { @@ -394,6 +395,21 @@ describe('Transports', function () { expect(fileContents.match(/DEBUG\s*:\s*Test/)).is.not.null; }, {unsafeCleanup: true}); }); + + it('It writes to file with a different base dir', async function () { + await withLocalTmpDir(async () => { + const [logger, testStream, rawStream] = testAppLogger({file: 'debug'}, {logBaseDir: path.resolve(process.cwd(), './config/logs')}); + const race = Promise.race([ + pEvent(testStream, 'data'), + sleep(10) + ]) as Promise; + logger.debug('Test'); + await sleep(20); + const res = await race; + const paths = readdirSync('./config/logs'); + expect(paths.length).eq(1); + }, {unsafeCleanup: true}); + }); }); }); -- 2.51.2