diff --git a/.talismanrc b/.talismanrc index 50726e53c0..a449f51069 100644 --- a/.talismanrc +++ b/.talismanrc @@ -1,4 +1,6 @@ fileignoreconfig: - filename: pnpm-lock.yaml checksum: a4f592e8bcc46e37d9b118deb6ac0c9a452c3392092cf716b16bae6fb037102d + - filename: packages/contentstack-utilities/src/logger/logger.ts + checksum: ddde5120404e55a0bc7b56223009ea2111d392e387037f6471c5e7b6388ae38b version: '1.0' diff --git a/packages/contentstack-utilities/src/logger/logger.ts b/packages/contentstack-utilities/src/logger/logger.ts index ebdb8fe21a..846f41c64f 100644 --- a/packages/contentstack-utilities/src/logger/logger.ts +++ b/packages/contentstack-utilities/src/logger/logger.ts @@ -65,7 +65,7 @@ export default class Logger { winston.format.printf((info) => { // Apply minimal redaction for files (debugging info preserved) const redactedInfo = this.redact(info, false); - return JSON.stringify(redactedInfo); + return this.safeStringify(redactedInfo); }), ), }), @@ -117,6 +117,26 @@ export default class Logger { } } + /** + * Stringifies a log entry without throwing on circular references. + * `redact()` can fall back to the original (still-circular) object when + * cloning fails, so this must never assume the input is cycle-free. + */ + private safeStringify(value: any): string { + const seen = new WeakSet(); + try { + return JSON.stringify(value, (_key, val) => { + if (typeof val === 'object' && val !== null) { + if (seen.has(val)) return '[Circular]'; + seen.add(val); + } + return val; + }); + } catch { + return JSON.stringify({ level: value?.level, message: value?.message ?? '[Unserializable log entry]' }); + } + } + private shouldLog(level: LogType, target: 'console' | 'file'): boolean { // If console logging is disabled, don't log to console if (target === 'console' && this.config.consoleLoggingEnabled === false) { @@ -186,9 +206,12 @@ export default class Logger { this.loggers.error.error(logPayload); } - // For console, use debug level if hidden, otherwise error level + // For console, use debug level if hidden, otherwise error level. Each level's + // winston logger instance bundles both the file and console transports, so only + // write again here when the target differs from the one already written above — + // otherwise this would duplicate both the console line and the file entry. const consoleLevel: LogType = params.hidden ? 'debug' : 'error'; - if (this.shouldLog(consoleLevel, 'console')) { + if (consoleLevel !== 'error' && this.shouldLog(consoleLevel, 'console')) { this.loggers[consoleLevel].error(logPayload); } } diff --git a/packages/contentstack/package.json b/packages/contentstack/package.json index 075ff0f80c..753b6487e7 100755 --- a/packages/contentstack/package.json +++ b/packages/contentstack/package.json @@ -22,7 +22,7 @@ "@contentstack/cli-audit": "~1.19.5", "@contentstack/cli-cm-export": "~1.25.3", "@contentstack/cli-cm-import": "~1.33.5", - "@contentstack/cli-auth": "~1.8.4", + "@contentstack/cli-auth": "~1.8.5", "@contentstack/cli-cm-bootstrap": "~1.19.7", "@contentstack/cli-cm-branches": "~1.8.3", "@contentstack/cli-cm-bulk-publish": "~1.12.1",