Skip to content

Commit

Permalink
feat(NODE-5672): support standardized logging
Browse files Browse the repository at this point in the history
  • Loading branch information
nbbeeken committed Jan 29, 2025
1 parent 6b15f20 commit a515dd7
Show file tree
Hide file tree
Showing 13 changed files with 112 additions and 301 deletions.
2 changes: 1 addition & 1 deletion .evergreen/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3517,7 +3517,7 @@ tasks:
- {key: VERSION, value: v6.0-perf}
- {key: TOPOLOGY, value: server}
- {key: AUTH, value: noauth}
- {key: MONGODB_CLIENT_OPTIONS, value: '{"__enableMongoLogger":true,"__internalLoggerConfig":{"MONGODB_LOG_ALL":"trace","MONGODB_LOG_PATH":"stderr"}}'}
- {key: MONGODB_CLIENT_OPTIONS, value: '{"mongodbLogPath":"stderr","mongodbLogComponentSeverities":{"default":"trace"}}'}
- func: install dependencies
- func: bootstrap mongo-orchestration
- func: run spec driver benchmarks
Expand Down
9 changes: 3 additions & 6 deletions .evergreen/generate_evergreen_tasks.js
Original file line number Diff line number Diff line change
Expand Up @@ -564,7 +564,7 @@ SINGLETON_TASKS.push(
updateExpansions({
VERSION: 'latest',
TOPOLOGY: 'replica_set',
NODE_LTS_VERSION: LATEST_LTS
NODE_LTS_VERSION: LATEST_LTS
}),
{ func: 'install dependencies' },
{ func: 'bootstrap mongo-orchestration' },
Expand Down Expand Up @@ -750,11 +750,8 @@ function addPerformanceTasks() {
monitorCommands: true
}),
makePerfTask('run-spec-benchmark-tests-node-server-logging', {
__enableMongoLogger: true,
__internalLoggerConfig: {
MONGODB_LOG_ALL: 'trace',
MONGODB_LOG_PATH: 'stderr'
}
mongodbLogPath: 'stderr',
mongodbLogComponentSeverities: { default: 'trace' }
})
];

Expand Down
4 changes: 2 additions & 2 deletions .evergreen/run-benchmarks.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ export MONGODB_CLIENT_OPTIONS=$MONGODB_CLIENT_OPTIONS
npm run build:ts


# If MONGODB_CLIENT_OPTIONS contains __enableMongoLogger redirect stderr to a file
if [[ $MONGODB_CLIENT_OPTIONS == *"__enableMongoLogger"* ]]; then
# If MONGODB_CLIENT_OPTIONS contains mongodbLogComponentSeverities redirect stderr to a file
if [[ $MONGODB_CLIENT_OPTIONS == *"mongodbLogComponentSeverities"* ]]; then
npm run check:bench 2> bench.log
else
npm run check:bench
Expand Down
128 changes: 49 additions & 79 deletions src/connection_string.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,7 @@ import {
type ServerApi,
ServerApiVersion
} from './mongo_client';
import {
MongoLoggableComponent,
MongoLogger,
type MongoLoggerEnvOptions,
type MongoLoggerMongoClientOptions,
SeverityLevel
} from './mongo_logger';
import { MongoLoggableComponent, MongoLogger, SeverityLevel } from './mongo_logger';
import { ReadConcern, type ReadConcernLevel } from './read_concern';
import { ReadPreference, type ReadPreferenceMode } from './read_preference';
import { ServerMonitoringMode } from './sdam/monitor';
Expand Down Expand Up @@ -528,31 +522,22 @@ export function parseOptions(
);
}

mongoOptions.__enableMongoLogger = mongoOptions.__enableMongoLogger ?? false;

