Compare commits

..

8 Commits

Author SHA1 Message Date
Kamil Myśliwiec
47941ba4d9 test: update outdated unit test 2024-11-18 09:56:13 +01:00
Kamil Myśliwiec
9129e4f858 refactor: remove redundant conditions 2024-11-18 09:10:34 +01:00
Kamil Myśliwiec
f37dafc132 chore: pass error instance to logger error method 2024-11-18 09:08:47 +01:00
Kamil Myśliwiec
66c4810b0f style: fix linter issues 2024-11-08 14:29:42 +01:00
Kamil Myśliwiec
23e602b712 style: fix linter issues 2024-11-08 14:09:58 +01:00
Kamil Myśliwiec
e767bd364e feat: produce a parseable json when colors off and compact on 2024-11-08 13:49:42 +01:00
Kamil Myśliwiec
03d8bcc21c feat: add pid to log object 2024-11-08 11:32:08 +01:00
Kamil Myśliwiec
e75eb1d14a feat(common): json logger and a few other improvements 2024-11-08 11:21:37 +01:00
12 changed files with 431 additions and 100 deletions

View File

@@ -1,9 +1,9 @@
import { Readable } from 'stream';
import { types } from 'util';
import { HttpStatus } from '../enums';
import { Logger } from '../services';
import { isFunction } from '../utils/shared.utils';
import { StreamableFileOptions, StreamableHandlerResponse } from './interfaces';
import { Logger } from '../services';
/**
* @see [Streaming files](https://docs.nestjs.com/techniques/streaming-files)
@@ -31,7 +31,7 @@ export class StreamableFile {
};
protected logError: (err: Error) => void = (err: Error) => {
this.logger.error(err.message, err.stack);
this.logger.error(err);
};
constructor(buffer: Uint8Array, options?: StreamableFileOptions);

View File

@@ -1,3 +1,4 @@
import { inspect, InspectOptions } from 'util';
import { Injectable, Optional } from '../decorators/core';
import { clc, yellow } from '../utils/cli-colors.util';
import {
@@ -9,6 +10,8 @@ import {
import { LoggerService, LogLevel } from './logger.service';
import { isLogLevelEnabled } from './utils';
const DEFAULT_DEPTH = 5;
export interface ConsoleLoggerOptions {
/**
* Enabled log levels.
@@ -16,8 +19,73 @@ export interface ConsoleLoggerOptions {
logLevels?: LogLevel[];
/**
* If enabled, will print timestamp (time difference) between current and previous log message.
* Note: This option is not used when `json` is enabled.
*/
timestamp?: boolean;
/**
* A prefix to be used for each log message.
* Note: This option is not used when `json` is enabled.
*/
prefix?: string;
/**
* If enabled, will print the log message in JSON format.
*/
json?: boolean;
/**
* If enabled, will print the log message in color.
* Default true if json is disabled, false otherwise
*/
colors?: boolean;
/**
* The context of the logger.
*/
context?: string;
/**
* If enabled, will print the log message in a single line, even if it is an object with multiple properties.
* If set to a number, the most n inner elements are united on a single line as long as all properties fit into breakLength. Short array elements are also grouped together.
* Default true when `json` is enabled, false otherwise.
*/
compact?: boolean | number;
/**
* Specifies the maximum number of Array, TypedArray, Map, Set, WeakMap, and WeakSet elements to include when formatting.
* Set to null or Infinity to show all elements. Set to 0 or negative to show no elements.
* Ignored when `json` is enabled, colors are disabled, and `compact` is set to true as it produces a parseable JSON output.
* @default 100
*/
maxArrayLength?: number;
/**
* Specifies the maximum number of characters to include when formatting.
* Set to null or Infinity to show all elements. Set to 0 or negative to show no characters.
* Ignored when `json` is enabled, colors are disabled, and `compact` is set to true as it produces a parseable JSON output.
* @default 10000.
*/
maxStringLength?: number;
/**
* If enabled, will sort keys while formatting objects.
* Can also be a custom sorting function.
* Ignored when `json` is enabled, colors are disabled, and `compact` is set to true as it produces a parseable JSON output.
* @default false
*/
sorted?: boolean | ((a: string, b: string) => number);
/**
* Specifies the number of times to recurse while formatting object. T
* This is useful for inspecting large objects. To recurse up to the maximum call stack size pass Infinity or null.
* Ignored when `json` is enabled, colors are disabled, and `compact` is set to true as it produces a parseable JSON output.
* @default 5
*/
depth?: number;
/**
* If true, object's non-enumerable symbols and properties are included in the formatted result.
* WeakMap and WeakSet entries are also included as well as user defined prototype properties
* @default false
*/
showHidden?: boolean;
/**
* The length at which input values are split across multiple lines. Set to Infinity to format the input as a single line (in combination with "compact" set to true).
* Default Infinity when "compact" is true, 80 otherwise.
* Ignored when `json` is enabled, colors are disabled, and `compact` is set to true as it produces a parseable JSON output.
*/
breakLength?: number;
}
const DEFAULT_LOG_LEVELS: LogLevel[] = [
@@ -40,22 +108,54 @@ const dateTimeFormatter = new Intl.DateTimeFormat(undefined, {
@Injectable()
export class ConsoleLogger implements LoggerService {
private static lastTimestampAt?: number;
private originalContext?: string;
/**
* The options of the logger.
*/
protected options: ConsoleLoggerOptions;
/**
* The context of the logger (can be set manually or automatically inferred).
*/
protected context?: string;
/**
* The original context of the logger (set in the constructor).
*/
protected originalContext?: string;
/**
* The options used for the "inspect" method.
*/
protected inspectOptions: InspectOptions;
/**
* The last timestamp at which the log message was printed.
*/
protected static lastTimestampAt?: number;
constructor();
constructor(context: string);
constructor(options: ConsoleLoggerOptions);
constructor(context: string, options: ConsoleLoggerOptions);
constructor(
@Optional()
protected context?: string,
contextOrOptions?: string | ConsoleLoggerOptions,
@Optional()
protected options: ConsoleLoggerOptions = {},
options?: ConsoleLoggerOptions,
) {
if (!options.logLevels) {
options.logLevels = DEFAULT_LOG_LEVELS;
}
// eslint-disable-next-line prefer-const
let [context, opts] = isString(contextOrOptions)
? [contextOrOptions, options]
: !!options
? [undefined, options]
: [contextOrOptions?.context, contextOrOptions];
opts = opts ?? {};
opts.logLevels ??= DEFAULT_LOG_LEVELS;
opts.colors ??= opts.colors ?? (opts.json ? false : true);
opts.prefix ??= 'Nest';
this.options = opts;
this.inspectOptions = this.getInspectOptions();
if (context) {
this.context = context;
this.originalContext = context;
}
}
@@ -91,7 +191,7 @@ export class ConsoleLogger implements LoggerService {
const { messages, context, stack } =
this.getContextAndStackAndMessagesToPrint([message, ...optionalParams]);
this.printMessages(messages, context, 'error', 'stderr');
this.printMessages(messages, context, 'error', 'stderr', stack);
this.printStackTrace(stack);
}
@@ -203,8 +303,18 @@ export class ConsoleLogger implements LoggerService {
context = '',
logLevel: LogLevel = 'log',
writeStreamType?: 'stdout' | 'stderr',
errorStack?: unknown,
) {
messages.forEach(message => {
if (this.options.json) {
this.printAsJson(message, {
context,
logLevel,
writeStreamType,
errorStack,
});
return;
}
const pidMessage = this.formatPid(process.pid);
const contextMessage = this.formatContext(context);
const timestampDiff = this.updateAndGetTimestampDiff();
@@ -222,12 +332,57 @@ export class ConsoleLogger implements LoggerService {
});
}
protected printAsJson(
message: unknown,
options: {
context: string;
logLevel: LogLevel;
writeStreamType?: 'stdout' | 'stderr';
errorStack?: unknown;
},
) {
type JsonLogObject = {
level: LogLevel;
pid: number;
timestamp: number;
message: unknown;
context?: string;
stack?: unknown;
};
const logObject: JsonLogObject = {
level: options.logLevel,
pid: process.pid,
timestamp: Date.now(),
message,
};
if (options.context) {
logObject.context = options.context;
}
if (options.errorStack) {
logObject.stack = options.errorStack;
}
const formattedMessage =
!this.options.colors && this.inspectOptions.compact === true
? JSON.stringify(logObject, this.stringifyReplacer)
: inspect(logObject, this.inspectOptions);
process[options.writeStreamType ?? 'stdout'].write(`${formattedMessage}\n`);
}
protected formatPid(pid: number) {
return `[Nest] ${pid} - `;
return `[${this.options.prefix}] ${pid} - `;
}
protected formatContext(context: string): string {
return context ? yellow(`[${context}] `) : '';
if (!context) {
return '';
}
context = `[${context}] `;
return this.options.colors ? yellow(context) : context;
}
protected formatMessage(
@@ -256,23 +411,30 @@ export class ConsoleLogger implements LoggerService {
return this.stringifyMessage(message(), logLevel);
}
return isPlainObject(message) || Array.isArray(message)
? `${this.colorize('Object:', logLevel)}\n${JSON.stringify(
message,
(key, value) =>
typeof value === 'bigint' ? value.toString() : value,
2,
)}\n`
: this.colorize(message as string, logLevel);
if (typeof message === 'string') {
return this.colorize(message, logLevel);
}
const outputText = inspect(message, this.inspectOptions);
if (isPlainObject(message)) {
return `Object(${Object.keys(message).length}) ${outputText}`;
}
if (Array.isArray(message)) {
return `Array(${message.length}) ${outputText}`;
}
return outputText;
}
protected colorize(message: string, logLevel: LogLevel) {
if (!this.options.colors || this.options.json) {
return message;
}
const color = this.getColorByLogLevel(logLevel);
return color(message);
}
protected printStackTrace(stack: string) {
if (!stack) {
if (!stack || this.options.json) {
return;
}
process.stderr.write(`${stack}\n`);
@@ -289,7 +451,58 @@ export class ConsoleLogger implements LoggerService {
}
protected formatTimestampDiff(timestampDiff: number) {
return yellow(` +${timestampDiff}ms`);
const formattedDiff = ` +${timestampDiff}ms`;
return this.options.colors ? yellow(formattedDiff) : formattedDiff;
}
protected getInspectOptions() {
let breakLength = this.options.breakLength;
if (typeof breakLength === 'undefined') {
breakLength = this.options.colors
? this.options.compact
? Infinity
: undefined
: this.options.compact === false
? undefined
: Infinity; // default breakLength to Infinity if inline is not set and colors is false
}
const inspectOptions: InspectOptions = {
depth: this.options.depth ?? DEFAULT_DEPTH,
sorted: this.options.sorted,
showHidden: this.options.showHidden,
compact: this.options.compact ?? (this.options.json ? true : false),
colors: this.options.colors,
breakLength,
};
if (this.options.maxArrayLength) {
inspectOptions.maxArrayLength = this.options.maxArrayLength;
}
if (this.options.maxStringLength) {
inspectOptions.maxStringLength = this.options.maxStringLength;
}
return inspectOptions;
}
protected stringifyReplacer(key: string, value: unknown) {
// Mimic util.inspect behavior for JSON logger with compact on and colors off
if (typeof value === 'bigint') {
return value.toString();
}
if (typeof value === 'symbol') {
return value.toString();
}
if (
value instanceof Map ||
value instanceof Set ||
value instanceof Error
) {
return `${inspect(value, this.inspectOptions)}`;
}
return value;
}
private getContextAndMessagesToPrint(args: unknown[]) {

View File

@@ -125,9 +125,12 @@ describe('Logger', () => {
Logger.error(error);
expect(processStderrWriteSpy.calledOnce).to.be.true;
expect(processStderrWriteSpy.firstCall.firstArg).to.include(`Object:`);
expect(processStderrWriteSpy.firstCall.firstArg).to.include(
`{\n "randomError": true\n}`,
`Object(${Object.keys(error).length})`,
);
expect(processStderrWriteSpy.firstCall.firstArg).to.include(
`randomError: \x1b[33mtrue`,
);
});
@@ -181,6 +184,153 @@ describe('Logger', () => {
expect(processStderrWriteSpy.thirdCall.firstArg).to.equal(stack + '\n');
});
});
describe('when the default logger is used and json mode is enabled', () => {
const logger = new ConsoleLogger({ json: 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.pid).to.equal(process.pid);
expect(json.level).to.equal('error');
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.pid).to.equal(process.pid);
expect(json.level).to.equal('log');
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.pid).to.equal(process.pid);
expect(json.level).to.equal('error');
expect(json.message).to.equal(message);
});
it('should log Map object', () => {
const map = new Map([
['key1', 'value1'],
['key2', 'value2'],
]);
logger.log(map);
const json = JSON.parse(processStdoutWriteSpy.firstCall?.firstArg);
expect(json.pid).to.equal(process.pid);
expect(json.level).to.equal('log');
expect(json.message).to.equal(
`Map(2) { 'key1' => 'value1', 'key2' => 'value2' }`,
);
});
it('should log Set object', () => {
const set = new Set(['value1', 'value2']);
logger.log(set);
const json = JSON.parse(processStdoutWriteSpy.firstCall?.firstArg);
expect(json.pid).to.equal(process.pid);
expect(json.level).to.equal('log');
expect(json.message).to.equal(`Set(2) { 'value1', 'value2' }`);
});
it('should log bigint', () => {
const bigInt = BigInt(9007199254740991);
logger.log(bigInt);
const json = JSON.parse(processStdoutWriteSpy.firstCall?.firstArg);
expect(json.pid).to.equal(process.pid);
expect(json.level).to.equal('log');
expect(json.message).to.equal('9007199254740991');
});
it('should log symbol', () => {
const symbol = Symbol('test');
logger.log(symbol);
const json = JSON.parse(processStdoutWriteSpy.firstCall?.firstArg);
expect(json.pid).to.equal(process.pid);
expect(json.level).to.equal('log');
expect(json.message).to.equal('Symbol(test)');
});
});
describe('when the default logger is used, json mode is enabled and compact is false (utils.inspect)', () => {
const logger = new ConsoleLogger({ json: true, compact: false });
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 log out to stdout as JSON (utils.inspect)', () => {
const message = 'message 1';
logger.log(message);
const json = convertInspectToJSON(
processStdoutWriteSpy.firstCall?.firstArg,
);
expect(json.pid).to.equal(process.pid);
expect(json.level).to.equal('log');
expect(json.message).to.equal(message);
});
it('should log out an error to stderr as JSON (utils.inspect)', () => {
const message = 'message 1';
logger.error(message);
const json = convertInspectToJSON(
processStderrWriteSpy.firstCall?.firstArg,
);
expect(json.pid).to.equal(process.pid);
expect(json.level).to.equal('error');
expect(json.message).to.equal(message);
});
});
describe('when logging is disabled', () => {
let processStdoutWriteSpy: sinon.SinonSpy;
let previousLoggerRef: LoggerService;
@@ -568,6 +718,7 @@ describe('Logger', () => {
expect(processStdoutWriteSpy.called).to.be.false;
});
});
describe('when custom logger is being used', () => {
class CustomLogger implements LoggerService {
log(message: any, context?: string) {}
@@ -723,7 +874,7 @@ describe('Logger', () => {
}
}
const consoleLogger = new CustomConsoleLogger();
const consoleLogger = new CustomConsoleLogger({ colors: false });
const consoleLoggerSpy = sinon.spy(
consoleLogger,
'stringifyMessage' as keyof ConsoleLogger,
@@ -739,30 +890,40 @@ describe('Logger', () => {
expect(consoleLoggerSpy.getCall(0).returnValue).to.equal('str1');
expect(consoleLoggerSpy.getCall(1).returnValue).to.equal(
`Object:
{
"key": "str2"
}
`,
`Object(1) {
key: 'str2'
}`,
);
expect(consoleLoggerSpy.getCall(2).returnValue).to.equal(
`Object:
[
"str3"
]
`,
`Array(1) [
'str3'
]`,
);
expect(consoleLoggerSpy.getCall(3).returnValue).to.equal(
`Object:
[
`Array(1) [
{
"key": "str4"
key: 'str4'
}
]
`,
]`,
);
expect(consoleLoggerSpy.getCall(4).returnValue).to.equal(null);
expect(consoleLoggerSpy.getCall(5).returnValue).to.equal(1);
expect(consoleLoggerSpy.getCall(4).returnValue).to.equal('null');
expect(consoleLoggerSpy.getCall(5).returnValue).to.equal('1');
});
});
});
function convertInspectToJSON(inspectOutput: string) {
const jsonLikeString = inspectOutput
.replace(/'([^']+)'/g, '"$1"') // single-quoted strings
.replace(/([a-zA-Z0-9_]+):/g, '"$1":') // unquoted object keys
.replace(/\bundefined\b/g, 'null')
.replace(/\[Function(: [^\]]+)?\]/g, '"[Function]"')
.replace(/\[Circular\]/g, '"[Circular]"');
try {
return JSON.parse(jsonLikeString);
} catch (error) {
console.error('Error parsing the modified inspect output:', error);
throw error;
}
}

