Skip to content

feat(NODE-5452): Logging Cosmos Document DB Info Message #3902

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 13 commits into from
Nov 3, 2023
Merged
1 change: 1 addition & 0 deletions src/connection_string.ts
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,7 @@ export function parseOptions(
MONGODB_LOG_TOPOLOGY: process.env.MONGODB_LOG_TOPOLOGY,
MONGODB_LOG_SERVER_SELECTION: process.env.MONGODB_LOG_SERVER_SELECTION,
MONGODB_LOG_CONNECTION: process.env.MONGODB_LOG_CONNECTION,
MONGODB_LOG_CLIENT: process.env.MONGODB_LOG_CLIENT,
MONGODB_LOG_ALL: process.env.MONGODB_LOG_ALL,
MONGODB_LOG_MAX_DOCUMENT_LENGTH: process.env.MONGODB_LOG_MAX_DOCUMENT_LENGTH,
MONGODB_LOG_PATH: process.env.MONGODB_LOG_PATH,
Expand Down
25 changes: 25 additions & 0 deletions src/mongo_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,13 @@ import type { SrvPoller } from './sdam/srv_polling';
import { Topology, type TopologyEvents } from './sdam/topology';
import { ClientSession, type ClientSessionOptions, ServerSessionPool } from './sessions';
import {
COSMOS_DB_CHECK,
COSMOS_DB_MSG,
DOCUMENT_DB_CHECK,
DOCUMENT_DB_MSG,
type HostAddress,
hostMatchesWildcards,
isHostMatch,
type MongoDBNamespace,
ns,
resolveOptions
Expand Down Expand Up @@ -364,6 +369,26 @@ export class MongoClient extends TypedEventEmitter<MongoClientEvents> {
return true;
}
};
this.checkForNonGenuineHosts();
}

/** @internal */
private checkForNonGenuineHosts() {
const documentDBHostnames = this[kOptions].hosts.filter((hostAddress: HostAddress) =>
isHostMatch(DOCUMENT_DB_CHECK, hostAddress.host)
);
const srvHostIsDocumentDB = isHostMatch(DOCUMENT_DB_CHECK, this[kOptions].srvHost);

const cosmosDBHostnames = this[kOptions].hosts.filter((hostAddress: HostAddress) =>
isHostMatch(COSMOS_DB_CHECK, hostAddress.host)
);
const srvHostIsCosmosDB = isHostMatch(COSMOS_DB_CHECK, this[kOptions].srvHost);

if (documentDBHostnames.length !== 0 || srvHostIsDocumentDB) {
this.mongoLogger.info('client', DOCUMENT_DB_MSG);
} else if (cosmosDBHostnames.length !== 0 || srvHostIsCosmosDB) {
this.mongoLogger.info('client', COSMOS_DB_MSG);
}
}

/** @see MongoOptions */
Expand Down
8 changes: 7 additions & 1 deletion src/mongo_logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ export const MongoLoggableComponent = Object.freeze({
COMMAND: 'command',
TOPOLOGY: 'topology',
SERVER_SELECTION: 'serverSelection',
CONNECTION: 'connection'
CONNECTION: 'connection',
CLIENT: 'client'
} as const);

/** @internal */
Expand All @@ -115,6 +116,8 @@ export interface MongoLoggerEnvOptions {
MONGODB_LOG_SERVER_SELECTION?: string;
/** Severity level for CMAP */
MONGODB_LOG_CONNECTION?: string;
/** Severity level for client */
MONGODB_LOG_CLIENT?: string;
/** Default severity level to be if any of the above are unset */
MONGODB_LOG_ALL?: string;
/** Max length of embedded EJSON docs. Setting to 0 disables truncation. Defaults to 1000. */
Expand All @@ -140,6 +143,8 @@ export interface MongoLoggerOptions {
serverSelection: SeverityLevel;
/** Severity level for connection component */
connection: SeverityLevel;
/** Severity level for client component */
client: SeverityLevel;
/** Default severity level to be used if any of the above are unset */
default: SeverityLevel;
};
Expand Down Expand Up @@ -528,6 +533,7 @@ export class MongoLogger {
parseSeverityFromString(combinedOptions.MONGODB_LOG_SERVER_SELECTION) ?? defaultSeverity,
connection:
parseSeverityFromString(combinedOptions.MONGODB_LOG_CONNECTION) ?? defaultSeverity,
client: parseSeverityFromString(combinedOptions.MONGODB_LOG_CLIENT) ?? defaultSeverity,
default: defaultSeverity
},
maxDocumentLength:
Expand Down
17 changes: 17 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1282,3 +1282,20 @@ export class TimeoutController extends AbortController {
this.timeoutId = null;
}
}

