Skip to content

Commit a26b009

Browse files
rebased changes
1 parent ed8eee3 commit a26b009

File tree

8 files changed

+125
-22
lines changed

8 files changed

+125
-22
lines changed

src/cmap/connection.ts

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import {
2626
MongoWriteConcernError
2727
} from '../error';
2828
import type { ServerApi, SupportedNodeConnectionOptions } from '../mongo_client';
29+
import { MongoLoggableComponent, type MongoLogger, SeverityLevel } from '../mongo_logger';
2930
import { type CancellationToken, TypedEventEmitter } from '../mongo_types';
3031
import type { ReadPreferenceLike } from '../read_preference';
3132
import { applySession, type ClientSession, updateSessionFromResponse } from '../sessions';
@@ -140,6 +141,7 @@ export interface ConnectionOptions
140141
socketTimeoutMS?: number;
141142
cancellationToken?: CancellationToken;
142143
metadata: ClientMetadata;
144+
mongoLogger: MongoLogger | undefined;
143145
}
144146

145147
/** @internal */
@@ -192,6 +194,10 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
192194
[kHello]: Document | null;
193195
/** @internal */
194196
[kClusterTime]: Document | null;
197+
/** @internal */
198+
override component = MongoLoggableComponent.COMMAND;
199+
/** @internal */
200+
override mongoLogger: MongoLogger | undefined;
195201

196202
/** @event */
197203
static readonly COMMAND_STARTED = COMMAND_STARTED;
@@ -221,6 +227,7 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
221227
this.closed = false;
222228
this[kHello] = null;
223229
this[kClusterTime] = null;
230+
this.mongoLogger = options.mongoLogger;
224231

225232
this[kDescription] = new StreamDescription(this.address, options);
226233
this[kGeneration] = options.generation;
@@ -717,9 +724,15 @@ function write(
717724
}
718725

