Skip to content

feat(NODE-4849): Add Typescript support for log path in client options #3886

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Oct 23, 2023
5 changes: 4 additions & 1 deletion src/connection_string.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<keyof MongoClientOptions, OptionDescriptor>;

Expand Down
17 changes: 15 additions & 2 deletions src/mongo_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
2 changes: 1 addition & 1 deletion src/mongo_logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
5 changes: 2 additions & 3 deletions test/tools/unified-spec-runner/entities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
51 changes: 51 additions & 0 deletions test/unit/connection_string.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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 }));
});
});
});
});
74 changes: 73 additions & 1 deletion test/unit/mongo_client.test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
'use strict';
('use strict');

import { inspect } from 'util';

const os = require('os');
const fs = require('fs');
const { expect } = require('chai');
Expand Down Expand Up @@ -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 }));
});
});
});
});