diff --git a/packages/logger/src/console.ts b/packages/logger/src/console.ts index 5aa5de18..b53c6094 100644 --- a/packages/logger/src/console.ts +++ b/packages/logger/src/console.ts @@ -1,6 +1,6 @@ import { format, transports } from 'winston'; import * as rTracer from 'cls-rtracer'; -import { globalLoggerCtxKey, traceFormatter } from './utils'; +import { getCircularReplacer, globalLoggerCtxKey, traceFormatter } from './utils'; export interface RestoreLoggerConsoleTransportOptions extends transports.ConsoleTransportOptions { prettyPrint?: boolean | any; @@ -23,10 +23,10 @@ function createTracerFormat(opts: RestoreLoggerConsoleTransportOptions) { delete info.timestamp; let object = {}; if (splat) { - object = JSON.stringify(splat); + object = JSON.stringify(splat, getCircularReplacer()); } if (message && Object.entries(message).length !== 0 && message.constructor === Object) { - message = JSON.stringify(message); + message = JSON.stringify(message, getCircularReplacer()); } let ret: string[] = []; ret.push(`${level}: ${time}`); diff --git a/packages/logger/src/elasticsearch.ts b/packages/logger/src/elasticsearch.ts index 03a9fdfd..80b3613e 100644 --- a/packages/logger/src/elasticsearch.ts +++ b/packages/logger/src/elasticsearch.ts @@ -1,7 +1,7 @@ import { ElasticsearchTransport, ElasticsearchTransportOptions, Transformer } from 'winston-elasticsearch'; import * as os from 'os'; import * as rTracer from 'cls-rtracer'; -import { globalLoggerCtxKey, getRealTrace } from './utils'; +import { globalLoggerCtxKey, getRealTrace, getCircularReplacer } from './utils'; export const indexTemplate = require('../elasticsearch-index-template.json'); @@ -42,7 +42,7 @@ function createTransformer(opts: RestoreLoggerElasticsearchTransportOptions) { transformed.source_host = os.hostname(); transformed.message = logData.message; if (typeof transformed.message === 'object') { - transformed.message = JSON.stringify(transformed.message); + transformed.message = JSON.stringify(transformed.message, getCircularReplacer()); } transformed.severity = logData.level; transformed.fields = logData.meta; diff --git a/packages/logger/src/utils.ts b/packages/logger/src/utils.ts index db58e030..4689e4da 100644 --- a/packages/logger/src/utils.ts +++ b/packages/logger/src/utils.ts @@ -136,3 +136,16 @@ export const getRealTrace = (): any => { // A symbol used as key in the global name space to put the AsyncLocalStorage store under export const globalLoggerCtxKey = Symbol('loggerCtx'); + +export const getCircularReplacer = () => { + const seen = new WeakSet(); + return (key: string, value: unknown) => { + if (typeof value === 'object' && value !== null) { + if (seen.has(value)) { + return; + } + seen.add(value); + } + return value; + }; +}; diff --git a/packages/logger/tests/test.ts b/packages/logger/tests/test.ts index e1c26b9f..9327439e 100644 --- a/packages/logger/tests/test.ts +++ b/packages/logger/tests/test.ts @@ -66,6 +66,12 @@ describe('a logger', () => { it('an error with stack trace', (done) => { logger.error('Generic Error!'); done(); + }); + it('a circular object', (done) => { + const obj: any = {name: "Bob"}; + obj.child = obj; + logger.info(obj); + done(); }); esTransport.bulkWriter.stop(); });