let loggerEnvOptions: MongoLoggerEnvOptions = {};
let loggerClientOptions: MongoLoggerMongoClientOptions = {};
if (mongoOptions.__enableMongoLogger) {
loggerEnvOptions = {
mongoOptions.mongoLoggerOptions = MongoLogger.resolveOptions(
{
MONGODB_LOG_COMMAND: process.env.MONGODB_LOG_COMMAND,
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,
...mongoOptions.__internalLoggerConfig
};
loggerClientOptions = {
MONGODB_LOG_PATH: process.env.MONGODB_LOG_PATH
},
{
mongodbLogPath: mongoOptions.mongodbLogPath,
mongodbLogComponentSeverities: mongoOptions.mongodbLogComponentSeverities,
mongodbLogMaxDocumentLength: mongoOptions.mongodbLogMaxDocumentLength
};
}
mongoOptions.mongoLoggerOptions = MongoLogger.resolveOptions(
loggerEnvOptions,
loggerClientOptions
}
);

mongoOptions.metadata = makeClientMetadata(mongoOptions);
Expand Down Expand Up @@ -1232,52 +1217,6 @@ export const OPTIONS = {
default: 0,
type: 'int'
},
// Custom types for modifying core behavior
connectionType: { type: 'any' },
srvPoller: { type: 'any' },
// Accepted Node.js Options
allowPartialTrustChain: { type: 'any' },
minDHSize: { type: 'any' },
pskCallback: { type: 'any' },
secureContext: { type: 'any' },
enableTrace: { type: 'any' },
requestCert: { type: 'any' },
rejectUnauthorized: { type: 'any' },
checkServerIdentity: { type: 'any' },
ALPNProtocols: { type: 'any' },
SNICallback: { type: 'any' },
session: { type: 'any' },
requestOCSP: { type: 'any' },
localAddress: { type: 'any' },
localPort: { type: 'any' },
hints: { type: 'any' },
lookup: { type: 'any' },
ca: { type: 'any' },
cert: { type: 'any' },
ciphers: { type: 'any' },
crl: { type: 'any' },
ecdhCurve: { type: 'any' },
key: { type: 'any' },
passphrase: { type: 'any' },
pfx: { type: 'any' },
secureProtocol: { type: 'any' },
index: { type: 'any' },
// Legacy options from v3 era
useNewUrlParser: {
type: 'boolean',
deprecated:
'useNewUrlParser has no effect since Node.js Driver version 4.0.0 and will be removed in the next major version'
} as OptionDescriptor,
useUnifiedTopology: {
type: 'boolean',
deprecated:
'useUnifiedTopology has no effect since Node.js Driver version 4.0.0 and will be removed in the next major version'
} as OptionDescriptor,
// MongoLogger
/**
* @internal
* TODO: NODE-5671 - remove internal flag
*/
mongodbLogPath: {
transform({ values: [value] }) {
if (
Expand All @@ -1296,10 +1235,6 @@ export const OPTIONS = {
return value;
}
},
/**
* @internal
* TODO: NODE-5671 - remove internal flag
*/
mongodbLogComponentSeverities: {
transform({ values: [value] }) {
if (typeof value !== 'object' || !value) {
Expand All @@ -1325,14 +1260,49 @@ export const OPTIONS = {
return value;
}
},
/**
* @internal
* TODO: NODE-5671 - remove internal flag
*/
mongodbLogMaxDocumentLength: { type: 'uint' },
__enableMongoLogger: { type: 'boolean' },
__skipPingOnConnect: { type: 'boolean' },
__internalLoggerConfig: { type: 'record' }
// Custom types for modifying core behavior
connectionType: { type: 'any' },
srvPoller: { type: 'any' },
// Accepted Node.js Options
allowPartialTrustChain: { type: 'any' },
minDHSize: { type: 'any' },
pskCallback: { type: 'any' },
secureContext: { type: 'any' },
enableTrace: { type: 'any' },
requestCert: { type: 'any' },
rejectUnauthorized: { type: 'any' },
checkServerIdentity: { type: 'any' },
ALPNProtocols: { type: 'any' },
SNICallback: { type: 'any' },
session: { type: 'any' },
requestOCSP: { type: 'any' },
localAddress: { type: 'any' },
localPort: { type: 'any' },
hints: { type: 'any' },
lookup: { type: 'any' },
ca: { type: 'any' },
cert: { type: 'any' },
ciphers: { type: 'any' },
crl: { type: 'any' },
ecdhCurve: { type: 'any' },
key: { type: 'any' },
passphrase: { type: 'any' },
pfx: { type: 'any' },
secureProtocol: { type: 'any' },
index: { type: 'any' },
// Legacy options from v3 era
useNewUrlParser: {
type: 'boolean',
deprecated:
'useNewUrlParser has no effect since Node.js Driver version 4.0.0 and will be removed in the next major version'
} as OptionDescriptor,
useUnifiedTopology: {
type: 'boolean',
deprecated:
'useUnifiedTopology has no effect since Node.js Driver version 4.0.0 and will be removed in the next major version'
} as OptionDescriptor,
__skipPingOnConnect: { type: 'boolean' }
} as Record<keyof MongoClientOptions, OptionDescriptor>;

export const DEFAULT_OPTIONS = new CaseInsensitiveMap(
Expand Down
5 changes: 2 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ export { CURSOR_FLAGS, CursorTimeoutMode } from './cursor/abstract_cursor';
export { MongoErrorLabel } from './error';
export { ExplainVerbosity } from './explain';
export { ServerApiVersion } from './mongo_client';
export { MongoLoggableComponent, SeverityLevel } from './mongo_logger';
export { ReturnDocument } from './operations/find_and_modify';
export { ProfilingLevel } from './operations/set_profiling_level';
export { ReadConcernLevel } from './read_concern';
Expand Down Expand Up @@ -422,12 +423,10 @@ export type {
LoggableServerHeartbeatStartedEvent,
LoggableServerHeartbeatSucceededEvent,
MongoDBLogWritable,
MongoLoggableComponent,
MongoLogger,
MongoLoggerEnvOptions,
MongoLoggerMongoClientOptions,
MongoLoggerOptions,
SeverityLevel
MongoLoggerOptions
} from './mongo_logger';
export type {
Abortable,
Expand Down
29 changes: 10 additions & 19 deletions src/mongo_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import {
type LogComponentSeveritiesClientOptions,
type MongoDBLogWritable,
MongoLogger,
type MongoLoggerEnvOptions,
type MongoLoggerOptions,
SeverityLevel
} from './mongo_logger';
Expand Down Expand Up @@ -279,33 +278,29 @@ export interface MongoClientOptions extends BSONSerializeOptions, SupportedNodeC
proxyPassword?: string;
/** Instructs the driver monitors to use a specific monitoring mode */
serverMonitoringMode?: ServerMonitoringMode;

/** @internal */
srvPoller?: SrvPoller;
/** @internal */
connectionType?: typeof Connection;
/**
* @internal
* TODO: NODE-5671 - remove internal flag
* @public
* Specifies the destination of the driver's logging. The default is stderr.
*/
mongodbLogPath?: 'stderr' | 'stdout' | MongoDBLogWritable;
/**
* @internal
* TODO: NODE-5671 - remove internal flag
* @public
* Enable logging level per component or use `default` to control any unset components.
*/
mongodbLogComponentSeverities?: LogComponentSeveritiesClientOptions;
/**
* @internal
* TODO: NODE-5671 - remove internal flag
* @public
* All BSON documents are stringified to EJSON. This controls the maximum length of those strings.
* It is defaulted to 1000.
*/
mongodbLogMaxDocumentLength?: number;

/** @internal */
__skipPingOnConnect?: boolean;
srvPoller?: SrvPoller;
/** @internal */
__internalLoggerConfig?: MongoLoggerEnvOptions;
connectionType?: typeof Connection;
/** @internal */
__enableMongoLogger?: boolean;
__skipPingOnConnect?: boolean;
}

/** @public */
Expand Down Expand Up @@ -1062,8 +1057,4 @@ export interface MongoOptions
timeoutMS?: number;
/** @internal */
__skipPingOnConnect?: boolean;
/** @internal */
__internalLoggerConfig?: Document;
/** @internal */
__enableMongoLogger?: boolean;
}
39 changes: 30 additions & 9 deletions src/mongo_logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,11 @@ import type {
} from './sdam/server_selection_events';
import { HostAddress, isPromiseLike, parseUnsignedInteger } from './utils';

/** @internal */
/**
* @public
* Severity levels align with unix syslog.
* Most typical driver functions will log to debug.
*/
export const SeverityLevel = Object.freeze({
EMERGENCY: 'emergency',
ALERT: 'alert',
Expand All @@ -93,7 +97,7 @@ export const SeverityLevel = Object.freeze({

/** @internal */
export const DEFAULT_MAX_DOCUMENT_LENGTH = 1000;
/** @internal */
/** @public */
export type SeverityLevel = (typeof SeverityLevel)[keyof typeof SeverityLevel];

/** @internal */
Expand Down Expand Up @@ -131,7 +135,7 @@ export const SEVERITY_LEVEL_MAP = new SeverityLevelMap([
[SeverityLevel.TRACE, 8]
]);

/** @internal */
/** @public */
export const MongoLoggableComponent = Object.freeze({
COMMAND: 'command',
TOPOLOGY: 'topology',
Expand All @@ -140,7 +144,7 @@ export const MongoLoggableComponent = Object.freeze({
CLIENT: 'client'
} as const);

/** @internal */
/** @public */
export type MongoLoggableComponent =
(typeof MongoLoggableComponent)[keyof typeof MongoLoggableComponent];

Expand All @@ -164,13 +168,13 @@ export interface MongoLoggerEnvOptions {
MONGODB_LOG_PATH?: string;
}

/** @internal */
/** @public */
export interface LogComponentSeveritiesClientOptions {
/** Optional severity level for command component */
command?: SeverityLevel;
/** Optional severity level for topology component */
topology?: SeverityLevel;
/** Optionsl severity level for server selection component */
/** Optional severity level for server selection component */
serverSelection?: SeverityLevel;
/** Optional severity level for connection component */
connection?: SeverityLevel;
Expand Down Expand Up @@ -292,7 +296,7 @@ function resolveSeverityConfiguration(
);
}

/** @internal */
/** @public */
export interface Log extends Record<string, any> {
t: Date;
c: MongoLoggableComponent;
Expand All @@ -301,10 +305,27 @@ export interface Log extends Record<string, any> {
}

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

Expand Down
Loading

0 comments on commit a515dd7

Please sign in to comment.