From cc600841a364b34cbe957ee4d884e992dbc0828a Mon Sep 17 00:00:00 2001 From: Warren James Date: Fri, 14 Apr 2023 16:39:23 -0400 Subject: [PATCH 1/2] feat(NODE-4815): add maybeTruncate --- src/mongo_logger.ts | 23 +++++++++++++--- test/unit/mongo_logger.test.ts | 48 ++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 4 deletions(-) diff --git a/src/mongo_logger.ts b/src/mongo_logger.ts index 40102ed5502..6d36c5a12f0 100644 --- a/src/mongo_logger.ts +++ b/src/mongo_logger.ts @@ -53,6 +53,8 @@ export const SeverityLevel = Object.freeze({ OFF: 'off' } as const); +/** @internal */ +export const DEFAULT_MAX_DOCUMENT_LENGTH = 1000; /** @internal */ export type SeverityLevel = (typeof SeverityLevel)[keyof typeof SeverityLevel]; @@ -254,6 +256,18 @@ export interface LogConvertible extends Record { toLog(): Record; } +/** @internal */ +export function maybeTruncate( + ejson: string, + maxDocumentLength: number = DEFAULT_MAX_DOCUMENT_LENGTH +): string { + if (maxDocumentLength === 0) { + return ejson; + } + + return ejson.length > maxDocumentLength ? `${ejson.slice(0, maxDocumentLength)}...` : ejson; +} + /** @internal */ export type Loggable = LoggableEvent | LogConvertible; @@ -292,7 +306,8 @@ function attachConnectionFields( } function defaultLogTransform( - logObject: LoggableEvent | Record + logObject: LoggableEvent | Record, + maxDocumentLength: number = DEFAULT_MAX_DOCUMENT_LENGTH ): Omit { let log: Omit = Object.create(null); @@ -300,14 +315,14 @@ function defaultLogTransform( case COMMAND_STARTED: log = attachCommandFields(log, logObject); log.message = 'Command started'; - log.command = EJSON.stringify(logObject.command); + log.command = maybeTruncate(EJSON.stringify(logObject.command), maxDocumentLength); log.databaseName = logObject.databaseName; return log; case COMMAND_SUCCEEDED: log = attachCommandFields(log, logObject); log.message = 'Command succeeded'; log.durationMS = logObject.duration; - log.reply = EJSON.stringify(logObject.reply); + log.reply = maybeTruncate(EJSON.stringify(logObject.reply), maxDocumentLength); return log; case COMMAND_FAILED: log = attachCommandFields(log, logObject); @@ -452,7 +467,7 @@ export class MongoLogger { if (isLogConvertible(message)) { logMessage = { ...logMessage, ...message.toLog() }; } else { - logMessage = { ...logMessage, ...defaultLogTransform(message) }; + logMessage = { ...logMessage, ...defaultLogTransform(message, this.maxDocumentLength) }; } } this.logDestination.write(logMessage); diff --git a/test/unit/mongo_logger.test.ts b/test/unit/mongo_logger.test.ts index 9f9ca46826e..1a8ecc07408 100644 --- a/test/unit/mongo_logger.test.ts +++ b/test/unit/mongo_logger.test.ts @@ -19,7 +19,9 @@ import { CONNECTION_POOL_CREATED, CONNECTION_POOL_READY, CONNECTION_READY, + DEFAULT_MAX_DOCUMENT_LENGTH, Log, + maybeTruncate, MongoDBLogWritable, MongoLogger, MongoLoggerOptions, @@ -1239,4 +1241,50 @@ describe('class MongoLogger', function () { }); } }); + + describe('maybeTruncate', function () { + let largeEjsonString: string; + let smallEjsonString: string; + before(function () { + const largeDoc = {}; + for (let i = 0; i < DEFAULT_MAX_DOCUMENT_LENGTH; i++) { + largeDoc[`test${i}`] = `Hello_${i}`; + } + largeEjsonString = EJSON.stringify(largeDoc); + const smallDoc = { test: 'Hello' }; + smallEjsonString = EJSON.stringify(smallDoc); + }); + + context('when maxDocumentLength = 0', function () { + it('does not truncate document', function () { + expect(maybeTruncate(largeEjsonString, 0)).to.equal(largeEjsonString); + }); + }); + + context('when maxDocumentLength is non-zero', function () { + context('when document has length greater than maxDocumentLength', function () { + it('truncates ejson string to length of maxDocumentLength + 3', function () { + expect(maybeTruncate(largeEjsonString)).to.have.lengthOf(DEFAULT_MAX_DOCUMENT_LENGTH + 3); + }); + it('ends with "..."', function () { + expect(maybeTruncate(largeEjsonString)).to.match(/^.*\.\.\.$/); + }); + }); + + context('when document has length less than or equal to maxDocumentLength', function () { + it('does not truncate document', function () { + expect(maybeTruncate(smallEjsonString)).to.equal(smallEjsonString); + }); + it('does not end with "..."', function () { + expect(maybeTruncate(smallEjsonString)).to.not.match(/^.*\.\.\./); + }); + + it('produces valid relaxed EJSON', function () { + expect(() => { + EJSON.parse(maybeTruncate(smallEjsonString)); + }).to.not.throw(); + }); + }); + }); + }); }); From 8749aaa1ad967ac9f153d67be7bc9576a4f0abf5 Mon Sep 17 00:00:00 2001 From: Warren James Date: Tue, 18 Apr 2023 16:28:36 -0400 Subject: [PATCH 2/2] fix(NODE-4815): review fixes --- src/mongo_logger.ts | 17 +++++++---------- test/unit/mongo_logger.test.ts | 32 +++++++++++++++++--------------- 2 files changed, 24 insertions(+), 25 deletions(-) diff --git a/src/mongo_logger.ts b/src/mongo_logger.ts index 6d36c5a12f0..4ede5846bba 100644 --- a/src/mongo_logger.ts +++ b/src/mongo_logger.ts @@ -257,15 +257,12 @@ export interface LogConvertible extends Record { } /** @internal */ -export function maybeTruncate( - ejson: string, - maxDocumentLength: number = DEFAULT_MAX_DOCUMENT_LENGTH -): string { - if (maxDocumentLength === 0) { - return ejson; - } +export function stringifyWithMaxLen(value: any, maxDocumentLength: number): string { + const ejson = EJSON.stringify(value); - return ejson.length > maxDocumentLength ? `${ejson.slice(0, maxDocumentLength)}...` : ejson; + return maxDocumentLength !== 0 && ejson.length > maxDocumentLength + ? `${ejson.slice(0, maxDocumentLength)}...` + : ejson; } /** @internal */ @@ -315,14 +312,14 @@ function defaultLogTransform( case COMMAND_STARTED: log = attachCommandFields(log, logObject); log.message = 'Command started'; - log.command = maybeTruncate(EJSON.stringify(logObject.command), maxDocumentLength); + log.command = stringifyWithMaxLen(logObject.command, maxDocumentLength); log.databaseName = logObject.databaseName; return log; case COMMAND_SUCCEEDED: log = attachCommandFields(log, logObject); log.message = 'Command succeeded'; log.durationMS = logObject.duration; - log.reply = maybeTruncate(EJSON.stringify(logObject.reply), maxDocumentLength); + log.reply = stringifyWithMaxLen(logObject.reply, maxDocumentLength); return log; case COMMAND_FAILED: log = attachCommandFields(log, logObject); diff --git a/test/unit/mongo_logger.test.ts b/test/unit/mongo_logger.test.ts index 1a8ecc07408..0840d15770e 100644 --- a/test/unit/mongo_logger.test.ts +++ b/test/unit/mongo_logger.test.ts @@ -21,12 +21,12 @@ import { CONNECTION_READY, DEFAULT_MAX_DOCUMENT_LENGTH, Log, - maybeTruncate, MongoDBLogWritable, MongoLogger, MongoLoggerOptions, SEVERITY_LEVEL_MAP, - SeverityLevel + SeverityLevel, + stringifyWithMaxLen } from '../mongodb'; class BufferingStream extends Writable { @@ -1242,46 +1242,48 @@ describe('class MongoLogger', function () { } }); - describe('maybeTruncate', function () { - let largeEjsonString: string; - let smallEjsonString: string; + describe('stringifyWithMaxLen', function () { + const largeDoc = {}; + const smallDoc = { test: 'Hello' }; before(function () { - const largeDoc = {}; for (let i = 0; i < DEFAULT_MAX_DOCUMENT_LENGTH; i++) { largeDoc[`test${i}`] = `Hello_${i}`; } - largeEjsonString = EJSON.stringify(largeDoc); - const smallDoc = { test: 'Hello' }; - smallEjsonString = EJSON.stringify(smallDoc); }); context('when maxDocumentLength = 0', function () { it('does not truncate document', function () { - expect(maybeTruncate(largeEjsonString, 0)).to.equal(largeEjsonString); + expect(stringifyWithMaxLen(largeDoc, 0)).to.equal(EJSON.stringify(largeDoc)); }); }); context('when maxDocumentLength is non-zero', function () { context('when document has length greater than maxDocumentLength', function () { it('truncates ejson string to length of maxDocumentLength + 3', function () { - expect(maybeTruncate(largeEjsonString)).to.have.lengthOf(DEFAULT_MAX_DOCUMENT_LENGTH + 3); + expect(stringifyWithMaxLen(largeDoc, DEFAULT_MAX_DOCUMENT_LENGTH)).to.have.lengthOf( + DEFAULT_MAX_DOCUMENT_LENGTH + 3 + ); }); it('ends with "..."', function () { - expect(maybeTruncate(largeEjsonString)).to.match(/^.*\.\.\.$/); + expect(stringifyWithMaxLen(largeDoc, DEFAULT_MAX_DOCUMENT_LENGTH)).to.match(/^.*\.\.\.$/); }); }); context('when document has length less than or equal to maxDocumentLength', function () { it('does not truncate document', function () { - expect(maybeTruncate(smallEjsonString)).to.equal(smallEjsonString); + expect(stringifyWithMaxLen(smallDoc, DEFAULT_MAX_DOCUMENT_LENGTH)).to.equal( + EJSON.stringify(smallDoc) + ); }); it('does not end with "..."', function () { - expect(maybeTruncate(smallEjsonString)).to.not.match(/^.*\.\.\./); + expect(stringifyWithMaxLen(smallDoc, DEFAULT_MAX_DOCUMENT_LENGTH)).to.not.match( + /^.*\.\.\./ + ); }); it('produces valid relaxed EJSON', function () { expect(() => { - EJSON.parse(maybeTruncate(smallEjsonString)); + EJSON.parse(stringifyWithMaxLen(smallDoc, DEFAULT_MAX_DOCUMENT_LENGTH)); }).to.not.throw(); }); });