Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(common): Support JSON format in ConsoleLogger #13561

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 35 additions & 3 deletions packages/common/services/console-logger.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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[] = [
Expand Down Expand Up @@ -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);
}
}

/**
Expand Down Expand Up @@ -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);
Expand All @@ -216,6 +225,7 @@ export class ConsoleLogger implements LoggerService {
formattedLogLevel,
contextMessage,
timestampDiff,
stack,
);

process[writeStreamType ?? 'stdout'].write(formattedMessage);
Expand All @@ -227,6 +237,10 @@ export class ConsoleLogger implements LoggerService {
}

protected formatContext(context: string): string {
if (this.options?.asJSON) {
return context;
}

return context ? yellow(`[${context}] `) : '';
}

Expand All @@ -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`;
Expand Down Expand Up @@ -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);
}
Expand Down
54 changes: 54 additions & 0 deletions packages/common/test/services/logger.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down