/** @internal */
export const DOCUMENT_DB_CHECK = /(\.docdb\.amazonaws\.com$)|(\.docdb-elastic\.amazonaws\.com$)/;
/** @internal */
export const COSMOS_DB_CHECK = /\.cosmos\.azure\.com$/;

/** @internal */
export const DOCUMENT_DB_MSG =
'You appear to be connected to a DocumentDB cluster. For more information regarding feature compatibility and support please visit https://www.mongodb.com/supportability/documentdb';
/** @internal */
export const COSMOS_DB_MSG =
'You appear to be connected to a CosmosDB cluster. For more information regarding feature compatibility and support please visit https://www.mongodb.com/supportability/cosmosdb';

/** @internal */
export function isHostMatch(match: RegExp, host?: string): boolean {
return host && match.test(host.toLowerCase()) ? true : false;
}
56 changes: 56 additions & 0 deletions test/unit/connection_string.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ import { inspect } from 'util';
import {
AUTH_MECHS_AUTH_SRC_EXTERNAL,
AuthMechanism,
COSMOS_DB_MSG,
DEFAULT_ALLOWED_HOSTS,
DOCUMENT_DB_MSG,
FEATURE_FLAGS,
type Log,
MongoAPIError,
Expand Down Expand Up @@ -883,4 +885,58 @@ describe('Connection String', function () {
});
});
});

describe('non-genuine hosts', () => {
beforeEach(() => {
process.env.MONGODB_LOG_CLIENT = 'info';
});

afterEach(() => {
process.env.MONGODB_LOG_CLIENT = undefined;
});

const loggerFeatureFlag = Symbol.for('@@mdb.enableMongoLogger');
const test_cases = [
['non-SRV example uri', 'mongodb://a.example.com:27017,b.example.com:27017/', ''],
['non-SRV default uri', 'mongodb://a.mongodb.net:27017', ''],
['SRV example uri', 'mongodb+srv://a.example.com/', ''],
['SRV default uri', 'mongodb+srv://a.mongodb.net/', ''],
// ensure case insensitity
['non-SRV cosmosDB uri', 'mongodb://a.mongo.COSmos.aZure.com:19555/', COSMOS_DB_MSG],
['non-SRV documentDB uri', 'mongodb://a.docDB.AmazonAws.com:27017/', DOCUMENT_DB_MSG],
[
'non-SRV documentDB uri ',
'mongodb://a.docdB-eLasTic.amazonaws.com:27017/',
DOCUMENT_DB_MSG
],
['SRV cosmosDB uri', 'mongodb+srv://a.mongo.COSmos.aZure.com/', COSMOS_DB_MSG],
['SRV documentDB uri', 'mongodb+srv://a.docDB.AmazonAws.com/', DOCUMENT_DB_MSG],
['SRV documentDB uri 2', 'mongodb+srv://a.docdB-eLastic.amazonaws.com/', DOCUMENT_DB_MSG]
];

context('when logging is turned on', () => {
for (const [name, uri, message] of test_cases) {
it(`${name} triggers ${message.length === 0 ? 'no' : 'correct info'} msg`, () => {
const stream = {
buffer: [],
write(log) {
this.buffer.push(log);
}
};
new MongoClient(uri, {
[loggerFeatureFlag]: true,
mongodbLogPath: stream
});

if (message.length > 0) {
expect(stream.buffer).to.have.lengthOf(1);
expect(stream.buffer[0]).to.have.property('c', 'client');
expect(stream.buffer[0]).to.have.property('message', message);
} else {
expect(stream.buffer).to.have.lengthOf(0);
}
});
}
});
});
});
3 changes: 2 additions & 1 deletion test/unit/mongo_logger.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,8 @@ describe('class MongoLogger', function () {
['MONGODB_LOG_COMMAND', 'command'],
['MONGODB_LOG_TOPOLOGY', 'topology'],
['MONGODB_LOG_SERVER_SELECTION', 'serverSelection'],
['MONGODB_LOG_CONNECTION', 'connection']
['MONGODB_LOG_CONNECTION', 'connection'],
['MONGODB_LOG_CLIENT', 'client']
]);

function* makeValidOptions(): Generator<[string, string]> {
Expand Down