From 14ef5e4720744f94f59df9ae423fe4342a2b028c Mon Sep 17 00:00:00 2001 From: nesro Date: Sat, 11 May 2024 22:44:34 +0200 Subject: [PATCH] feat(common): Support JSON format in ConsoleLogger --- .../common/services/console-logger.service.ts | 38 +++++++++++-- .../test/services/logger.service.spec.ts | 54 +++++++++++++++++++ 2 files changed, 89 insertions(+), 3 deletions(-) diff --git a/packages/common/services/console-logger.service.ts b/packages/common/services/console-logger.service.ts index a6da2a41986..cd4587eb0ee 100644 --- a/packages/common/services/console-logger.service.ts +++ b/packages/common/services/console-logger.service.ts @@ -6,7 +6,7 @@ import { isString, isUndefined, } from '../utils/shared.utils'; -import { LoggerService, LogLevel } from './logger.service'; +import { LogLevel, LoggerService } from './logger.service'; import { isLogLevelEnabled } from './utils'; export interface ConsoleLoggerOptions { @@ -18,6 +18,11 @@ export interface ConsoleLoggerOptions { * If enabled, will print timestamp (time difference) between current and previous log message. */ timestamp?: boolean; + + /** + * If enabled, logs will be in form of JSON strings. + */ + asJSON?: boolean; } const DEFAULT_LOG_LEVELS: LogLevel[] = [ @@ -91,8 +96,11 @@ export class ConsoleLogger implements LoggerService { const { messages, context, stack } = this.getContextAndStackAndMessagesToPrint([message, ...optionalParams]); - this.printMessages(messages, context, 'error', 'stderr'); - this.printStackTrace(stack); + this.printMessages(messages, context, 'error', 'stderr', stack); + + if (!this.options?.asJSON) { + this.printStackTrace(stack); + } } /** @@ -203,6 +211,7 @@ export class ConsoleLogger implements LoggerService { context = '', logLevel: LogLevel = 'log', writeStreamType?: 'stdout' | 'stderr', + stack?: string, ) { messages.forEach(message => { const pidMessage = this.formatPid(process.pid); @@ -216,6 +225,7 @@ export class ConsoleLogger implements LoggerService { formattedLogLevel, contextMessage, timestampDiff, + stack, ); process[writeStreamType ?? 'stdout'].write(formattedMessage); @@ -227,6 +237,10 @@ export class ConsoleLogger implements LoggerService { } protected formatContext(context: string): string { + if (this.options?.asJSON) { + return context; + } + return context ? yellow(`[${context}] `) : ''; } @@ -237,8 +251,22 @@ export class ConsoleLogger implements LoggerService { formattedLogLevel: string, contextMessage: string, timestampDiff: string, + stack?: string, ) { const output = this.stringifyMessage(message, logLevel); + + if (this.options?.asJSON) { + return `${JSON.stringify({ + pid: process.pid, + timestamp: Date.now(), + logLevel, + context: contextMessage, + message, + ...(timestampDiff !== '' && { timestampDiff }), + ...(stack && { stack }), + })}\n`; + } + pidMessage = this.colorize(pidMessage, logLevel); formattedLogLevel = this.colorize(formattedLogLevel, logLevel); return `${pidMessage}${this.getTimestamp()} ${formattedLogLevel} ${contextMessage}${output}${timestampDiff}\n`; @@ -267,6 +295,10 @@ export class ConsoleLogger implements LoggerService { } protected colorize(message: string, logLevel: LogLevel) { + if (this.options?.asJSON) { + return message; + } + const color = this.getColorByLogLevel(logLevel); return color(message); } diff --git a/packages/common/test/services/logger.service.spec.ts b/packages/common/test/services/logger.service.spec.ts index e3a61464d06..e57a9b2b6b2 100644 --- a/packages/common/test/services/logger.service.spec.ts +++ b/packages/common/test/services/logger.service.spec.ts @@ -539,6 +539,60 @@ describe('Logger', () => { }); }); + describe('when the default logger is used and global context is set and asJSON enabled', () => { + const globalContext = 'GlobalContext'; + + const logger = new ConsoleLogger(globalContext, { asJSON: true }); + + let processStdoutWriteSpy: sinon.SinonSpy; + let processStderrWriteSpy: sinon.SinonSpy; + + beforeEach(() => { + processStdoutWriteSpy = sinon.spy(process.stdout, 'write'); + processStderrWriteSpy = sinon.spy(process.stderr, 'write'); + }); + + afterEach(() => { + processStdoutWriteSpy.restore(); + processStderrWriteSpy.restore(); + }); + + it('should print error with stack as JSON to the console', () => { + const errorMessage = 'error message'; + const error = new Error(errorMessage); + + logger.error(error.message, error.stack); + + const json = JSON.parse(processStderrWriteSpy.firstCall?.firstArg); + + expect(json.logLevel).to.equal('error'); + expect(json.context).to.equal(globalContext); + expect(json.message).to.equal(errorMessage); + }); + it('should log out to stdout as JSON', () => { + const message = 'message 1'; + + logger.log(message); + + const json = JSON.parse(processStdoutWriteSpy.firstCall?.firstArg); + + expect(json.logLevel).to.equal('log'); + expect(json.context).to.equal(globalContext); + expect(json.message).to.equal(message); + }); + it('should log out an error to stderr as JSON', () => { + const message = 'message 1'; + + logger.error(message); + + const json = JSON.parse(processStderrWriteSpy.firstCall?.firstArg); + + expect(json.logLevel).to.equal('error'); + expect(json.context).to.equal(globalContext); + expect(json.message).to.equal(message); + }); + }); + describe('when logging is disabled', () => { const logger = new Logger();