719726
// if command monitoring is enabled we need to modify the callback here
720-
if (conn.monitorCommands) {
721-
conn.emit(
727+
if (
728+
conn.monitorCommands ||
729+
(typeof conn[kDescription].serverConnectionId === 'bigint' &&
730+
conn.mongoLogger?.willLog(SeverityLevel.DEBUG, conn.component))
731+
) {
732+
conn.emitAndLogCommand(
733+
conn.monitorCommands,
722734
Connection.COMMAND_STARTED,
735+
command.databaseName,
723736
new CommandStartedEvent(conn, command, conn[kDescription].serverConnectionId)
724737
);
725738

@@ -729,8 +742,10 @@ function write(
729742
// a command succeeded event, even if there's an error. Write concern errors
730743
// will have an ok: 1 in their reply.
731744
if (err && reply?.ok !== 1) {
732-
conn.emit(
745+
conn.emitAndLogCommand(
746+
conn.monitorCommands,
733747
Connection.COMMAND_FAILED,
748+
command.databaseName,
734749
new CommandFailedEvent(
735750
conn,
736751
command,
@@ -741,8 +756,10 @@ function write(
741756
);
742757
} else {
743758
if (reply && (reply.ok === 0 || reply.$err)) {
744-
conn.emit(
759+
conn.emitAndLogCommand(
760+
conn.monitorCommands,
745761
Connection.COMMAND_FAILED,
762+
command.databaseName,
746763
new CommandFailedEvent(
747764
conn,
748765
command,
@@ -752,8 +769,10 @@ function write(
752769
)
753770
);
754771
} else {
755-
conn.emit(
772+
conn.emitAndLogCommand(
773+
conn.monitorCommands,
756774
Connection.COMMAND_SUCCEEDED,
775+
command.databaseName,
757776
new CommandSucceededEvent(
758777
conn,
759778
command,

src/cmap/connection_pool.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -699,7 +699,8 @@ export class ConnectionPool extends TypedEventEmitter<ConnectionPoolEvents> {
699699
...this.options,
700700
id: this[kConnectionCounter].next().value,
701701
generation: this[kGeneration],
702-
cancellationToken: this[kCancellationToken]
702+
cancellationToken: this[kCancellationToken],
703+
mongoLogger: this[kServer].topology.client.mongoLogger
703704
};
704705

705706
this[kPending]++;

src/constants.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,11 @@ export const CONNECTION_CHECKED_OUT = 'connectionCheckedOut' as const;
5252
/** @internal */
5353
export const CONNECTION_CHECKED_IN = 'connectionCheckedIn' as const;
5454
export const CLUSTER_TIME_RECEIVED = 'clusterTimeReceived' as const;
55+
/** @internal */
5556
export const COMMAND_STARTED = 'commandStarted' as const;
57+
/** @internal */
5658
export const COMMAND_SUCCEEDED = 'commandSucceeded' as const;
59+
/** @internal */
5760
export const COMMAND_FAILED = 'commandFailed' as const;
5861
/** @internal */
5962
export const SERVER_HEARTBEAT_STARTED = 'serverHeartbeatStarted' as const;

src/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,9 @@ export type { StreamDescription, StreamDescriptionOptions } from './cmap/stream_
291291
export type { CompressorName } from './cmap/wire_protocol/compression';
292292
export type { CollectionOptions, CollectionPrivate, ModifyResult } from './collection';
293293
export type {
294+
COMMAND_FAILED,
295+
COMMAND_STARTED,
296+
COMMAND_SUCCEEDED,
294297
CONNECTION_CHECK_OUT_FAILED,
295298
CONNECTION_CHECK_OUT_STARTED,
296299
CONNECTION_CHECKED_IN,
@@ -362,6 +365,8 @@ export type {
362365
LogComponentSeveritiesClientOptions,
363366
LogConvertible,
364367
Loggable,
368+
LoggableCommandFailedEvent,
369+
LoggableCommandSucceededEvent,
365370
LoggableEvent,
366371
LoggableServerHeartbeatFailedEvent,
367372
LoggableServerHeartbeatStartedEvent,

src/mongo_logger.ts

Lines changed: 50 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
1-
import { type Document, EJSON, type EJSONOptions } from 'bson';
1+
import { type Document, EJSON, type EJSONOptions, type ObjectId } from 'bson';
22
import type { Writable } from 'stream';
33
import { inspect } from 'util';
44

5-
import type {
6-
CommandFailedEvent,
7-
CommandStartedEvent,
8-
CommandSucceededEvent
9-
} from './cmap/command_monitoring_events';
5+
import type { CommandStartedEvent } from './cmap/command_monitoring_events';
106
import type {
117
ConnectionCheckedInEvent,
128
ConnectionCheckedOutEvent,
@@ -285,6 +281,40 @@ function compareSeverity(s0: SeverityLevel, s1: SeverityLevel): 1 | 0 | -1 {
285281
return s0Num < s1Num ? -1 : s0Num > s1Num ? 1 : 0;
286282
}
287283

284+
/**
285+
* @internal
286+
* Must be separate from Events API due to differences in spec requirements for logging a command success
287+
*/
288+
export type LoggableCommandSucceededEvent = {
289+
address: string;
290+
connectionId?: string | number;
291+
requestId: number;
292+
duration: number;
293+
commandName: string;
294+
reply: Document | undefined;
295+
serviceId?: ObjectId;
296+
name: typeof COMMAND_SUCCEEDED;
297+
serverConnectionId: bigint | null;
298+
databaseName: string;
299+
};
300+
301+
/**
302+
* @internal
303+
* Must be separate from Events API due to differences in spec requirements for logging a command failure
304+
*/
305+
export type LoggableCommandFailedEvent = {
306+
address: string;
307+
connectionId?: string | number;
308+
requestId: number;
309+
duration: number;
310+
commandName: string;
311+
failure: Error;
312+
serviceId?: ObjectId;
313+
name: typeof COMMAND_FAILED;
314+
serverConnectionId: bigint | null;
315+
databaseName: string;
316+
};
317+
288318
/**
289319
* @internal
290320
* Must be separate from Events API due to differences in spec requirements for logging server heartbeat beginning
@@ -336,8 +366,8 @@ type SDAMLoggableEvent =
336366
/** @internal */
337367
export type LoggableEvent =
338368
| CommandStartedEvent
339-
| CommandSucceededEvent
340-
| CommandFailedEvent
369+
| LoggableCommandSucceededEvent
370+
| LoggableCommandFailedEvent
341371
| ConnectionPoolCreatedEvent
342372
| ConnectionPoolReadyEvent
343373
| ConnectionPoolClosedEvent
@@ -387,7 +417,7 @@ function isLogConvertible(obj: Loggable): obj is LogConvertible {
387417

388418
function attachCommandFields(
389419
log: Record<string, any>,
390-
commandEvent: CommandStartedEvent | CommandSucceededEvent | CommandFailedEvent
420+
commandEvent: CommandStartedEvent | LoggableCommandSucceededEvent | LoggableCommandFailedEvent
391421
) {
392422
log.commandName = commandEvent.commandName;
393423
log.requestId = commandEvent.requestId;
@@ -398,6 +428,8 @@ function attachCommandFields(
398428
if (commandEvent?.serviceId) {
399429
log.serviceId = commandEvent.serviceId.toHexString();
400430
}
431+
log.databaseName = commandEvent.databaseName;
432+
log.serverConnectionId = commandEvent?.serverConnectionId;
401433

402434
return log;
403435
}
@@ -444,20 +476,20 @@ function defaultLogTransform(
444476
case COMMAND_STARTED:
445477
log = attachCommandFields(log, logObject);
446478
log.message = 'Command started';
447-
log.command = stringifyWithMaxLen(logObject.command, maxDocumentLength);
479+
log.command = stringifyWithMaxLen(logObject.command, maxDocumentLength, { relaxed: true });
448480
log.databaseName = logObject.databaseName;
449481
return log;
450482
case COMMAND_SUCCEEDED:
451483
log = attachCommandFields(log, logObject);
452484
log.message = 'Command succeeded';
453485
log.durationMS = logObject.duration;
454-
log.reply = stringifyWithMaxLen(logObject.reply, maxDocumentLength);
486+
log.reply = stringifyWithMaxLen(logObject.reply, maxDocumentLength, { relaxed: true });
455487
return log;
456488
case COMMAND_FAILED:
457489
log = attachCommandFields(log, logObject);
458490
log.message = 'Command failed';
459491
log.durationMS = logObject.duration;
460-
log.failure = logObject.failure;
492+
log.failure = logObject.failure.message;
461493
return log;
462494
case CONNECTION_POOL_CREATED:
463495
log = attachConnectionFields(log, logObject);
@@ -655,12 +687,16 @@ export class MongoLogger {
655687
this.logDestination = options.logDestination;
656688
}
657689

690+
willLog(severity: SeverityLevel, component: MongoLoggableComponent): boolean {
691+
return compareSeverity(severity, this.componentSeverities[component]) <= 0;
692+
}
693+
658694
private log(
659695
severity: SeverityLevel,
660696
component: MongoLoggableComponent,
661697
message: Loggable | string
662698
): void {
663-
if (compareSeverity(severity, this.componentSeverities[component]) > 0) return;
699+
if (!this.willLog(severity, component)) return;
664700

665701
let logMessage: Log = { t: new Date(), c: component, s: severity };
666702
if (typeof message === 'string') {
@@ -669,6 +705,7 @@ export class MongoLogger {
669705
if (isLogConvertible(message)) {
670706
logMessage = { ...logMessage, ...message.toLog() };
671707
} else {
708+
672709
logMessage = { ...logMessage, ...defaultLogTransform(message, this.maxDocumentLength) };
673710
}
674711
}

src/mongo_types.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@ import type {
1212
ObjectId,
1313
Timestamp
1414
} from './bson';
15+
import { type CommandStartedEvent } from './cmap/command_monitoring_events';
1516
import type {
17+
LoggableCommandFailedEvent,
18+
LoggableCommandSucceededEvent,
1619
LoggableServerHeartbeatFailedEvent,
1720
LoggableServerHeartbeatStartedEvent,
1821
LoggableServerHeartbeatSucceededEvent,
@@ -438,6 +441,27 @@ export class TypedEventEmitter<Events extends EventsDescription> extends EventEm
438441
this.mongoLogger?.debug(this.component, loggableHeartbeatEvent);
439442
}
440443
}
444+
/** @internal */
445+
emitAndLogCommand<EventKey extends keyof Events>(
446+
monitorCommands: boolean,
447+
event: EventKey | symbol,
448+
databaseName: string,
449+
...args: Parameters<Events[EventKey]>
450+
): void {
451+
if (monitorCommands) {
452+
this.emit(event, ...args);
453+
}
454+
if (this.component && typeof args[0]?.serverConnectionId === 'bigint') {
455+
const loggableCommandEvent:
456+
| CommandStartedEvent
457+
| LoggableCommandFailedEvent
458+
| LoggableCommandSucceededEvent = {
459+
databaseName: databaseName,
460+
...args[0]
461+
};
462+
this.mongoLogger?.debug(this.component, loggableCommandEvent);
463+
}
464+
}
441465
}
442466

443467
/** @public */

test/integration/command-logging-and-monitoring/command_logging_and_monitoring.spec.test.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,18 @@ describe('Command Logging and Monitoring Spec', function () {
2222
);
2323
});
2424

25-
describe.skip('Command Logging Spec', () => {
26-
runUnifiedSuite(loadSpecTests(path.join('command-logging-and-monitoring', 'logging')));
27-
}).skipReason = 'TODO(NODE-4686): Unskip these tests';
25+
describe.only('Command Logging Spec', () => {
26+
const tests = loadSpecTests(path.join('command-logging-and-monitoring', 'logging'));
27+
runUnifiedSuite(tests, test => {
28+
if (
29+
[
30+
'Successful bulk write command log messages include operationIds',
31+
'Failed bulk write command log message includes operationId'
32+
].includes(test.description)
33+
) {
34+
return 'not applicable: operationId not supported';
35+
}
36+
return false;
37+
});
38+
});
2839
});

test/tools/unified-spec-runner/match.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -675,6 +675,9 @@ export function compareLogs(
675675
actual: ExpectedLogMessage[],
676676
entities: EntitiesMap
677677
): void {
678+
console.log('ACTUAL', actual);
679+
console.log('EXPECTED', expected);
680+
678681
expect(actual).to.have.lengthOf(expected.length);
679682

680683
for (const [index, actualLog] of actual.entries()) {

0 commit comments

Comments
 (0)