From 21999b1b4d43547bb227053b0a52a3d0527f273e Mon Sep 17 00:00:00 2001 From: Alena Khineika Date: Fri, 17 Jan 2025 15:24:16 +0100 Subject: [PATCH 1/8] feat(logging): custom log entries MONGOSH-1989 --- .../cli-repl/test/fixtures/custom-log-info.js | 1 + packages/e2e-tests/test/e2e.spec.ts | 44 ++++++++ .../src/setup-logger-and-telemetry.spec.ts | 101 ++++++++++++++++++ .../logging/src/setup-logger-and-telemetry.ts | 12 +++ .../shell-api/src/shell-instance-state.ts | 43 +++++++- packages/types/src/index.ts | 12 +++ 6 files changed, 212 insertions(+), 1 deletion(-) create mode 100644 packages/cli-repl/test/fixtures/custom-log-info.js diff --git a/packages/cli-repl/test/fixtures/custom-log-info.js b/packages/cli-repl/test/fixtures/custom-log-info.js new file mode 100644 index 0000000000..7855b6112d --- /dev/null +++ b/packages/cli-repl/test/fixtures/custom-log-info.js @@ -0,0 +1 @@ +log.info('Hi there'); diff --git a/packages/e2e-tests/test/e2e.spec.ts b/packages/e2e-tests/test/e2e.spec.ts index c9b2b18c74..20afe1527f 100644 --- a/packages/e2e-tests/test/e2e.spec.ts +++ b/packages/e2e-tests/test/e2e.spec.ts @@ -578,6 +578,11 @@ describe('e2e', function () { shell.assertNoErrors(); }); + it('runs a custom log command', async function () { + await shell.executeLine("log.info('Try me')"); + shell.assertNoErrors(); + }); + it('runs help command', async function () { expect(await shell.executeLine('help')).to.include('Shell Help'); shell.assertNoErrors(); @@ -1506,6 +1511,45 @@ describe('e2e', function () { ).to.have.lengthOf(1); }); }); + + it('writes custom log directly', async function () { + const connectionString = await testServer.connectionString(); + await shell.executeLine( + `connect(${JSON.stringify(connectionString)})` + ); + await shell.executeLine("log.info('This is a custom entry')"); + await eventually(async () => { + const log = await readLogfile(); + expect( + log.filter((logEntry) => + logEntry.msg.includes('This is a custom entry') + ) + ).to.have.lengthOf(1); + }); + }); + + it('writes custom log when loads a script', async function () { + const connectionString = await testServer.connectionString(); + await shell.executeLine( + `connect(${JSON.stringify(connectionString)})` + ); + const filename = path.resolve( + __dirname, + '..', + '..', + 'cli-repl', + 'test', + 'fixtures', + 'custom-log-info.js' + ); + await shell.executeLine(`load('${filename}')`); + await eventually(async () => { + const log = await readLogfile(); + expect( + log.filter((logEntry) => logEntry.msg.includes('Hi there')) + ).to.have.lengthOf(1); + }); + }); }); describe('history file', function () { diff --git a/packages/logging/src/setup-logger-and-telemetry.spec.ts b/packages/logging/src/setup-logger-and-telemetry.spec.ts index b604219ba6..29226427c7 100644 --- a/packages/logging/src/setup-logger-and-telemetry.spec.ts +++ b/packages/logging/src/setup-logger-and-telemetry.spec.ts @@ -822,4 +822,105 @@ describe('setupLoggerAndTelemetry', function () { expect(logOutput).to.have.lengthOf(0); expect(analyticsOutput).to.be.empty; }); + + it('tracks custom logging events', function () { + setupLoggerAndTelemetry( + bus, + logger, + analytics, + { + platform: process.platform, + arch: process.arch, + }, + '1.0.0' + ); + expect(logOutput).to.have.lengthOf(0); + expect(analyticsOutput).to.be.empty; + + bus.emit('mongosh:connect', { + uri: 'mongodb://localhost/', + is_localhost: true, + is_atlas: false, + resolved_hostname: 'localhost', + node_version: 'v12.19.0', + }); + + bus.emit('mongosh:write-custom-log', { + method: 'info', + message: 'This is an info message', + attr: { some: 'value' }, + }); + + bus.emit('mongosh:write-custom-log', { + method: 'warn', + message: 'This is a warn message', + }); + + bus.emit('mongosh:write-custom-log', { + method: 'error', + message: 'Error!', + }); + + bus.emit('mongosh:write-custom-log', { + method: 'fatal', + message: 'Fatal!', + }); + + bus.emit('mongosh:write-custom-log', { + method: 'debug', + message: 'Debug', + level: 1, + }); + + expect(logOutput[0].msg).to.equal('Connecting to server'); + expect(logOutput[0].attr.connectionUri).to.equal('mongodb://localhost/'); + expect(logOutput[0].attr.is_localhost).to.equal(true); + expect(logOutput[0].attr.is_atlas).to.equal(false); + expect(logOutput[0].attr.atlas_hostname).to.equal(null); + expect(logOutput[0].attr.node_version).to.equal('v12.19.0'); + + expect(logOutput[1].s).to.equal('I'); + expect(logOutput[1].c).to.equal('MONGOSH-SCRIPTS'); + expect(logOutput[1].ctx).to.equal('custom-log'); + expect(logOutput[1].msg).to.equal('This is an info message'); + expect(logOutput[1].attr.some).to.equal('value'); + + expect(logOutput[2].s).to.equal('W'); + expect(logOutput[2].c).to.equal('MONGOSH-SCRIPTS'); + expect(logOutput[2].ctx).to.equal('custom-log'); + expect(logOutput[2].msg).to.equal('This is a warn message'); + + expect(logOutput[3].s).to.equal('E'); + expect(logOutput[3].c).to.equal('MONGOSH-SCRIPTS'); + expect(logOutput[3].ctx).to.equal('custom-log'); + expect(logOutput[3].msg).to.equal('Error!'); + + expect(logOutput[4].s).to.equal('F'); + expect(logOutput[4].c).to.equal('MONGOSH-SCRIPTS'); + expect(logOutput[4].ctx).to.equal('custom-log'); + expect(logOutput[4].msg).to.equal('Fatal!'); + + expect(logOutput[5].s).to.equal('D1'); + expect(logOutput[5].c).to.equal('MONGOSH-SCRIPTS'); + expect(logOutput[5].ctx).to.equal('custom-log'); + expect(logOutput[5].msg).to.equal('Debug'); + + expect(analyticsOutput).to.deep.equal([ + [ + 'track', + { + anonymousId: undefined, + event: 'New Connection', + properties: { + mongosh_version: '1.0.0', + session_id: '5fb3c20ee1507e894e5340f3', + is_localhost: true, + is_atlas: false, + atlas_hostname: null, + node_version: 'v12.19.0', + }, + }, + ], + ]); + }); }); diff --git a/packages/logging/src/setup-logger-and-telemetry.ts b/packages/logging/src/setup-logger-and-telemetry.ts index 4afa8fa5a8..c659d21b9e 100644 --- a/packages/logging/src/setup-logger-and-telemetry.ts +++ b/packages/logging/src/setup-logger-and-telemetry.ts @@ -31,6 +31,7 @@ import type { FetchingUpdateMetadataEvent, FetchingUpdateMetadataCompleteEvent, SessionStartedEvent, + WriteCustomLogEvent, } from '@mongosh/types'; import { inspect } from 'util'; import type { MongoLogWriter } from 'mongodb-log-writer'; @@ -140,6 +141,17 @@ export function setupLoggerAndTelemetry( } ); + bus.on('mongosh:write-custom-log', (event: WriteCustomLogEvent) => { + log[event.method]( + 'MONGOSH-SCRIPTS', + mongoLogId(1_000_000_054), + 'custom-log', + event.message, + event.attr, + event.level + ); + }); + bus.on('mongosh:connect', function (args: ConnectEvent) { const { uri, resolved_hostname, ...argsWithoutUriAndHostname } = args; const connectionUri = uri && redactURICredentials(uri); diff --git a/packages/shell-api/src/shell-instance-state.ts b/packages/shell-api/src/shell-instance-state.ts index 06e281dce9..6db3d1c807 100644 --- a/packages/shell-api/src/shell-instance-state.ts +++ b/packages/shell-api/src/shell-instance-state.ts @@ -359,7 +359,48 @@ export default class ShellInstanceState { }); } - this.messageBus.emit('mongosh:setCtx', { method: 'setCtx', arguments: {} }); + const bus = this.messageBus; + if (contextObject.log === undefined) { + contextObject.log = { + info(message: string, attr?: unknown) { + bus.emit('mongosh:write-custom-log', { + method: 'info', + message, + attr, + }); + }, + warn(message: string, attr?: unknown) { + bus.emit('mongosh:write-custom-log', { + method: 'warn', + message, + attr, + }); + }, + error(message: string, attr?: unknown) { + bus.emit('mongosh:write-custom-log', { + method: 'error', + message, + attr, + }); + }, + fatal(message: string, attr?: unknown) { + bus.emit('mongosh:write-custom-log', { + method: 'fatal', + message, + attr, + }); + }, + debug(message: string, attr?: unknown, level?: 1 | 2 | 3 | 4 | 5) { + bus.emit('mongosh:write-custom-log', { + method: 'debug', + message, + attr, + level, + }); + }, + }; + } + bus.emit('mongosh:setCtx', { method: 'setCtx', arguments: {} }); } get currentServiceProvider(): ServiceProvider { diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 06fa242c0d..9267eddc89 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -187,6 +187,13 @@ export interface SessionStartedEvent { }; } +export interface WriteCustomLogEvent { + method: 'info' | 'error' | 'warn' | 'fatal' | 'debug'; + message: string; + attr?: unknown; + level?: 1 | 2 | 3 | 4 | 5; +} + export interface MongoshBusEventsMap extends ConnectEventMap { /** * Signals a connection to a MongoDB instance has been established @@ -267,6 +274,11 @@ export interface MongoshBusEventsMap extends ConnectEventMap { 'mongosh:start-loading-cli-scripts': ( event: StartLoadingCliScriptsEvent ) => void; + /** + * Signals to start writing log to the disc after MongoLogManager is initialized. + */ + 'mongosh:write-custom-log': (ev: WriteCustomLogEvent) => void; + /** * Signals the successful startup of the mongosh REPL after initial files and configuration * have been loaded. From 928349c61e87b1e4e52533466e0e55014753db0a Mon Sep 17 00:00:00 2001 From: Alena Khineika Date: Wed, 22 Jan 2025 16:56:43 +0100 Subject: [PATCH 2/8] refactor: finalise pr --- packages/e2e-tests/test/e2e.spec.ts | 15 +++--- .../test/fixtures/custom-log-info.js | 0 .../test/fixtures/simple-console-log.js | 0 .../shell-api/src/shell-instance-state.ts | 47 ++-------------- packages/shell-api/src/shell-log.ts | 54 +++++++++++++++++++ 5 files changed, 67 insertions(+), 49 deletions(-) rename packages/{cli-repl => e2e-tests}/test/fixtures/custom-log-info.js (100%) rename packages/{cli-repl => e2e-tests}/test/fixtures/simple-console-log.js (100%) create mode 100644 packages/shell-api/src/shell-log.ts diff --git a/packages/e2e-tests/test/e2e.spec.ts b/packages/e2e-tests/test/e2e.spec.ts index 20afe1527f..4780cb43e2 100644 --- a/packages/e2e-tests/test/e2e.spec.ts +++ b/packages/e2e-tests/test/e2e.spec.ts @@ -1513,10 +1513,6 @@ describe('e2e', function () { }); it('writes custom log directly', async function () { - const connectionString = await testServer.connectionString(); - await shell.executeLine( - `connect(${JSON.stringify(connectionString)})` - ); await shell.executeLine("log.info('This is a custom entry')"); await eventually(async () => { const log = await readLogfile(); @@ -1537,14 +1533,19 @@ describe('e2e', function () { __dirname, '..', '..', - 'cli-repl', + 'e2e-tests', 'test', 'fixtures', 'custom-log-info.js' ); - await shell.executeLine(`load('${filename}')`); + await shell.executeLine(`load(${JSON.stringify(filename)})`); await eventually(async () => { const log = await readLogfile(); + expect( + log.filter((logEntry) => + logEntry.msg.includes('Initiating connection attemp') + ) + ).to.have.lengthOf(1); expect( log.filter((logEntry) => logEntry.msg.includes('Hi there')) ).to.have.lengthOf(1); @@ -1975,7 +1976,7 @@ describe('e2e', function () { __dirname, '..', '..', - 'cli-repl', + 'e2e-tests', 'test', 'fixtures', 'simple-console-log.js' diff --git a/packages/cli-repl/test/fixtures/custom-log-info.js b/packages/e2e-tests/test/fixtures/custom-log-info.js similarity index 100% rename from packages/cli-repl/test/fixtures/custom-log-info.js rename to packages/e2e-tests/test/fixtures/custom-log-info.js diff --git a/packages/cli-repl/test/fixtures/simple-console-log.js b/packages/e2e-tests/test/fixtures/simple-console-log.js similarity index 100% rename from packages/cli-repl/test/fixtures/simple-console-log.js rename to packages/e2e-tests/test/fixtures/simple-console-log.js diff --git a/packages/shell-api/src/shell-instance-state.ts b/packages/shell-api/src/shell-instance-state.ts index 6cb89248a2..4ded72d02a 100644 --- a/packages/shell-api/src/shell-instance-state.ts +++ b/packages/shell-api/src/shell-instance-state.ts @@ -35,6 +35,7 @@ import NoDatabase from './no-db'; import type { ShellBson } from './shell-bson'; import constructShellBson from './shell-bson'; import { Streams } from './streams'; +import constructShellLog, { type ShellLog } from './shell-log'; /** * The subset of CLI options that is relevant for the shell API's behavior itself. @@ -159,6 +160,7 @@ export default class ShellInstanceState { public context: any; public mongos: Mongo[]; public shellApi: ShellApi; + public shellLog: ShellLog; public shellBson: ShellBson; public cliOptions: ShellCliOptions; public evaluationListener: EvaluationListener; @@ -187,6 +189,7 @@ export default class ShellInstanceState { this.initialServiceProvider = initialServiceProvider; this.messageBus = messageBus; this.shellApi = new ShellApi(this); + this.shellLog = constructShellLog(this.messageBus); this.shellBson = constructShellBson( initialServiceProvider.bsonLibrary, (msg: string) => { @@ -362,48 +365,8 @@ export default class ShellInstanceState { }); } - const bus = this.messageBus; - if (contextObject.log === undefined) { - contextObject.log = { - info(message: string, attr?: unknown) { - bus.emit('mongosh:write-custom-log', { - method: 'info', - message, - attr, - }); - }, - warn(message: string, attr?: unknown) { - bus.emit('mongosh:write-custom-log', { - method: 'warn', - message, - attr, - }); - }, - error(message: string, attr?: unknown) { - bus.emit('mongosh:write-custom-log', { - method: 'error', - message, - attr, - }); - }, - fatal(message: string, attr?: unknown) { - bus.emit('mongosh:write-custom-log', { - method: 'fatal', - message, - attr, - }); - }, - debug(message: string, attr?: unknown, level?: 1 | 2 | 3 | 4 | 5) { - bus.emit('mongosh:write-custom-log', { - method: 'debug', - message, - attr, - level, - }); - }, - }; - } - bus.emit('mongosh:setCtx', { method: 'setCtx', arguments: {} }); + Object.assign(contextObject, this.shellLog); + this.messageBus.emit('mongosh:setCtx', { method: 'setCtx', arguments: {} }); } get currentServiceProvider(): ServiceProvider { diff --git a/packages/shell-api/src/shell-log.ts b/packages/shell-api/src/shell-log.ts new file mode 100644 index 0000000000..a2a9217efa --- /dev/null +++ b/packages/shell-api/src/shell-log.ts @@ -0,0 +1,54 @@ +import type { MongoshBus } from '@mongosh/types'; + +export interface ShellLog { + log: { + info: (message: string, attr?: unknown) => void; + warn: (message: string, attr?: unknown) => void; + error: (message: string, attr?: unknown) => void; + fatal: (message: string, attr?: unknown) => void; + debug: (message: string, attr?: unknown, level?: 1 | 2 | 3 | 4 | 5) => void; + }; +} + +export default function constructShellLog(messageBus: MongoshBus): ShellLog { + return { + log: { + info(message: string, attr?: unknown) { + messageBus.emit('mongosh:write-custom-log', { + method: 'info', + message, + attr, + }); + }, + warn(message: string, attr?: unknown) { + messageBus.emit('mongosh:write-custom-log', { + method: 'warn', + message, + attr, + }); + }, + error(message: string, attr?: unknown) { + messageBus.emit('mongosh:write-custom-log', { + method: 'error', + message, + attr, + }); + }, + fatal(message: string, attr?: unknown) { + messageBus.emit('mongosh:write-custom-log', { + method: 'fatal', + message, + attr, + }); + }, + debug(message: string, attr?: unknown, level?: 1 | 2 | 3 | 4 | 5) { + messageBus.emit('mongosh:write-custom-log', { + method: 'debug', + message, + attr, + level, + }); + }, + }, + }; +} From eeb174fba8a792ea77d9e5e0989f3de45beee9ad Mon Sep 17 00:00:00 2001 From: Alena Khineika Date: Thu, 23 Jan 2025 13:25:33 +0100 Subject: [PATCH 3/8] refactor: extend shell log from shell api --- packages/e2e-tests/test/e2e.spec.ts | 11 +---- .../shell-api/src/shell-instance-state.ts | 4 +- packages/shell-api/src/shell-log.ts | 40 +++++++++++++------ 3 files changed, 31 insertions(+), 24 deletions(-) diff --git a/packages/e2e-tests/test/e2e.spec.ts b/packages/e2e-tests/test/e2e.spec.ts index 4780cb43e2..4b52d0e630 100644 --- a/packages/e2e-tests/test/e2e.spec.ts +++ b/packages/e2e-tests/test/e2e.spec.ts @@ -578,11 +578,6 @@ describe('e2e', function () { shell.assertNoErrors(); }); - it('runs a custom log command', async function () { - await shell.executeLine("log.info('Try me')"); - shell.assertNoErrors(); - }); - it('runs help command', async function () { expect(await shell.executeLine('help')).to.include('Shell Help'); shell.assertNoErrors(); @@ -1514,6 +1509,7 @@ describe('e2e', function () { it('writes custom log directly', async function () { await shell.executeLine("log.info('This is a custom entry')"); + expect(shell.assertNoErrors()); await eventually(async () => { const log = await readLogfile(); expect( @@ -1531,14 +1527,11 @@ describe('e2e', function () { ); const filename = path.resolve( __dirname, - '..', - '..', - 'e2e-tests', - 'test', 'fixtures', 'custom-log-info.js' ); await shell.executeLine(`load(${JSON.stringify(filename)})`); + expect(shell.assertNoErrors()); await eventually(async () => { const log = await readLogfile(); expect( diff --git a/packages/shell-api/src/shell-instance-state.ts b/packages/shell-api/src/shell-instance-state.ts index 4ded72d02a..b4e7e7db9b 100644 --- a/packages/shell-api/src/shell-instance-state.ts +++ b/packages/shell-api/src/shell-instance-state.ts @@ -35,7 +35,7 @@ import NoDatabase from './no-db'; import type { ShellBson } from './shell-bson'; import constructShellBson from './shell-bson'; import { Streams } from './streams'; -import constructShellLog, { type ShellLog } from './shell-log'; +import ShellLog from './shell-log'; /** * The subset of CLI options that is relevant for the shell API's behavior itself. @@ -189,7 +189,7 @@ export default class ShellInstanceState { this.initialServiceProvider = initialServiceProvider; this.messageBus = messageBus; this.shellApi = new ShellApi(this); - this.shellLog = constructShellLog(this.messageBus); + this.shellLog = new ShellLog(this); this.shellBson = constructShellBson( initialServiceProvider.bsonLibrary, (msg: string) => { diff --git a/packages/shell-api/src/shell-log.ts b/packages/shell-api/src/shell-log.ts index a2a9217efa..49eb9cb2fc 100644 --- a/packages/shell-api/src/shell-log.ts +++ b/packages/shell-api/src/shell-log.ts @@ -1,6 +1,16 @@ -import type { MongoshBus } from '@mongosh/types'; +import type ShellInstanceState from './shell-instance-state'; +import { shellApiClassDefault, ShellApiClass } from './decorators'; -export interface ShellLog { +const instanceStateSymbol = Symbol.for('@@mongosh.instanceState'); + +/** + * This class contains the *global log* property that is considered part of the immediate shell API. + */ +@shellApiClassDefault +export default class ShellLog extends ShellApiClass { + // Use symbols to make sure these are *not* among the things copied over into + // the global scope. + [instanceStateSymbol]: ShellInstanceState; log: { info: (message: string, attr?: unknown) => void; warn: (message: string, attr?: unknown) => void; @@ -8,47 +18,51 @@ export interface ShellLog { fatal: (message: string, attr?: unknown) => void; debug: (message: string, attr?: unknown, level?: 1 | 2 | 3 | 4 | 5) => void; }; -} -export default function constructShellLog(messageBus: MongoshBus): ShellLog { - return { - log: { + get _instanceState(): ShellInstanceState { + return this[instanceStateSymbol]; + } + + constructor(instanceState: ShellInstanceState) { + super(); + this[instanceStateSymbol] = instanceState; + this.log = { info(message: string, attr?: unknown) { - messageBus.emit('mongosh:write-custom-log', { + instanceState.messageBus.emit('mongosh:write-custom-log', { method: 'info', message, attr, }); }, warn(message: string, attr?: unknown) { - messageBus.emit('mongosh:write-custom-log', { + instanceState.messageBus.emit('mongosh:write-custom-log', { method: 'warn', message, attr, }); }, error(message: string, attr?: unknown) { - messageBus.emit('mongosh:write-custom-log', { + instanceState.messageBus.emit('mongosh:write-custom-log', { method: 'error', message, attr, }); }, fatal(message: string, attr?: unknown) { - messageBus.emit('mongosh:write-custom-log', { + instanceState.messageBus.emit('mongosh:write-custom-log', { method: 'fatal', message, attr, }); }, debug(message: string, attr?: unknown, level?: 1 | 2 | 3 | 4 | 5) { - messageBus.emit('mongosh:write-custom-log', { + instanceState.messageBus.emit('mongosh:write-custom-log', { method: 'debug', message, attr, level, }); }, - }, - }; + }; + } } From 12a3763bb4e2cfe67f24123a92396133f332c27e Mon Sep 17 00:00:00 2001 From: Alena Khineika Date: Fri, 24 Jan 2025 16:00:14 +0100 Subject: [PATCH 4/8] feat: add todo for the log help --- packages/i18n/src/locales/en_US.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/packages/i18n/src/locales/en_US.ts b/packages/i18n/src/locales/en_US.ts index 3e9478989f..ea547e736e 100644 --- a/packages/i18n/src/locales/en_US.ts +++ b/packages/i18n/src/locales/en_US.ts @@ -132,6 +132,19 @@ const translations: Catalog = { 'service-provider-node-driver': {}, 'shell-api': { classes: { + ShellLog: { + help: { + description: 'Shell log methods', + link: '#', + attributes: { + // TODO(MONGOSH-1995): Print help for the global log property. + log: { + description: 'Writes a custom entry to the log file', + example: 'log.info("Custom message")', + }, + }, + }, + }, ShellApi: { help: { description: 'Shell Help', From a3da1cecc393a07904bd36bb2478537e9717431e Mon Sep 17 00:00:00 2001 From: Alena Khineika Date: Fri, 24 Jan 2025 16:19:49 +0100 Subject: [PATCH 5/8] test: extend expect cases --- packages/e2e-tests/test/e2e.spec.ts | 12 +++++++----- .../logging/src/setup-logger-and-telemetry.spec.ts | 14 ++++++++++++-- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/packages/e2e-tests/test/e2e.spec.ts b/packages/e2e-tests/test/e2e.spec.ts index 4b52d0e630..2c90123468 100644 --- a/packages/e2e-tests/test/e2e.spec.ts +++ b/packages/e2e-tests/test/e2e.spec.ts @@ -1512,11 +1512,13 @@ describe('e2e', function () { expect(shell.assertNoErrors()); await eventually(async () => { const log = await readLogfile(); - expect( - log.filter((logEntry) => - logEntry.msg.includes('This is a custom entry') - ) - ).to.have.lengthOf(1); + const customLogEntry = log.filter((logEntry) => + logEntry.msg.includes('This is a custom entry') + ); + expect(customLogEntry).to.have.lengthOf(1); + expect(customLogEntry[0].s).to.be.equal('I'); + expect(customLogEntry[0].c).to.be.equal('MONGOSH-SCRIPTS'); + expect(customLogEntry[0].ctx).to.be.equal('custom-log'); }); }); diff --git a/packages/logging/src/setup-logger-and-telemetry.spec.ts b/packages/logging/src/setup-logger-and-telemetry.spec.ts index 29226427c7..c0c92678c8 100644 --- a/packages/logging/src/setup-logger-and-telemetry.spec.ts +++ b/packages/logging/src/setup-logger-and-telemetry.spec.ts @@ -868,10 +868,15 @@ describe('setupLoggerAndTelemetry', function () { bus.emit('mongosh:write-custom-log', { method: 'debug', - message: 'Debug', + message: 'Debug with level', level: 1, }); + bus.emit('mongosh:write-custom-log', { + method: 'debug', + message: 'Debug without level', + }); + expect(logOutput[0].msg).to.equal('Connecting to server'); expect(logOutput[0].attr.connectionUri).to.equal('mongodb://localhost/'); expect(logOutput[0].attr.is_localhost).to.equal(true); @@ -903,7 +908,12 @@ describe('setupLoggerAndTelemetry', function () { expect(logOutput[5].s).to.equal('D1'); expect(logOutput[5].c).to.equal('MONGOSH-SCRIPTS'); expect(logOutput[5].ctx).to.equal('custom-log'); - expect(logOutput[5].msg).to.equal('Debug'); + expect(logOutput[5].msg).to.equal('Debug with level'); + + expect(logOutput[6].s).to.equal('D1'); + expect(logOutput[6].c).to.equal('MONGOSH-SCRIPTS'); + expect(logOutput[6].ctx).to.equal('custom-log'); + expect(logOutput[6].msg).to.equal('Debug without level'); expect(analyticsOutput).to.deep.equal([ [ From 4d917a73b288e7b38bbb0b0af84a70e2957fa534 Mon Sep 17 00:00:00 2001 From: Alena Khineika Date: Tue, 28 Jan 2025 11:40:54 +0100 Subject: [PATCH 6/8] fix: disable lint for log --- packages/e2e-tests/test/fixtures/custom-log-info.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/e2e-tests/test/fixtures/custom-log-info.js b/packages/e2e-tests/test/fixtures/custom-log-info.js index 7855b6112d..ed0516bf54 100644 --- a/packages/e2e-tests/test/fixtures/custom-log-info.js +++ b/packages/e2e-tests/test/fixtures/custom-log-info.js @@ -1 +1,2 @@ +// eslint-disable-next-line no-undef log.info('Hi there'); From 684e216bc3abb16079bd0f54226770b2ff37c788 Mon Sep 17 00:00:00 2001 From: Alena Khineika Date: Tue, 28 Jan 2025 14:38:54 +0100 Subject: [PATCH 7/8] refactor: move methods to the class itself --- packages/i18n/src/locales/en_US.ts | 22 ++++- .../shell-api/src/shell-instance-state.ts | 2 +- packages/shell-api/src/shell-log.ts | 82 +++++++++---------- 3 files changed, 57 insertions(+), 49 deletions(-) diff --git a/packages/i18n/src/locales/en_US.ts b/packages/i18n/src/locales/en_US.ts index ea547e736e..b7e69e72ce 100644 --- a/packages/i18n/src/locales/en_US.ts +++ b/packages/i18n/src/locales/en_US.ts @@ -138,9 +138,25 @@ const translations: Catalog = { link: '#', attributes: { // TODO(MONGOSH-1995): Print help for the global log property. - log: { - description: 'Writes a custom entry to the log file', - example: 'log.info("Custom message")', + info: { + description: 'Writes a custom info message to the log file', + example: 'log.info("Custom info message")', + }, + warn: { + description: 'Writes a custom warning message to the log file', + example: 'log.warn("Custom warning message")', + }, + error: { + description: 'Writes a custom error message to the log file', + example: 'log.error("Custom error message")', + }, + fatal: { + description: 'Writes a custom fatal message to the log file', + example: 'log.fatal("Custom fatal message")', + }, + debug: { + description: 'Writes a custom debug message to the log file', + example: 'log.debug("Custom debug message")', }, }, }, diff --git a/packages/shell-api/src/shell-instance-state.ts b/packages/shell-api/src/shell-instance-state.ts index b4e7e7db9b..13877c2e9d 100644 --- a/packages/shell-api/src/shell-instance-state.ts +++ b/packages/shell-api/src/shell-instance-state.ts @@ -365,7 +365,7 @@ export default class ShellInstanceState { }); } - Object.assign(contextObject, this.shellLog); + Object.assign(contextObject, { log: this.shellLog }); this.messageBus.emit('mongosh:setCtx', { method: 'setCtx', arguments: {} }); } diff --git a/packages/shell-api/src/shell-log.ts b/packages/shell-api/src/shell-log.ts index 49eb9cb2fc..0bade750d8 100644 --- a/packages/shell-api/src/shell-log.ts +++ b/packages/shell-api/src/shell-log.ts @@ -11,13 +11,6 @@ export default class ShellLog extends ShellApiClass { // Use symbols to make sure these are *not* among the things copied over into // the global scope. [instanceStateSymbol]: ShellInstanceState; - log: { - info: (message: string, attr?: unknown) => void; - warn: (message: string, attr?: unknown) => void; - error: (message: string, attr?: unknown) => void; - fatal: (message: string, attr?: unknown) => void; - debug: (message: string, attr?: unknown, level?: 1 | 2 | 3 | 4 | 5) => void; - }; get _instanceState(): ShellInstanceState { return this[instanceStateSymbol]; @@ -26,43 +19,42 @@ export default class ShellLog extends ShellApiClass { constructor(instanceState: ShellInstanceState) { super(); this[instanceStateSymbol] = instanceState; - this.log = { - info(message: string, attr?: unknown) { - instanceState.messageBus.emit('mongosh:write-custom-log', { - method: 'info', - message, - attr, - }); - }, - warn(message: string, attr?: unknown) { - instanceState.messageBus.emit('mongosh:write-custom-log', { - method: 'warn', - message, - attr, - }); - }, - error(message: string, attr?: unknown) { - instanceState.messageBus.emit('mongosh:write-custom-log', { - method: 'error', - message, - attr, - }); - }, - fatal(message: string, attr?: unknown) { - instanceState.messageBus.emit('mongosh:write-custom-log', { - method: 'fatal', - message, - attr, - }); - }, - debug(message: string, attr?: unknown, level?: 1 | 2 | 3 | 4 | 5) { - instanceState.messageBus.emit('mongosh:write-custom-log', { - method: 'debug', - message, - attr, - level, - }); - }, - }; + } + + info(message: string, attr?: unknown) { + this[instanceStateSymbol].messageBus.emit('mongosh:write-custom-log', { + method: 'info', + message, + attr, + }); + } + warn(message: string, attr?: unknown) { + this[instanceStateSymbol].messageBus.emit('mongosh:write-custom-log', { + method: 'warn', + message, + attr, + }); + } + error(message: string, attr?: unknown) { + this[instanceStateSymbol].messageBus.emit('mongosh:write-custom-log', { + method: 'error', + message, + attr, + }); + } + fatal(message: string, attr?: unknown) { + this[instanceStateSymbol].messageBus.emit('mongosh:write-custom-log', { + method: 'fatal', + message, + attr, + }); + } + debug(message: string, attr?: unknown, level?: 1 | 2 | 3 | 4 | 5) { + this[instanceStateSymbol].messageBus.emit('mongosh:write-custom-log', { + method: 'debug', + message, + attr, + level, + }); } } From b3f76c48460fca692ce0037a3c83f937a8fa2982 Mon Sep 17 00:00:00 2001 From: Alena Khineika Date: Tue, 28 Jan 2025 16:33:24 +0100 Subject: [PATCH 8/8] refactor: no default export --- packages/shell-api/src/shell-instance-state.ts | 5 +++-- packages/shell-api/src/shell-log.ts | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/shell-api/src/shell-instance-state.ts b/packages/shell-api/src/shell-instance-state.ts index 13877c2e9d..f14b93b9aa 100644 --- a/packages/shell-api/src/shell-instance-state.ts +++ b/packages/shell-api/src/shell-instance-state.ts @@ -35,7 +35,7 @@ import NoDatabase from './no-db'; import type { ShellBson } from './shell-bson'; import constructShellBson from './shell-bson'; import { Streams } from './streams'; -import ShellLog from './shell-log'; +import { ShellLog } from './shell-log'; /** * The subset of CLI options that is relevant for the shell API's behavior itself. @@ -365,7 +365,8 @@ export default class ShellInstanceState { }); } - Object.assign(contextObject, { log: this.shellLog }); + contextObject.log = this.shellLog; + this.messageBus.emit('mongosh:setCtx', { method: 'setCtx', arguments: {} }); } diff --git a/packages/shell-api/src/shell-log.ts b/packages/shell-api/src/shell-log.ts index 0bade750d8..50325ab1b0 100644 --- a/packages/shell-api/src/shell-log.ts +++ b/packages/shell-api/src/shell-log.ts @@ -7,7 +7,7 @@ const instanceStateSymbol = Symbol.for('@@mongosh.instanceState'); * This class contains the *global log* property that is considered part of the immediate shell API. */ @shellApiClassDefault -export default class ShellLog extends ShellApiClass { +export class ShellLog extends ShellApiClass { // Use symbols to make sure these are *not* among the things copied over into // the global scope. [instanceStateSymbol]: ShellInstanceState;