mirror of
https://github.com/nestjs/nest.git
synced 2026-02-23 15:52:50 +00:00
Compare commits
8 Commits
fix/instan
...
feat/logge
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
47941ba4d9 | ||
|
|
9129e4f858 | ||
|
|
f37dafc132 | ||
|
|
66c4810b0f | ||
|
|
23e602b712 | ||
|
|
e767bd364e | ||
|
|
03d8bcc21c | ||
|
|
e75eb1d14a |
@@ -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);
|
||||
|
||||
@@ -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[]) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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 }));
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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',
|
||||
);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user