Skip to content

Commit a3c0298

Browse files
feat(NODE-4719): add SDAM Logging Spec (#3940)
1 parent 371aae8 commit a3c0298

File tree

64 files changed

+3930
-136
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+3930
-136
lines changed

Diff for: src/constants.ts

+9
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,17 @@ export const MESSAGE = 'message' as const;
1717
export const PINNED = 'pinned' as const;
1818
export const UNPINNED = 'unpinned' as const;
1919
export const DESCRIPTION_RECEIVED = 'descriptionReceived';
20+
/** @internal */
2021
export const SERVER_OPENING = 'serverOpening' as const;
22+
/** @internal */
2123
export const SERVER_CLOSED = 'serverClosed' as const;
24+
/** @internal */
2225
export const SERVER_DESCRIPTION_CHANGED = 'serverDescriptionChanged' as const;
26+
/** @internal */
2327
export const TOPOLOGY_OPENING = 'topologyOpening' as const;
28+
/** @internal */
2429
export const TOPOLOGY_CLOSED = 'topologyClosed' as const;
30+
/** @internal */
2531
export const TOPOLOGY_DESCRIPTION_CHANGED = 'topologyDescriptionChanged' as const;
2632
/** @internal */
2733
export const CONNECTION_POOL_CREATED = 'connectionPoolCreated' as const;
@@ -49,8 +55,11 @@ export const CLUSTER_TIME_RECEIVED = 'clusterTimeReceived' as const;
4955
export const COMMAND_STARTED = 'commandStarted' as const;
5056
export const COMMAND_SUCCEEDED = 'commandSucceeded' as const;
5157
export const COMMAND_FAILED = 'commandFailed' as const;
58+
/** @internal */
5259
export const SERVER_HEARTBEAT_STARTED = 'serverHeartbeatStarted' as const;
60+
/** @internal */
5361
export const SERVER_HEARTBEAT_SUCCEEDED = 'serverHeartbeatSucceeded' as const;
62+
/** @internal */
5463
export const SERVER_HEARTBEAT_FAILED = 'serverHeartbeatFailed' as const;
5564
export const RESPONSE = 'response' as const;
5665
export const MORE = 'more' as const;

Diff for: src/index.ts

+13-1
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,16 @@ export type {
305305
CONNECTION_POOL_CREATED,
306306
CONNECTION_POOL_READY,
307307
CONNECTION_READY,
308-
MONGO_CLIENT_EVENTS
308+
MONGO_CLIENT_EVENTS,
309+
SERVER_CLOSED,
310+
SERVER_DESCRIPTION_CHANGED,
311+
SERVER_HEARTBEAT_FAILED,
312+
SERVER_HEARTBEAT_STARTED,
313+
SERVER_HEARTBEAT_SUCCEEDED,
314+
SERVER_OPENING,
315+
TOPOLOGY_CLOSED,
316+
TOPOLOGY_DESCRIPTION_CHANGED,
317+
TOPOLOGY_OPENING
309318
} from './constants';
310319
export type {
311320
AbstractCursorEvents,
@@ -357,6 +366,9 @@ export type {
357366
LogConvertible,
358367
Loggable,
359368
LoggableEvent,
369+
LoggableServerHeartbeatFailedEvent,
370+
LoggableServerHeartbeatStartedEvent,
371+
LoggableServerHeartbeatSucceededEvent,
360372
MongoDBLogWritable,
361373
MongoLoggableComponent,
362374
MongoLogger,

Diff for: src/mongo_logger.ts

+153-8
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { EJSON } from 'bson';
1+
import { type Document, EJSON, type EJSONOptions } from 'bson';
22
import type { Writable } from 'stream';
33
import { inspect } from 'util';
44

@@ -35,8 +35,23 @@ import {
3535
CONNECTION_POOL_CLOSED,
3636
CONNECTION_POOL_CREATED,
3737
CONNECTION_POOL_READY,
38-
CONNECTION_READY
38+
CONNECTION_READY,
39+
SERVER_CLOSED,
40+
SERVER_HEARTBEAT_FAILED,
41+
SERVER_HEARTBEAT_STARTED,
42+
SERVER_HEARTBEAT_SUCCEEDED,
43+
SERVER_OPENING,
44+
TOPOLOGY_CLOSED,
45+
TOPOLOGY_DESCRIPTION_CHANGED,
46+
TOPOLOGY_OPENING
3947
} from './constants';
48+
import type {
49+
ServerClosedEvent,
50+
ServerOpeningEvent,
51+
TopologyClosedEvent,
52+
TopologyDescriptionChangedEvent,
53+
TopologyOpeningEvent
54+
} from './sdam/events';
4055
import { HostAddress, parseUnsignedInteger } from './utils';
4156

4257
/** @internal */
@@ -270,6 +285,54 @@ function compareSeverity(s0: SeverityLevel, s1: SeverityLevel): 1 | 0 | -1 {
270285
return s0Num < s1Num ? -1 : s0Num > s1Num ? 1 : 0;
271286
}
272287

288+
/**
289+
* @internal
290+
* Must be separate from Events API due to differences in spec requirements for logging server heartbeat beginning
291+
*/
292+
export type LoggableServerHeartbeatStartedEvent = {
293+
topologyId: number;
294+
awaited: boolean;
295+
connectionId: string;
296+
name: typeof SERVER_HEARTBEAT_STARTED;
297+
};
298+
299+
/**
300+
* @internal
301+
* Must be separate from Events API due to differences in spec requirements for logging server heartbeat success
302+
*/
303+
export type LoggableServerHeartbeatSucceededEvent = {
304+
topologyId: number;
305+
awaited: boolean;
306+
connectionId: string;
307+
reply: Document;
308+
serverConnectionId: number | '<monitor>';
309+
duration: number;
310+
name: typeof SERVER_HEARTBEAT_SUCCEEDED;
311+
};
312+
313+
/**
314+
* @internal
315+
* Must be separate from Events API due to differences in spec requirements for logging server heartbeat failure
316+
*/
317+
export type LoggableServerHeartbeatFailedEvent = {
318+
topologyId: number;
319+
awaited: boolean;
320+
connectionId: string;
321+
failure: Error;
322+
duration: number;
323+
name: typeof SERVER_HEARTBEAT_FAILED;
324+
};
325+
326+
type SDAMLoggableEvent =
327+
| ServerClosedEvent
328+
| LoggableServerHeartbeatFailedEvent
329+
| LoggableServerHeartbeatStartedEvent
330+
| LoggableServerHeartbeatSucceededEvent
331+
| ServerOpeningEvent
332+
| TopologyClosedEvent
333+
| TopologyDescriptionChangedEvent
334+
| TopologyOpeningEvent;
335+
273336
/** @internal */
274337
export type LoggableEvent =
275338
| CommandStartedEvent
@@ -285,16 +348,28 @@ export type LoggableEvent =
285348
| ConnectionCheckedInEvent
286349
| ConnectionCheckedOutEvent
287350
| ConnectionCheckOutStartedEvent
288-
| ConnectionCheckOutFailedEvent;
351+
| ConnectionCheckOutFailedEvent
352+
| ServerClosedEvent
353+
| LoggableServerHeartbeatFailedEvent
354+
| LoggableServerHeartbeatStartedEvent
355+
| LoggableServerHeartbeatSucceededEvent
356+
| ServerOpeningEvent
357+
| TopologyClosedEvent
358+
| TopologyDescriptionChangedEvent
359+
| TopologyOpeningEvent;
289360

290361
/** @internal */
291362
export interface LogConvertible extends Record<string, any> {
292363
toLog(): Record<string, any>;
293364
}
294365

295366
/** @internal */
296-
export function stringifyWithMaxLen(value: any, maxDocumentLength: number): string {
297-
const ejson = EJSON.stringify(value);
367+
export function stringifyWithMaxLen(
368+
value: any,
369+
maxDocumentLength: number,
370+
options: EJSONOptions = {}
371+
): string {
372+
const ejson = EJSON.stringify(value, options);
298373

299374
return maxDocumentLength !== 0 && ejson.length > maxDocumentLength
300375
? `${ejson.slice(0, maxDocumentLength)}...`
@@ -329,15 +404,36 @@ function attachCommandFields(
329404

330405
function attachConnectionFields(
331406
log: Record<string, any>,
332-
connectionPoolEvent: ConnectionPoolMonitoringEvent
407+
event: ConnectionPoolMonitoringEvent | ServerOpeningEvent | ServerClosedEvent
333408
) {
334-
const { host, port } = HostAddress.fromString(connectionPoolEvent.address).toHostPort();
409+
const { host, port } = HostAddress.fromString(event.address).toHostPort();
335410
log.serverHost = host;
336411
log.serverPort = port;
337412

338413
return log;
339414
}
340415

416+
function attachSDAMFields(log: Record<string, any>, sdamEvent: SDAMLoggableEvent) {
417+
log.topologyId = sdamEvent.topologyId;
418+
return log;
419+
}
420+
421+
function attachServerHeartbeatFields(
422+
log: Record<string, any>,
423+
serverHeartbeatEvent:
424+
| LoggableServerHeartbeatFailedEvent
425+
| LoggableServerHeartbeatStartedEvent
426+
| LoggableServerHeartbeatSucceededEvent
427+
) {
428+
const { awaited, connectionId } = serverHeartbeatEvent;
429+
log.awaited = awaited;
430+
log.driverConnectionId = serverHeartbeatEvent.connectionId;
431+
const { host, port } = HostAddress.fromString(connectionId).toHostPort();
432+
log.serverHost = host;
433+
log.serverPort = port;
434+
return log;
435+
}
436+
341437
function defaultLogTransform(
342438
logObject: LoggableEvent | Record<string, any>,
343439
maxDocumentLength: number = DEFAULT_MAX_DOCUMENT_LENGTH
@@ -456,14 +552,63 @@ function defaultLogTransform(
456552
case CONNECTION_CHECKED_OUT:
457553
log = attachConnectionFields(log, logObject);
458554
log.message = 'Connection checked out';
459-
460555
log.driverConnectionId = logObject.connectionId;
461556
return log;
462557
case CONNECTION_CHECKED_IN:
463558
log = attachConnectionFields(log, logObject);
464559
log.message = 'Connection checked in';
465560
log.driverConnectionId = logObject.connectionId;
466561
return log;
562+
case SERVER_OPENING:
563+
log = attachSDAMFields(log, logObject);
564+
log = attachConnectionFields(log, logObject);
565+
log.message = 'Starting server monitoring';
566+
return log;
567+
case SERVER_CLOSED:
568+
log = attachSDAMFields(log, logObject);
569+
log = attachConnectionFields(log, logObject);
570+
log.message = 'Stopped server monitoring';
571+
return log;
572+
case SERVER_HEARTBEAT_STARTED:
573+
log = attachSDAMFields(log, logObject);
574+
log = attachServerHeartbeatFields(log, logObject);
575+
log.message = 'Server heartbeat started';
576+
return log;
577+
case SERVER_HEARTBEAT_SUCCEEDED:
578+
log = attachSDAMFields(log, logObject);
579+
log = attachServerHeartbeatFields(log, logObject);
580+
log.message = 'Server heartbeat succeeded';
581+
log.durationMS = logObject.duration;
582+
log.serverConnectionId = logObject.serverConnectionId;
583+
log.reply = stringifyWithMaxLen(logObject.reply, maxDocumentLength, { relaxed: true });
584+
return log;
585+
case SERVER_HEARTBEAT_FAILED:
586+
log = attachSDAMFields(log, logObject);
587+
log = attachServerHeartbeatFields(log, logObject);
588+
log.message = 'Server heartbeat failed';
589+
log.durationMS = logObject.duration;
590+
log.failure = logObject.failure.message;
591+
return log;
592+
case TOPOLOGY_OPENING:
593+
log = attachSDAMFields(log, logObject);
594+
log.message = 'Starting topology monitoring';
595+
return log;
596+
case TOPOLOGY_CLOSED:
597+
log = attachSDAMFields(log, logObject);
598+
log.message = 'Stopped topology monitoring';
599+
return log;
600+
case TOPOLOGY_DESCRIPTION_CHANGED:
601+
log = attachSDAMFields(log, logObject);
602+
log.message = 'Topology description changed';
603+
log.previousDescription = log.reply = stringifyWithMaxLen(
604+
logObject.previousDescription,
605+
maxDocumentLength
606+
);
607+
log.newDescription = log.reply = stringifyWithMaxLen(
608+
logObject.newDescription,
609+
maxDocumentLength
610+
);
611+
return log;
467612
default:
468613
for (const [key, value] of Object.entries(logObject)) {
469614
if (value != null) log[key] = value;

Diff for: src/mongo_types.ts

+28-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,13 @@ import type {
1212
ObjectId,
1313
Timestamp
1414
} from './bson';
15-
import type { MongoLoggableComponent, MongoLogger } from './mongo_logger';
15+
import type {
16+
LoggableServerHeartbeatFailedEvent,
17+
LoggableServerHeartbeatStartedEvent,
18+
LoggableServerHeartbeatSucceededEvent,
19+
MongoLoggableComponent,
20+
MongoLogger
21+
} from './mongo_logger';
1622
import type { Sort } from './sort';
1723

1824
/** @internal */
@@ -405,13 +411,33 @@ export class TypedEventEmitter<Events extends EventsDescription> extends EventEm
405411
/** @internal */
406412
protected component?: MongoLoggableComponent;
407413
/** @internal */
408-
protected emitAndLog<EventKey extends keyof Events>(
414+
emitAndLog<EventKey extends keyof Events>(
409415
event: EventKey | symbol,
410416
...args: Parameters<Events[EventKey]>
411417
): void {
412418
this.emit(event, ...args);
413419
if (this.component) this.mongoLogger?.debug(this.component, args[0]);
414420
}
421+
/** @internal */
422+
emitAndLogHeartbeat<EventKey extends keyof Events>(
423+
event: EventKey | symbol,
424+
topologyId: number,
425+
serverConnectionId?: number | '<monitor>',
426+
...args: Parameters<Events[EventKey]>
427+
): void {
428+
this.emit(event, ...args);
429+
if (this.component) {
430+
const loggableHeartbeatEvent:
431+
| LoggableServerHeartbeatFailedEvent
432+
| LoggableServerHeartbeatSucceededEvent
433+
| LoggableServerHeartbeatStartedEvent = {
434+
topologyId: topologyId,
435+
serverConnectionId: serverConnectionId ?? null,
436+
...args[0]
437+
};
438+
this.mongoLogger?.debug(this.component, loggableHeartbeatEvent);
439+
}
440+
}
415441
}
416442

417443
/** @public */

0 commit comments

Comments
 (0)