Skip to content

Commit 5582cb3

Browse files
committed
fix: backport emitWarning helper
Unify the way we print out messages relating to upcoming changes or potential misuse of the driver. Using the emitWarning API allows users to configure when and how messages are shown. NODE-2317
1 parent d67ffa7 commit 5582cb3

File tree

16 files changed

+86
-20
lines changed

16 files changed

+86
-20
lines changed

.eslintrc.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
"es/no-destructuring": "error",
2222
"es/no-rest-spread-properties": "error",
2323
"es/no-spread-elements": "error",
24-
"no-console": "off",
24+
"no-console": "error",
2525
"eqeqeq": ["error", "always", { "null": "ignore" }],
2626
"strict": ["error", "global"]
2727
},

lib/collection.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
const deprecate = require('util').deprecate;
44
const deprecateOptions = require('./utils').deprecateOptions;
5+
const emitWarningOnce = require('./utils').emitWarningOnce;
56
const checkCollectionName = require('./utils').checkCollectionName;
67
const ObjectID = require('./core').BSON.ObjectID;
78
const MongoError = require('./core').MongoError;
@@ -323,7 +324,7 @@ Collection.prototype.find = deprecateOptions(
323324
function(query, options, callback) {
324325
if (typeof callback === 'object') {
325326
// TODO(MAJOR): throw in the future
326-
console.warn('Third parameter to `find()` must be a callback or undefined');
327+
emitWarningOnce('Third parameter to `find()` must be a callback or undefined');
327328
}
328329

329330
let selector = query;
@@ -1092,7 +1093,7 @@ Collection.prototype.findOne = deprecateOptions(
10921093
function(query, options, callback) {
10931094
if (typeof callback === 'object') {
10941095
// TODO(MAJOR): throw in the future
1095-
console.warn('Third parameter to `findOne()` must be a callback or undefined');
1096+
emitWarningOnce('Third parameter to `findOne()` must be a callback or undefined');
10961097
}
10971098

10981099
if (typeof query === 'function') (callback = query), (query = {}), (options = {});

lib/core/auth/scram.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ const Buffer = require('safe-buffer').Buffer;
44
const retrieveBSON = require('../connection/utils').retrieveBSON;
55
const MongoError = require('../error').MongoError;
66
const AuthProvider = require('./auth_provider').AuthProvider;
7+
const emitWarningOnce = require('../../utils').emitWarning;
78

89
const BSON = retrieveBSON();
910
const Binary = BSON.Binary;
@@ -24,7 +25,7 @@ class ScramSHA extends AuthProvider {
2425
prepare(handshakeDoc, authContext, callback) {
2526
const cryptoMethod = this.cryptoMethod;
2627
if (cryptoMethod === 'sha256' && saslprep == null) {
27-
console.warn('Warning: no saslprep library specified. Passwords will not be sanitized');
28+
emitWarningOnce('Warning: no saslprep library specified. Passwords will not be sanitized');
2829
}
2930

3031
crypto.randomBytes(24, (err, nonce) => {

lib/core/connection/logger.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ var Logger = function(className, options) {
3737
if (options.logger) {
3838
currentLogger = options.logger;
3939
} else if (currentLogger == null) {
40+
// eslint-disable-next-line no-console
4041
currentLogger = console.log;
4142
}
4243

lib/core/connection/msg.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ const Buffer = require('safe-buffer').Buffer;
3131
const opcodes = require('../wireprotocol/shared').opcodes;
3232
const databaseNamespace = require('../wireprotocol/shared').databaseNamespace;
3333
const ReadPreference = require('../topologies/read_preference');
34+
const emitWarning = require('../../utils').emitWarning;
3435

3536
// Incrementing request id
3637
let _requestId = 0;
@@ -196,7 +197,7 @@ class BinMsg {
196197
while (this.index < this.data.length) {
197198
const payloadType = this.data.readUInt8(this.index++);
198199
if (payloadType === 1) {
199-
console.error('TYPE 1');
200+
emitWarning('TYPE 1'); // ???
200201
} else if (payloadType === 0) {
201202
const bsonSize = this.data.readUInt32LE(this.index);
202203
const bin = this.data.slice(this.index, this.index + bsonSize);

lib/core/sdam/topology.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ const ServerSessionPool = require('../sessions').ServerSessionPool;
2727
const makeClientMetadata = require('../utils').makeClientMetadata;
2828
const CMAP_EVENT_NAMES = require('../../cmap/events').CMAP_EVENT_NAMES;
2929
const compareTopologyVersion = require('./server_description').compareTopologyVersion;
30+
const emitWarning = require('../../utils').emitWarning;
3031

3132
const common = require('./common');
3233
const drainTimerQueue = common.drainTimerQueue;
@@ -739,7 +740,7 @@ class Topology extends EventEmitter {
739740
}
740741

741742
unref() {
742-
console.log('not implemented: `unref`');
743+
emitWarning('not implemented: `unref`');
743744
}
744745

745746
// NOTE: There are many places in code where we explicitly check the last isMaster

lib/core/tools/smoke_plugin.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ exports.attachToRunner = function(runner, outputFile) {
5252
fs.writeFileSync(outputFile, JSON.stringify(smokeOutput));
5353

5454
// Standard NodeJS uncaught exception handler
55+
// eslint-disable-next-line no-console
5556
console.error(err.stack);
5657
process.exit(1);
5758
});

lib/core/topologies/read_preference.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
'use strict';
2+
const emitWarningOnce = require('../../utils').emitWarningOnce;
23

34
/**
45
* The **ReadPreference** class is a class that represents a MongoDB ReadPreference and is
@@ -20,7 +21,7 @@ const ReadPreference = function(mode, tags, options) {
2021

2122
// TODO(major): tags MUST be an array of tagsets
2223
if (tags && !Array.isArray(tags)) {
23-
console.warn(
24+
emitWarningOnce(
2425
'ReadPreference tags must be an array, this will change in the next major version'
2526
);
2627

lib/core/uri_parser.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ const qs = require('querystring');
44
const dns = require('dns');
55
const MongoParseError = require('./error').MongoParseError;
66
const ReadPreference = require('./topologies/read_preference');
7+
const emitWarningOnce = require('../utils').emitWarningOnce;
78

89
/**
910
* The following regular expression validates a connection string and breaks the
@@ -438,7 +439,7 @@ function parseQueryString(query, options) {
438439
// special cases for known deprecated options
439440
if (result.wtimeout && result.wtimeoutms) {
440441
delete result.wtimeout;
441-
console.warn('Unsupported option `wtimeout` specified');
442+
emitWarningOnce('Unsupported option `wtimeout` specified');
442443
}
443444

444445
return Object.keys(result).length ? result : null;

lib/core/wireprotocol/kill_cursors.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const MongoError = require('../error').MongoError;
55
const MongoNetworkError = require('../error').MongoNetworkError;
66
const collectionNamespace = require('./shared').collectionNamespace;
77
const maxWireVersion = require('../utils').maxWireVersion;
8+
const emitWarning = require('../utils').emitWarning;
89
const command = require('./command');
910

1011
function killCursors(server, ns, cursorState, callback) {
@@ -31,7 +32,7 @@ function killCursors(server, ns, cursorState, callback) {
3132
if (typeof callback === 'function') {
3233
callback(err, null);
3334
} else {
34-
console.warn(err);
35+
emitWarning(err);
3536
}
3637
}
3738
}

lib/operations/add_user.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const defineAspects = require('./operation').defineAspects;
66
const crypto = require('crypto');
77
const handleCallback = require('../utils').handleCallback;
88
const toError = require('../utils').toError;
9+
const emitWarning = require('../utils').emitWarning;
910

1011
class AddUserOperation extends CommandOperation {
1112
constructor(db, username, password, options) {
@@ -29,7 +30,7 @@ class AddUserOperation extends CommandOperation {
2930
// If not roles defined print deprecated message
3031
// TODO: handle deprecation properly
3132
if (roles.length === 0) {
32-
console.log('Creating a user without roles is deprecated in MongoDB >= 2.6');
33+
emitWarning('Creating a user without roles is deprecated in MongoDB >= 2.6');
3334
}
3435

3536
// Check the db name and add roles if needed

lib/operations/connect.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const ReplSet = require('../topologies/replset');
1313
const Server = require('../topologies/server');
1414
const ServerSessionPool = require('../core').Sessions.ServerSessionPool;
1515
const emitDeprecationWarning = require('../utils').emitDeprecationWarning;
16+
const emitWarningOnce = require('../utils').emitWarningOnce;
1617
const fs = require('fs');
1718
const WriteConcern = require('../write_concern');
1819
const BSON = require('../core/connection/utils').retrieveBSON();
@@ -182,12 +183,12 @@ function validOptions(options) {
182183
if (options.validateOptions) {
183184
return new MongoError(`option ${name} is not supported`);
184185
} else {
185-
console.warn(`the options [${name}] is not supported`);
186+
emitWarningOnce(`the options [${name}] is not supported`);
186187
}
187188
}
188189

189190
if (legacyOptionNames.indexOf(name) !== -1) {
190-
console.warn(
191+
emitWarningOnce(
191192
`the server/replset/mongos/db options are deprecated, ` +
192193
`all their options are supported at the top level of the options object [${validOptionNames}]`
193194
);
@@ -257,9 +258,6 @@ function resolveTLSOptions(options) {
257258
});
258259
}
259260

260-
const emitDeprecationForNonUnifiedTopology = deprecate(() => {},
261-
'current Server Discovery and Monitoring engine is deprecated, and will be removed in a future version. ' + 'To use the new Server Discover and Monitoring engine, pass option { useUnifiedTopology: true } to the MongoClient constructor.');
262-
263261
function connect(mongoClient, url, options, callback) {
264262
options = Object.assign({}, options);
265263

@@ -335,7 +333,9 @@ function connect(mongoClient, url, options, callback) {
335333
return createTopology(mongoClient, 'unified', _finalOptions, connectCallback);
336334
}
337335

338-
emitDeprecationForNonUnifiedTopology();
336+
emitWarningOnce(
337+
'Current Server Discovery and Monitoring engine is deprecated, and will be removed in a future version. To use the new Server Discover and Monitoring engine, pass option { useUnifiedTopology: true } to the MongoClient constructor.'
338+
);
339339

340340
// Do we have a replicaset then skip discovery and go straight to connectivity
341341
if (_finalOptions.replicaSet || _finalOptions.rs_name) {

lib/utils.js

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -534,7 +534,9 @@ function decorateWithExplain(command, explain) {
534534
return { explain: command, verbosity: explain.verbosity };
535535
}
536536

537-
const emitProcessWarning = msg => process.emitWarning(msg, 'DeprecationWarning');
537+
const emitProcessWarning = msg =>
538+
process.emitWarning(msg, { type: 'DeprecationWarning', code: MONGODB_WARNING_CODE });
539+
// eslint-disable-next-line no-console
538540
const emitConsoleWarning = msg => console.error(msg);
539541
const emitDeprecationWarning = process.emitWarning ? emitProcessWarning : emitConsoleWarning;
540542

@@ -816,6 +818,48 @@ function hasAtomicOperators(doc) {
816818
);
817819
}
818820

821+
/**
822+
* When the driver used emitWarning the code will be equal to this.
823+
* @public
824+
*
825+
* @example
826+
* ```js
827+
* process.on('warning', (warning) => {
828+
* if (warning.code === MONGODB_WARNING_CODE) console.error('Ah an important warning! :)')
829+
* })
830+
* ```
831+
*/
832+
const MONGODB_WARNING_CODE = 'MONGODB DRIVER';
833+
834+
/**
835+
* @internal
836+
* @param {string} message - message to warn about
837+
*/
838+
function emitWarning(message) {
839+
if (process.emitWarning) {
840+
return process.emitWarning(message, { code: MONGODB_WARNING_CODE });
841+
} else {
842+
// Approximate the style of print out on node versions pre 8.x
843+
// eslint-disable-next-line no-console
844+
return console.error(`[${MONGODB_WARNING_CODE}] Warning:`, message);
845+
}
846+
}
847+
848+
const emittedWarnings = new Set();
849+
/**
850+
* Will emit a warning once for the duration of the application.
851+
* Uses the message to identify if it has already been emitted
852+
* so using string interpolation can cause multiple emits
853+
* @internal
854+
* @param {string} message - message to warn about
855+
*/
856+
function emitWarningOnce(message) {
857+
if (!emittedWarnings.has(message)) {
858+
emittedWarnings.add(message);
859+
return emitWarning(message);
860+
}
861+
}
862+
819863
module.exports = {
820864
filterOptions,
821865
mergeOptions,
@@ -849,5 +893,8 @@ module.exports = {
849893
now,
850894
calculateDurationInMs,
851895
makeInterruptableAsyncInterval,
852-
hasAtomicOperators
896+
hasAtomicOperators,
897+
MONGODB_WARNING_CODE,
898+
emitWarning,
899+
emitWarningOnce
853900
};

lib/write_concern.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use strict';
22

33
const kWriteConcernKeys = new Set(['w', 'wtimeout', 'j', 'journal', 'fsync']);
4+
let utils;
45

56
/**
67
* The **WriteConcern** class is a class that represents a MongoDB WriteConcern.
@@ -75,7 +76,10 @@ class WriteConcern {
7576
);
7677
}
7778

78-
console.warn(
79+
// this is down here to prevent circular dependency
80+
if (!utils) utils = require('./utils');
81+
82+
utils.emitWarningOnce(
7983
`Top-level use of w, wtimeout, j, and fsync is deprecated. Use writeConcern instead.`
8084
);
8185
return new WriteConcern(

test/.eslintrc.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"extends": "../.eslintrc.json",
3+
"rules": {
4+
"no-console": "off"
5+
}
6+
}

test/unit/sdam/server_selection/select_servers.test.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,6 @@ describe('selectServer', function() {
8585
let completed = 0;
8686
function finish() {
8787
completed++;
88-
console.log(completed);
8988
if (completed === toSelect) done();
9089
}
9190

0 commit comments

Comments
 (0)