Skip to content

Commit 6d32c32

Browse files
rebased changes
1 parent efece79 commit 6d32c32

File tree

8 files changed

+103
-17
lines changed

8 files changed

+103
-17
lines changed

src/cmap/connection.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {
2424
MongoWriteConcernError
2525
} from '../error';
2626
import type { ServerApi, SupportedNodeConnectionOptions } from '../mongo_client';
27+
import { MongoLoggableComponent, type MongoLogger, SeverityLevel } from '../mongo_logger';
2728
import { type CancellationToken, TypedEventEmitter } from '../mongo_types';
2829
import type { ReadPreferenceLike } from '../read_preference';
2930
import { applySession, type ClientSession, updateSessionFromResponse } from '../sessions';
@@ -114,6 +115,7 @@ export interface ConnectionOptions
114115
socketTimeoutMS?: number;
115116
cancellationToken?: CancellationToken;
116117
metadata: ClientMetadata;
118+
mongoLogger: MongoLogger | undefined;
117119
}
118120

119121
/** @internal */

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
@@ -285,6 +285,9 @@ export type { StreamDescription, StreamDescriptionOptions } from './cmap/stream_
285285
export type { CompressorName } from './cmap/wire_protocol/compression';
286286
export type { CollectionOptions, CollectionPrivate, ModifyResult } from './collection';
287287
export type {
288+
COMMAND_FAILED,
289+
COMMAND_STARTED,
290+
COMMAND_SUCCEEDED,
288291
CONNECTION_CHECK_OUT_FAILED,
289292
CONNECTION_CHECK_OUT_STARTED,
290293
CONNECTION_CHECKED_IN,
@@ -356,6 +359,8 @@ export type {
356359
LogComponentSeveritiesClientOptions,
357360
LogConvertible,
358361
Loggable,
362+
LoggableCommandFailedEvent,
363+
LoggableCommandSucceededEvent,
359364
LoggableEvent,
360365
LoggableServerHeartbeatFailedEvent,
361366
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)