Skip to content

Commit d1b2453

Browse files
authored
feat(NODE-5672): support standardized logging (#4387)
1 parent 6b15f20 commit d1b2453

14 files changed

+115
-302
lines changed

.evergreen/config.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -3517,7 +3517,7 @@ tasks:
35173517
- {key: VERSION, value: v6.0-perf}
35183518
- {key: TOPOLOGY, value: server}
35193519
- {key: AUTH, value: noauth}
3520-
- {key: MONGODB_CLIENT_OPTIONS, value: '{"__enableMongoLogger":true,"__internalLoggerConfig":{"MONGODB_LOG_ALL":"trace","MONGODB_LOG_PATH":"stderr"}}'}
3520+
- {key: MONGODB_CLIENT_OPTIONS, value: '{"mongodbLogPath":"stderr","mongodbLogComponentSeverities":{"default":"trace"}}'}
35213521
- func: install dependencies
35223522
- func: bootstrap mongo-orchestration
35233523
- func: run spec driver benchmarks

.evergreen/generate_evergreen_tasks.js

+3-6
Original file line numberDiff line numberDiff line change
@@ -564,7 +564,7 @@ SINGLETON_TASKS.push(
564564
updateExpansions({
565565
VERSION: 'latest',
566566
TOPOLOGY: 'replica_set',
567-
NODE_LTS_VERSION: LATEST_LTS
567+
NODE_LTS_VERSION: LATEST_LTS
568568
}),
569569
{ func: 'install dependencies' },
570570
{ func: 'bootstrap mongo-orchestration' },
@@ -750,11 +750,8 @@ function addPerformanceTasks() {
750750
monitorCommands: true
751751
}),
752752
makePerfTask('run-spec-benchmark-tests-node-server-logging', {
753-
__enableMongoLogger: true,
754-
__internalLoggerConfig: {
755-
MONGODB_LOG_ALL: 'trace',
756-
MONGODB_LOG_PATH: 'stderr'
757-
}
753+
mongodbLogPath: 'stderr',
754+
mongodbLogComponentSeverities: { default: 'trace' }
758755
})
759756
];
760757

.evergreen/run-benchmarks.sh

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ export MONGODB_CLIENT_OPTIONS=$MONGODB_CLIENT_OPTIONS
1212
npm run build:ts
1313

1414

15-
# If MONGODB_CLIENT_OPTIONS contains __enableMongoLogger redirect stderr to a file
16-
if [[ $MONGODB_CLIENT_OPTIONS == *"__enableMongoLogger"* ]]; then
15+
# If MONGODB_CLIENT_OPTIONS contains mongodbLogComponentSeverities redirect stderr to a file
16+
if [[ $MONGODB_CLIENT_OPTIONS == *"mongodbLogComponentSeverities"* ]]; then
1717
npm run check:bench 2> bench.log
1818
else
1919
npm run check:bench

src/connection_string.ts

+49-79
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,7 @@ import {
2222
type ServerApi,
2323
ServerApiVersion
2424
} from './mongo_client';
25-
import {
26-
MongoLoggableComponent,
27-
MongoLogger,
28-
type MongoLoggerEnvOptions,
29-
type MongoLoggerMongoClientOptions,
30-
SeverityLevel
31-
} from './mongo_logger';
25+
import { MongoLoggableComponent, MongoLogger, SeverityLevel } from './mongo_logger';
3226
import { ReadConcern, type ReadConcernLevel } from './read_concern';
3327
import { ReadPreference, type ReadPreferenceMode } from './read_preference';
3428
import { ServerMonitoringMode } from './sdam/monitor';
@@ -528,31 +522,22 @@ export function parseOptions(
528522
);
529523
}
530524

