import { Writable } from 'stream'; import { parseUnsignedInteger } from './utils'; /** @internal */ export const SeverityLevel = Object.freeze({ EMERGENCY: 'emergency', ALERT: 'alert', CRITICAL: 'critical', ERROR: 'error', WARNING: 'warn', NOTICE: 'notice', INFORMATIONAL: 'info', DEBUG: 'debug', TRACE: 'trace', OFF: 'off' } as const); /** @internal */ export type SeverityLevel = (typeof SeverityLevel)[keyof typeof SeverityLevel]; /** @internal */ export const MongoLoggableComponent = Object.freeze({ COMMAND: 'command', TOPOLOGY: 'topology', SERVER_SELECTION: 'serverSelection', CONNECTION: 'connection' } as const); /** @internal */ export type MongoLoggableComponent = (typeof MongoLoggableComponent)[keyof typeof MongoLoggableComponent]; /** @internal */ export interface MongoLoggerEnvOptions { /** Severity level for command component */ MONGODB_LOG_COMMAND?: string; /** Severity level for topology component */ MONGODB_LOG_TOPOLOGY?: string; /** Severity level for server selection component */ MONGODB_LOG_SERVER_SELECTION?: string; /** Severity level for CMAP */ MONGODB_LOG_CONNECTION?: string; /** Default severity level to be if any of the above are unset */ MONGODB_LOG_ALL?: string; /** Max length of embedded EJSON docs. Setting to 0 disables truncation. Defaults to 1000. */ MONGODB_LOG_MAX_DOCUMENT_LENGTH?: string; /** Destination for log messages. Must be 'stderr', 'stdout'. Defaults to 'stderr'. */ MONGODB_LOG_PATH?: string; } /** @internal */ export interface MongoLoggerMongoClientOptions { /** Destination for log messages */ mongodbLogPath?: 'stdout' | 'stderr' | Writable; } /** @internal */ export interface MongoLoggerOptions { componentSeverities: { /** Severity level for command component */ command: SeverityLevel; /** Severity level for topology component */ topology: SeverityLevel; /** Severity level for server selection component */ serverSelection: SeverityLevel; /** Severity level for connection component */ connection: SeverityLevel; /** Default severity level to be used if any of the above are unset */ default: SeverityLevel; }; /** Max length of embedded EJSON docs. Setting to 0 disables truncation. Defaults to 1000. */ maxDocumentLength: number; /** Destination for log messages. */ logDestination: Writable; } /** * Parses a string as one of SeverityLevel * * @param s - the value to be parsed * @returns one of SeverityLevel if value can be parsed as such, otherwise null */ function parseSeverityFromString(s?: string): SeverityLevel | null { const validSeverities: string[] = Object.values(SeverityLevel); const lowerSeverity = s?.toLowerCase(); if (lowerSeverity != null && validSeverities.includes(lowerSeverity)) { return lowerSeverity as SeverityLevel; } return null; } /** * resolves the MONGODB_LOG_PATH and mongodbLogPath options from the environment and the * mongo client options respectively. * * @returns the Writable stream to write logs to */ function resolveLogPath( { MONGODB_LOG_PATH }: MongoLoggerEnvOptions, { mongodbLogPath }: { mongodbLogPath?: unknown; } ): Writable { const isValidLogDestinationString = (destination: string) => ['stdout', 'stderr'].includes(destination.toLowerCase()); if (typeof mongodbLogPath === 'string' && isValidLogDestinationString(mongodbLogPath)) { return mongodbLogPath.toLowerCase() === 'stderr' ? process.stderr : process.stdout; } // TODO(NODE-4813): check for minimal interface instead of instanceof Writable if (typeof mongodbLogPath === 'object' && mongodbLogPath instanceof Writable) { return mongodbLogPath; } if (typeof MONGODB_LOG_PATH === 'string' && isValidLogDestinationString(MONGODB_LOG_PATH)) { return MONGODB_LOG_PATH.toLowerCase() === 'stderr' ? process.stderr : process.stdout; } return process.stderr; } /** @internal */ export class MongoLogger { componentSeverities: Record; maxDocumentLength: number; logDestination: Writable; constructor(options: MongoLoggerOptions) { this.componentSeverities = options.componentSeverities; this.maxDocumentLength = options.maxDocumentLength; this.logDestination = options.logDestination; } /* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable @typescript-eslint/no-empty-function */ emergency(component: any, message: any): void {} alert(component: any, message: any): void {} critical(component: any, message: any): void {} error(component: any, message: any): void {} warn(component: any, message: any): void {} notice(component: any, message: any): void {} info(component: any, message: any): void {} debug(component: any, message: any): void {} trace(component: any, message: any): void {} /** * Merges options set through environment variables and the MongoClient, preferring environment * variables when both are set, and substituting defaults for values not set. Options set in * constructor take precedence over both environment variables and MongoClient options. * * @remarks * When parsing component severity levels, invalid values are treated as unset and replaced with * the default severity. * * @param envOptions - options set for the logger from the environment * @param clientOptions - options set for the logger in the MongoClient options * @returns a MongoLoggerOptions object to be used when instantiating a new MongoLogger */ static resolveOptions( envOptions: MongoLoggerEnvOptions, clientOptions: MongoLoggerMongoClientOptions ): MongoLoggerOptions { // client options take precedence over env options const combinedOptions = { ...envOptions, ...clientOptions, mongodbLogPath: resolveLogPath(envOptions, clientOptions) }; const defaultSeverity = parseSeverityFromString(combinedOptions.MONGODB_LOG_ALL) ?? SeverityLevel.OFF; return { componentSeverities: { command: parseSeverityFromString(combinedOptions.MONGODB_LOG_COMMAND) ?? defaultSeverity, topology: parseSeverityFromString(combinedOptions.MONGODB_LOG_TOPOLOGY) ?? defaultSeverity, serverSelection: parseSeverityFromString(combinedOptions.MONGODB_LOG_SERVER_SELECTION) ?? defaultSeverity, connection: parseSeverityFromString(combinedOptions.MONGODB_LOG_CONNECTION) ?? defaultSeverity, default: defaultSeverity }, maxDocumentLength: parseUnsignedInteger(combinedOptions.MONGODB_LOG_MAX_DOCUMENT_LENGTH) ?? 1000, logDestination: combinedOptions.mongodbLogPath }; } }