View File

@@ -1,14 +1,9 @@
import { RuntimeException } from './exceptions/runtime.exception';
import { Logger } from '@nestjs/common/services/logger.service';
export class ExceptionHandler {
private static readonly logger = new Logger(ExceptionHandler.name);
public handle(exception: RuntimeException | Error) {
if (!(exception instanceof RuntimeException)) {
ExceptionHandler.logger.error(exception.message, exception.stack);
return;
}
ExceptionHandler.logger.error(exception.what(), exception.stack);
public handle(exception: Error) {
ExceptionHandler.logger.error(exception);
}
}

View File

@@ -68,12 +68,6 @@ export class BaseExceptionFilter<T = any> implements ExceptionFilter<T> {
applicationRef.end(response);
}
if (this.isExceptionObject(exception)) {
return BaseExceptionFilter.logger.error(
exception.message,
exception.stack,
);
}
return BaseExceptionFilter.logger.error(exception);
}

View File

@@ -5,7 +5,7 @@ export class ExternalExceptionFilter<T = any, R = any> {
catch(exception: T, host: ArgumentsHost): R | Promise<R> {
if (exception instanceof Error && !(exception instanceof HttpException)) {
ExternalExceptionFilter.logger.error(exception.message, exception.stack);
ExternalExceptionFilter.logger.error(exception);
}
throw exception;
}

View File

@@ -1,8 +1,7 @@
import * as sinon from 'sinon';
import { expect } from 'chai';
import * as sinon from 'sinon';
import { ExceptionHandler } from '../../../errors/exception-handler';
import { RuntimeException } from '../../../errors/exceptions/runtime.exception';
import { InvalidMiddlewareException } from '../../../errors/exceptions/invalid-middleware.exception';
describe('ExceptionHandler', () => {
let instance: ExceptionHandler;
@@ -10,7 +9,7 @@ describe('ExceptionHandler', () => {
instance = new ExceptionHandler();
});
describe('handle', () => {
let logger;
let logger: { error: Function };
let errorSpy: sinon.SinonSpy;
beforeEach(() => {
logger = {
@@ -19,16 +18,10 @@ describe('ExceptionHandler', () => {
(ExceptionHandler as any).logger = logger;
errorSpy = sinon.spy(logger, 'error');
});
it('when exception is instanceof RuntimeException', () => {
it('should call the logger.error method with the thrown exception passed as an argument', () => {
const exception = new RuntimeException('msg');
instance.handle(exception);
expect(errorSpy.calledWith(exception.message, exception.stack)).to.be
.true;
});
it('when exception is not instanceof RuntimeException', () => {
const exception = new InvalidMiddlewareException('msg');
instance.handle(exception);
expect(errorSpy.calledWith(exception.what(), exception.stack)).to.be.true;
expect(errorSpy.calledWith(exception)).to.be.true;
});
});
});

View File

@@ -26,11 +26,8 @@ export class BaseRpcExceptionFilter<T = any, R = any>
public handleUnknownError(exception: T, status: string) {
const errorMessage = MESSAGES.UNKNOWN_EXCEPTION_MESSAGE;
const loggerArgs = this.isError(exception)
? [exception.message, exception.stack]
: [exception];
const logger = BaseRpcExceptionFilter.logger;
logger.error.apply(logger, loggerArgs as any);
logger.error.apply(logger, [exception]);
return _throw(() => ({ status, message: errorMessage }));
}

View File

@@ -39,36 +39,25 @@ export class ListenerMetadataExplorer {
const instancePrototype = Object.getPrototypeOf(instance);
return this.metadataScanner
.getAllMethodNames(instancePrototype)
.map(method =>
this.exploreMethodMetadata(instance, instancePrototype, method),
)
.map(method => this.exploreMethodMetadata(instancePrototype, method))
.filter(metadata => metadata);
}
public exploreMethodMetadata(
instance: Controller,
instancePrototype: object,
methodKey: string,
): EventOrMessageListenerDefinition {
const prototypeCallback = instancePrototype[methodKey];
const targetCallback = instancePrototype[methodKey];
const handlerType = Reflect.getMetadata(
PATTERN_HANDLER_METADATA,
prototypeCallback,
targetCallback,
);
if (isUndefined(handlerType)) {
return;
}
const patterns = Reflect.getMetadata(PATTERN_METADATA, prototypeCallback);
const transport = Reflect.getMetadata(
TRANSPORT_METADATA,
prototypeCallback,
);
const extras = Reflect.getMetadata(
PATTERN_EXTRAS_METADATA,
prototypeCallback,
);
const targetCallback = instance[methodKey];
const patterns = Reflect.getMetadata(PATTERN_METADATA, targetCallback);
const transport = Reflect.getMetadata(TRANSPORT_METADATA, targetCallback);
const extras = Reflect.getMetadata(PATTERN_EXTRAS_METADATA, targetCallback);
return {
methodKey,
targetCallback,

View File

@@ -597,7 +597,7 @@ export class ServerGrpc extends Server implements CustomTransportStrategy {
private async createServices(grpcPkg: any, packageName: string) {
if (!grpcPkg) {
const invalidPackageError = new InvalidGrpcPackageException(packageName);
this.logger.error(invalidPackageError.message, invalidPackageError.stack);
this.logger.error(invalidPackageError);
throw invalidPackageError;
}

View File

@@ -71,7 +71,6 @@ describe('ListenerMetadataExplorer', () => {
});
it(`should return undefined when "handlerType" metadata is undefined`, () => {
const metadata = instance.exploreMethodMetadata(
test,
Object.getPrototypeOf(test),
'noPattern',
);
@@ -81,7 +80,6 @@ describe('ListenerMetadataExplorer', () => {
describe('@MessagePattern', () => {
it(`should return pattern properties when "handlerType" metadata is not undefined`, () => {
const metadata = instance.exploreMethodMetadata(
test,
Object.getPrototypeOf(test),
'testMessage',
);
@@ -98,7 +96,6 @@ describe('ListenerMetadataExplorer', () => {
});
it(`should return multiple patterns when more than one is declared`, () => {
const metadata = instance.exploreMethodMetadata(
test,
Object.getPrototypeOf(test),
'testMultipleMessage',
);
@@ -119,7 +116,6 @@ describe('ListenerMetadataExplorer', () => {
describe('@EventPattern', () => {
it(`should return pattern properties when "handlerType" metadata is not undefined`, () => {
const metadata = instance.exploreMethodMetadata(
test,
Object.getPrototypeOf(test),
'testEvent',
);
@@ -136,7 +132,6 @@ describe('ListenerMetadataExplorer', () => {
});
it(`should return multiple patterns when more than one is declared`, () => {
const metadata = instance.exploreMethodMetadata(
test,
Object.getPrototypeOf(test),
'testMultipleEvent',
);

View File

@@ -46,12 +46,6 @@ export class BaseWsExceptionFilter<TError = any>
message: MESSAGES.UNKNOWN_EXCEPTION_MESSAGE,
});
if (this.isExceptionObject(exception)) {
return BaseWsExceptionFilter.logger.error(
exception.message,
exception.stack,
);
}
return BaseWsExceptionFilter.logger.error(exception);
}