531-
mongoOptions.__enableMongoLogger = mongoOptions.__enableMongoLogger ?? false;
532-
533-
let loggerEnvOptions: MongoLoggerEnvOptions = {};
534-
let loggerClientOptions: MongoLoggerMongoClientOptions = {};
535-
if (mongoOptions.__enableMongoLogger) {
536-
loggerEnvOptions = {
525+
mongoOptions.mongoLoggerOptions = MongoLogger.resolveOptions(
526+
{
537527
MONGODB_LOG_COMMAND: process.env.MONGODB_LOG_COMMAND,
538528
MONGODB_LOG_TOPOLOGY: process.env.MONGODB_LOG_TOPOLOGY,
539529
MONGODB_LOG_SERVER_SELECTION: process.env.MONGODB_LOG_SERVER_SELECTION,
540530
MONGODB_LOG_CONNECTION: process.env.MONGODB_LOG_CONNECTION,
541531
MONGODB_LOG_CLIENT: process.env.MONGODB_LOG_CLIENT,
542532
MONGODB_LOG_ALL: process.env.MONGODB_LOG_ALL,
543533
MONGODB_LOG_MAX_DOCUMENT_LENGTH: process.env.MONGODB_LOG_MAX_DOCUMENT_LENGTH,
544-
MONGODB_LOG_PATH: process.env.MONGODB_LOG_PATH,
545-
...mongoOptions.__internalLoggerConfig
546-
};
547-
loggerClientOptions = {
534+
MONGODB_LOG_PATH: process.env.MONGODB_LOG_PATH
535+
},
536+
{
548537
mongodbLogPath: mongoOptions.mongodbLogPath,
549538
mongodbLogComponentSeverities: mongoOptions.mongodbLogComponentSeverities,
550539
mongodbLogMaxDocumentLength: mongoOptions.mongodbLogMaxDocumentLength
551-
};
552-
}
553-
mongoOptions.mongoLoggerOptions = MongoLogger.resolveOptions(
554-
loggerEnvOptions,
555-
loggerClientOptions
540+
}
556541
);
557542

558543
mongoOptions.metadata = makeClientMetadata(mongoOptions);
@@ -1232,52 +1217,6 @@ export const OPTIONS = {
12321217
default: 0,
12331218
type: 'int'
12341219
},
1235-
// Custom types for modifying core behavior
1236-
connectionType: { type: 'any' },
1237-
srvPoller: { type: 'any' },
1238-
// Accepted Node.js Options
1239-
allowPartialTrustChain: { type: 'any' },
1240-
minDHSize: { type: 'any' },
1241-
pskCallback: { type: 'any' },
1242-
secureContext: { type: 'any' },
1243-
enableTrace: { type: 'any' },
1244-
requestCert: { type: 'any' },
1245-
rejectUnauthorized: { type: 'any' },
1246-
checkServerIdentity: { type: 'any' },
1247-
ALPNProtocols: { type: 'any' },
1248-
SNICallback: { type: 'any' },
1249-
session: { type: 'any' },
1250-
requestOCSP: { type: 'any' },
1251-
localAddress: { type: 'any' },
1252-
localPort: { type: 'any' },
1253-
hints: { type: 'any' },
1254-
lookup: { type: 'any' },
1255-
ca: { type: 'any' },
1256-
cert: { type: 'any' },
1257-
ciphers: { type: 'any' },
1258-
crl: { type: 'any' },
1259-
ecdhCurve: { type: 'any' },
1260-
key: { type: 'any' },
1261-
passphrase: { type: 'any' },
1262-
pfx: { type: 'any' },
1263-
secureProtocol: { type: 'any' },
1264-
index: { type: 'any' },
1265-
// Legacy options from v3 era
1266-
useNewUrlParser: {
1267-
type: 'boolean',
1268-
deprecated:
1269-
'useNewUrlParser has no effect since Node.js Driver version 4.0.0 and will be removed in the next major version'
1270-
} as OptionDescriptor,
1271-
useUnifiedTopology: {
1272-
type: 'boolean',
1273-
deprecated:
1274-
'useUnifiedTopology has no effect since Node.js Driver version 4.0.0 and will be removed in the next major version'
1275-
} as OptionDescriptor,
1276-
// MongoLogger
1277-
/**
1278-
* @internal
1279-
* TODO: NODE-5671 - remove internal flag
1280-
*/
12811220
mongodbLogPath: {
12821221
transform({ values: [value] }) {
12831222
if (
@@ -1296,10 +1235,6 @@ export const OPTIONS = {
12961235
return value;
12971236
}
12981237
},
1299-
/**
1300-
* @internal
1301-
* TODO: NODE-5671 - remove internal flag
1302-
*/
13031238
mongodbLogComponentSeverities: {
13041239
transform({ values: [value] }) {
13051240
if (typeof value !== 'object' || !value) {
@@ -1325,14 +1260,49 @@ export const OPTIONS = {
13251260
return value;
13261261
}
13271262
},
1328-
/**
1329-
* @internal
1330-
* TODO: NODE-5671 - remove internal flag
1331-
*/
13321263
mongodbLogMaxDocumentLength: { type: 'uint' },
1333-
__enableMongoLogger: { type: 'boolean' },
1334-
__skipPingOnConnect: { type: 'boolean' },
1335-
__internalLoggerConfig: { type: 'record' }
1264+
// Custom types for modifying core behavior
1265+
connectionType: { type: 'any' },
1266+
srvPoller: { type: 'any' },
1267+
// Accepted Node.js Options
1268+
allowPartialTrustChain: { type: 'any' },
1269+
minDHSize: { type: 'any' },
1270+
pskCallback: { type: 'any' },
1271+
secureContext: { type: 'any' },
1272+
enableTrace: { type: 'any' },
1273+
requestCert: { type: 'any' },
1274+
rejectUnauthorized: { type: 'any' },
1275+
checkServerIdentity: { type: 'any' },
1276+
ALPNProtocols: { type: 'any' },
1277+
SNICallback: { type: 'any' },
1278+
session: { type: 'any' },
1279+
requestOCSP: { type: 'any' },
1280+
localAddress: { type: 'any' },
1281+
localPort: { type: 'any' },
1282+
hints: { type: 'any' },
1283+
lookup: { type: 'any' },
1284+
ca: { type: 'any' },
1285+
cert: { type: 'any' },
1286+
ciphers: { type: 'any' },
1287+
crl: { type: 'any' },
1288+
ecdhCurve: { type: 'any' },
1289+
key: { type: 'any' },
1290+
passphrase: { type: 'any' },
1291+
pfx: { type: 'any' },
1292+
secureProtocol: { type: 'any' },
1293+
index: { type: 'any' },
1294+
// Legacy options from v3 era
1295+
useNewUrlParser: {
1296+
type: 'boolean',
1297+
deprecated:
1298+
'useNewUrlParser has no effect since Node.js Driver version 4.0.0 and will be removed in the next major version'
1299+
} as OptionDescriptor,
1300+
useUnifiedTopology: {
1301+
type: 'boolean',
1302+
deprecated:
1303+
'useUnifiedTopology has no effect since Node.js Driver version 4.0.0 and will be removed in the next major version'
1304+
} as OptionDescriptor,
1305+
__skipPingOnConnect: { type: 'boolean' }
13361306
} as Record<keyof MongoClientOptions, OptionDescriptor>;
13371307

13381308
export const DEFAULT_OPTIONS = new CaseInsensitiveMap(

src/index.ts

+2-3
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ export { CURSOR_FLAGS, CursorTimeoutMode } from './cursor/abstract_cursor';
120120
export { MongoErrorLabel } from './error';
121121
export { ExplainVerbosity } from './explain';
122122
export { ServerApiVersion } from './mongo_client';
123+
export { MongoLoggableComponent, SeverityLevel } from './mongo_logger';
123124
export { ReturnDocument } from './operations/find_and_modify';
124125
export { ProfilingLevel } from './operations/set_profiling_level';
125126
export { ReadConcernLevel } from './read_concern';
@@ -422,12 +423,10 @@ export type {
422423
LoggableServerHeartbeatStartedEvent,
423424
LoggableServerHeartbeatSucceededEvent,
424425
MongoDBLogWritable,
425-
MongoLoggableComponent,
426426
MongoLogger,
427427
MongoLoggerEnvOptions,
428428
MongoLoggerMongoClientOptions,
429-
MongoLoggerOptions,
430-
SeverityLevel
429+
MongoLoggerOptions
431430
} from './mongo_logger';
432431
export type {
433432
Abortable,

src/mongo_client.ts

+10-19
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ import {
2727
type LogComponentSeveritiesClientOptions,
2828
type MongoDBLogWritable,
2929
MongoLogger,
30-
type MongoLoggerEnvOptions,
3130
type MongoLoggerOptions,
3231
SeverityLevel
3332
} from './mongo_logger';
@@ -279,33 +278,29 @@ export interface MongoClientOptions extends BSONSerializeOptions, SupportedNodeC
279278
proxyPassword?: string;
280279
/** Instructs the driver monitors to use a specific monitoring mode */
281280
serverMonitoringMode?: ServerMonitoringMode;
282-
283-
/** @internal */
284-
srvPoller?: SrvPoller;
285-
/** @internal */
286-
connectionType?: typeof Connection;
287281
/**
288-
* @internal
289-
* TODO: NODE-5671 - remove internal flag
282+
* @public
283+
* Specifies the destination of the driver's logging. The default is stderr.
290284
*/
291285
mongodbLogPath?: 'stderr' | 'stdout' | MongoDBLogWritable;
292286
/**
293-
* @internal
294-
* TODO: NODE-5671 - remove internal flag
287+
* @public
288+
* Enable logging level per component or use `default` to control any unset components.
295289
*/
296290
mongodbLogComponentSeverities?: LogComponentSeveritiesClientOptions;
297291
/**
298-
* @internal
299-
* TODO: NODE-5671 - remove internal flag
292+
* @public
293+
* All BSON documents are stringified to EJSON. This controls the maximum length of those strings.
294+
* It is defaulted to 1000.
300295
*/
301296
mongodbLogMaxDocumentLength?: number;
302297

303298
/** @internal */
304-
__skipPingOnConnect?: boolean;
299+
srvPoller?: SrvPoller;
305300
/** @internal */
306-
__internalLoggerConfig?: MongoLoggerEnvOptions;
301+
connectionType?: typeof Connection;
307302
/** @internal */
308-
__enableMongoLogger?: boolean;
303+
__skipPingOnConnect?: boolean;
309304
}
310305

311306
/** @public */
@@ -1062,8 +1057,4 @@ export interface MongoOptions
10621057
timeoutMS?: number;
10631058
/** @internal */
10641059
__skipPingOnConnect?: boolean;
1065-
/** @internal */
1066-
__internalLoggerConfig?: Document;
1067-
/** @internal */
1068-
__enableMongoLogger?: boolean;
10691060
}

src/mongo_logger.ts

+30-9
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,11 @@ import type {
7777
} from './sdam/server_selection_events';
7878
import { HostAddress, isPromiseLike, parseUnsignedInteger } from './utils';
7979

80-
/** @internal */
80+
/**
81+
* @public
82+
* Severity levels align with unix syslog.
83+
* Most typical driver functions will log to debug.
84+
*/
8185
export const SeverityLevel = Object.freeze({
8286
EMERGENCY: 'emergency',
8387
ALERT: 'alert',
@@ -93,7 +97,7 @@ export const SeverityLevel = Object.freeze({
9397

9498
/** @internal */
9599
export const DEFAULT_MAX_DOCUMENT_LENGTH = 1000;
96-
/** @internal */
100+
/** @public */
97101
export type SeverityLevel = (typeof SeverityLevel)[keyof typeof SeverityLevel];
98102

99103
/** @internal */
@@ -131,7 +135,7 @@ export const SEVERITY_LEVEL_MAP = new SeverityLevelMap([
131135
[SeverityLevel.TRACE, 8]
132136
]);
133137

134-
/** @internal */
138+
/** @public */
135139
export const MongoLoggableComponent = Object.freeze({
136140
COMMAND: 'command',
137141
TOPOLOGY: 'topology',
@@ -140,7 +144,7 @@ export const MongoLoggableComponent = Object.freeze({
140144
CLIENT: 'client'
141145
} as const);
142146

143-
/** @internal */
147+
/** @public */
144148
export type MongoLoggableComponent =
145149
(typeof MongoLoggableComponent)[keyof typeof MongoLoggableComponent];
146150

@@ -164,13 +168,13 @@ export interface MongoLoggerEnvOptions {
164168
MONGODB_LOG_PATH?: string;
165169
}
166170

167-
/** @internal */
171+
/** @public */
168172
export interface LogComponentSeveritiesClientOptions {
169173
/** Optional severity level for command component */
170174
command?: SeverityLevel;
171175
/** Optional severity level for topology component */
172176
topology?: SeverityLevel;
173-
/** Optionsl severity level for server selection component */
177+
/** Optional severity level for server selection component */
174178
serverSelection?: SeverityLevel;
175179
/** Optional severity level for connection component */
176180
connection?: SeverityLevel;
@@ -292,7 +296,7 @@ function resolveSeverityConfiguration(
292296
);
293297
}
294298

295-
/** @internal */
299+
/** @public */
296300
export interface Log extends Record<string, any> {
297301
t: Date;
298302
c: MongoLoggableComponent;
@@ -301,10 +305,27 @@ export interface Log extends Record<string, any> {
301305
}
302306

303307
/**
304-
* @internal
305-
* TODO: NODE-5671 - remove internal flag and add API comments
308+
* @public
309+
*
310+
* A custom destination for structured logging messages.
306311
*/
307312
export interface MongoDBLogWritable {
313+
/**
314+
* This function will be called for every enabled log message.
315+
*
316+
* It can be sync or async:
317+
* - If it is synchronous it will block the driver from proceeding until this method returns.
318+
* - If it is asynchronous the driver will not await the returned promise. It will attach fulfillment handling (`.then`).
319+
* If the promise rejects the logger will write an error message to stderr and stop functioning.
320+
* If the promise resolves the driver proceeds to the next log message (or waits for new ones to occur).
321+
*
322+
* Tips:
323+
* - We recommend writing an async `write` function that _never_ rejects.
324+
* Instead handle logging errors as necessary to your use case and make the write function a noop, until it can be recovered.
325+
* - The Log messages are structured but **subject to change** since the intended purpose is informational.
326+
* Program against this defensively and err on the side of stringifying whatever is passed in to write in some form or another.
327+
*
328+
*/
308329
write(log: Log): PromiseLike<unknown> | unknown;
309330
}
310331

0 commit comments

Comments
 (0)