diff --git a/src/connection_string.ts b/src/connection_string.ts index 54dec09765f..a1dd0614251 100644 --- a/src/connection_string.ts +++ b/src/connection_string.ts @@ -1224,7 +1224,10 @@ export const OPTIONS = { 'useUnifiedTopology has no effect since Node.js Driver version 4.0.0 and will be removed in the next major version' } as OptionDescriptor, // MongoLogger - // TODO(NODE-4849): Tighten the type of mongodbLogPath + /** + * @internal + * TODO: NODE-5671 - remove internal flag + */ mongodbLogPath: { type: 'any' } } as Record; diff --git a/src/mongo_client.ts b/src/mongo_client.ts index 3e9a4c504b8..2122530c7f4 100644 --- a/src/mongo_client.ts +++ b/src/mongo_client.ts @@ -21,7 +21,7 @@ import { MONGO_CLIENT_EVENTS } from './constants'; import { Db, type DbOptions } from './db'; import type { Encrypter } from './encrypter'; import { MongoInvalidArgumentError } from './error'; -import { MongoLogger, type MongoLoggerOptions } from './mongo_logger'; +import { type MongoDBLogWritable, MongoLogger, type MongoLoggerOptions } from './mongo_logger'; import { TypedEventEmitter } from './mongo_types'; import { executeOperation } from './operations/execute_operation'; import { RunAdminCommandOperation } from './operations/run_command'; @@ -252,6 +252,11 @@ export interface MongoClientOptions extends BSONSerializeOptions, SupportedNodeC srvPoller?: SrvPoller; /** @internal */ connectionType?: typeof Connection; + /** + * @internal + * TODO: NODE-5671 - remove internal flag + */ + mongodbLogPath?: 'stderr' | 'stdout' | MongoDBLogWritable; /** @internal */ [featureFlag: symbol]: any; @@ -825,6 +830,14 @@ export interface MongoOptions /** @internal */ [featureFlag: symbol]: any; - /** @internal */ + /** + * @internal + * TODO: NODE-5671 - remove internal flag + */ mongoLoggerOptions: MongoLoggerOptions; + /** + * @internal + * TODO: NODE-5671 - remove internal flag + */ + mongodbLogPath?: 'stderr' | 'stdout' | MongoDBLogWritable; } diff --git a/src/mongo_logger.ts b/src/mongo_logger.ts index ab75388d7fe..d5704655eb2 100644 --- a/src/mongo_logger.ts +++ b/src/mongo_logger.ts @@ -191,7 +191,7 @@ export function createStdioLogger(stream: { */ function resolveLogPath( { MONGODB_LOG_PATH }: MongoLoggerEnvOptions, - { mongodbLogPath }: { mongodbLogPath?: string | Writable | MongoDBLogWritable } + { mongodbLogPath }: MongoLoggerMongoClientOptions ): MongoDBLogWritable { if (typeof mongodbLogPath === 'string' && /^stderr$/i.test(mongodbLogPath)) { return createStdioLogger(process.stderr); diff --git a/test/tools/unified-spec-runner/entities.ts b/test/tools/unified-spec-runner/entities.ts index 623b2ffe82a..f6058a3d8e6 100644 --- a/test/tools/unified-spec-runner/entities.ts +++ b/test/tools/unified-spec-runner/entities.ts @@ -189,10 +189,9 @@ export class UnifiedMongoClient extends MongoClient { [Symbol.for('@@mdb.skipPingOnConnect')]: true, [Symbol.for('@@mdb.enableMongoLogger')]: true, [Symbol.for('@@mdb.internalLoggerConfig')]: componentSeverities, - // @ts-expect-error TODO(NODE-4849): Remove this once we have support for mongodbLogPath - mongodbLogPath: logCollector, ...getEnvironmentalOptions(), - ...(description.serverApi ? { serverApi: description.serverApi } : {}) + ...(description.serverApi ? { serverApi: description.serverApi } : {}), + mongodbLogPath: logCollector } as any); this.logCollector = logCollector; diff --git a/test/unit/connection_string.test.ts b/test/unit/connection_string.test.ts index 01baa184e1c..372e9f27b54 100644 --- a/test/unit/connection_string.test.ts +++ b/test/unit/connection_string.test.ts @@ -4,12 +4,14 @@ import * as process from 'node:process'; import { expect } from 'chai'; import * as dns from 'dns'; import * as sinon from 'sinon'; +import { inspect } from 'util'; import { AUTH_MECHS_AUTH_SRC_EXTERNAL, AuthMechanism, DEFAULT_ALLOWED_HOSTS, FEATURE_FLAGS, + type Log, MongoAPIError, MongoClient, MongoCredentials, @@ -832,4 +834,53 @@ describe('Connection String', function () { .that.matches(/useUnifiedTopology has no effect/); }); }); + + describe('when mongodbLogPath is in options', function () { + const loggerFeatureFlag = Symbol.for('@@mdb.enableMongoLogger'); + + let stderrStub; + let stdoutStub; + + beforeEach(() => { + stdoutStub = sinon.stub(process.stdout); + stderrStub = sinon.stub(process.stderr); + }); + + afterEach(() => { + sinon.restore(); + }); + + context('when option is `stderr`', function () { + it('it is accessible through mongoLogger.logDestination', function () { + const client = new MongoClient('mongodb://a/?mongodbLogPath=stderr', { + [loggerFeatureFlag]: true + }); + const log: Log = { t: new Date(), c: 'ConnectionStringStdErr', s: 'error' }; + client.options.mongoLoggerOptions.logDestination.write(log); + expect(stderrStub.write).calledWith(inspect(log, { breakLength: Infinity, compact: true })); + }); + }); + + context('when option is `stdout`', function () { + it('it is accessible through mongoLogger.logDestination', function () { + const client = new MongoClient('mongodb://a/?mongodbLogPath=stdout', { + [loggerFeatureFlag]: true + }); + const log: Log = { t: new Date(), c: 'ConnectionStringStdOut', s: 'error' }; + client.options.mongoLoggerOptions.logDestination.write(log); + expect(stdoutStub.write).calledWith(inspect(log, { breakLength: Infinity, compact: true })); + }); + }); + + context('when option is invalid', function () { + it('it defaults to stderr', function () { + const client = new MongoClient('mongodb://a/?mongodbLogPath=stdnothing', { + [loggerFeatureFlag]: true + }); + const log: Log = { t: new Date(), c: 'ConnectionStringInvalidOption', s: 'error' }; + client.options.mongoLoggerOptions.logDestination.write(log); + expect(stderrStub.write).calledWith(inspect(log, { breakLength: Infinity, compact: true })); + }); + }); + }); }); diff --git a/test/unit/mongo_client.test.js b/test/unit/mongo_client.test.js index ad18d944a85..8757146baf3 100644 --- a/test/unit/mongo_client.test.js +++ b/test/unit/mongo_client.test.js @@ -1,4 +1,7 @@ -'use strict'; +('use strict'); + +import { inspect } from 'util'; + const os = require('os'); const fs = require('fs'); const { expect } = require('chai'); @@ -813,4 +816,73 @@ describe('MongoOptions', function () { expect(client.options).to.have.property('mongoLoggerOptions').to.equal(expectedLoggingObject); }); }); + + context('when mongodbLogPath is in options', function () { + const loggerFeatureFlag = Symbol.for('@@mdb.enableMongoLogger'); + + let stderrStub; + let stdoutStub; + + beforeEach(() => { + stdoutStub = sinon.stub(process.stdout); + stderrStub = sinon.stub(process.stderr); + }); + + afterEach(() => { + sinon.restore(); + }); + + context('when option is `stderr`', function () { + it('it is accessible through mongoLogger.logDestination', function () { + const client = new MongoClient('mongodb://a/', { + [loggerFeatureFlag]: true, + mongodbLogPath: 'stderr' + }); + const log = { t: new Date(), c: 'constructorStdErr', s: 'error' }; + client.options.mongoLoggerOptions.logDestination.write(log); + expect(stderrStub.write).calledWith(inspect(log, { breakLength: Infinity, compact: true })); + }); + }); + + context('when option is a MongoDBLogWritable stream', function () { + it('it is accessible through mongoLogger.logDestination', function () { + const writable = { + buffer: [], + write(log) { + this.buffer.push(log); + } + }; + const client = new MongoClient('mongodb://a/', { + [loggerFeatureFlag]: true, + mongodbLogPath: writable + }); + expect(client.options.mongoLoggerOptions.logDestination).to.deep.equal(writable); + }); + }); + + context('when option is `stdout`', function () { + it('it is accessible through mongoLogger.logDestination', function () { + const client = new MongoClient('mongodb://a/', { + [loggerFeatureFlag]: true, + mongodbLogPath: 'stdout' + }); + const log = { t: new Date(), c: 'constructorStdOut', s: 'error' }; + client.options.mongoLoggerOptions.logDestination.write(log); + expect(stdoutStub.write).calledWith(inspect(log, { breakLength: Infinity, compact: true })); + }); + }); + + context('when option is invalid', function () { + it('it defaults to stderr', function () { + const invalidOption = 'stdnothing'; + const client = new MongoClient('mongodb://a/', { + [loggerFeatureFlag]: true, + mongodbLogPath: invalidOption + }); + const log = { t: new Date(), c: 'constructorInvalidOption', s: 'error' }; + client.options.mongoLoggerOptions.logDestination.write(log); + expect(stderrStub.write).calledWith(inspect(log, { breakLength: Infinity, compact: true })); + }); + }); + }); });