Skip to content

Commit beaf85b

Browse files
committed
feat: add ability to detach logger and use only telemetry and more tests
Will add some more E2E and unit tests.
1 parent a21ef64 commit beaf85b

File tree

6 files changed

+301
-129
lines changed

6 files changed

+301
-129
lines changed

packages/cli-repl/src/cli-repl.spec.ts

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,27 @@ describe('CliRepl', function () {
238238
});
239239
});
240240

241+
it('does not write to log syntax errors if logging is disabled', async function () {
242+
expect(
243+
(await log()).filter((entry) =>
244+
entry.attr?.stack?.startsWith('SyntaxError:')
245+
)
246+
).to.have.lengthOf(0);
247+
input.write('config.set("disableLogging", true)\n');
248+
await waitEval(cliRepl.bus);
249+
expect(output).includes('Setting "disableLogging" has been changed');
250+
251+
input.write('<cat>\n');
252+
await waitBus(cliRepl.bus, 'mongosh:error');
253+
await eventually(async () => {
254+
expect(
255+
(await log()).filter((entry) =>
256+
entry.attr?.stack?.startsWith('SyntaxError:')
257+
)
258+
).to.have.lengthOf(0);
259+
});
260+
});
261+
241262
it('writes JS errors to the log file', async function () {
242263
input.write('throw new Error("plain js error")\n');
243264
await waitBus(cliRepl.bus, 'mongosh:error');
@@ -1365,29 +1386,16 @@ describe('CliRepl', function () {
13651386

13661387
context('logging configuration', function () {
13671388
it('logging is enabled by default', async function () {
1368-
const onLoggerInitialized = sinon.stub();
1369-
cliRepl.bus.on('mongosh:logger-initialized', onLoggerInitialized);
1389+
const onLogInitialized = sinon.stub();
1390+
cliRepl.bus.on('mongosh:log-initialized', onLogInitialized);
13701391

13711392
await cliRepl.start(await testServer.connectionString(), {});
13721393

13731394
expect(await cliRepl.getConfig('disableLogging')).is.false;
13741395

1375-
expect(onLoggerInitialized).calledOnce;
1396+
expect(onLogInitialized).calledOnce;
13761397
expect(cliRepl.logWriter).is.instanceOf(MongoLogWriter);
13771398
});
1378-
1379-
it('does not start logging when it is disabled', async function () {
1380-
cliRepl.config.disableLogging = true;
1381-
const onLoggerInitialized = sinon.stub();
1382-
cliRepl.bus.on('mongosh:logger-initialized', onLoggerInitialized);
1383-
1384-
await cliRepl.start(await testServer.connectionString(), {});
1385-
1386-
expect(await cliRepl.getConfig('disableLogging')).is.true;
1387-
expect(onLoggerInitialized).not.called;
1388-
1389-
expect(cliRepl.logWriter).is.undefined;
1390-
});
13911399
});
13921400

13931401
it('times out fast', async function () {

packages/cli-repl/src/cli-repl.ts

Lines changed: 42 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,11 @@ import type { MongoLogWriter } from 'mongodb-log-writer';
3030
import { MongoLogManager, mongoLogId } from 'mongodb-log-writer';
3131
import type { MongoshNodeReplOptions, MongoshIOProvider } from './mongosh-repl';
3232
import MongoshNodeRepl from './mongosh-repl';
33+
import { MongoshLoggingAndTelemetry } from '@mongosh/logging';
3334
import {
3435
ToggleableAnalytics,
3536
ThrottledAnalytics,
3637
SampledAnalytics,
37-
MongoshLoggingAndTelemetry,
3838
} from '@mongosh/logging';
3939
import type { MongoshBus } from '@mongosh/types';
4040
import {
@@ -119,7 +119,7 @@ export class CliRepl implements MongoshIOProvider {
119119
config: CliUserConfigOnDisk;
120120
globalConfig: Partial<CliUserConfig> | null = null;
121121
globalConfigPaths: string[];
122-
logManager: MongoLogManager;
122+
logManager?: MongoLogManager;
123123
logWriter?: MongoLogWriter;
124124
input: Readable;
125125
output: Writable;
@@ -140,6 +140,8 @@ export class CliRepl implements MongoshIOProvider {
140140
fetchMongoshUpdateUrlRegardlessOfCiEnvironment = false; // for testing
141141
cachedGlibcVersion: null | string | undefined = null;
142142

143+
private loggingAndTelemetry: MongoshLoggingAndTelemetry | undefined;
144+
143145
/**
144146
* Instantiate the new CLI Repl.
145147
*/
@@ -189,16 +191,6 @@ export class CliRepl implements MongoshIOProvider {
189191
});
190192
});
191193

192-
this.logManager = new MongoLogManager({
193-
directory: this.shellHomeDirectory.localPath('.'),
194-
retentionDays: 30,
195-
maxLogFileCount: +(
196-
process.env.MONGOSH_TEST_ONLY_MAX_LOG_FILE_COUNT || 100
197-
),
198-
onerror: (err: Error) => this.bus.emit('mongosh:error', err, 'log'),
199-
onwarn: (err: Error, path: string) =>
200-
this.warnAboutInaccessibleFile(err, path),
201-
});
202194
this.agent = useOrCreateAgent(this.proxyOptions);
203195
this.updateNotificationManager = new UpdateNotificationManager({
204196
proxyOptions: this.agent,
@@ -257,9 +249,24 @@ export class CliRepl implements MongoshIOProvider {
257249
}
258250

259251
/** Setup log writer and start logging. */
260-
private async startLogging(
261-
loggingAndTelemetry: MongoshLoggingAndTelemetry
262-
): Promise<void> {
252+
private async startLogging(): Promise<void> {
253+
if (!this.loggingAndTelemetry) {
254+
throw new Error('Logging and telemetry not setup');
255+
}
256+
257+
if (!this.logManager) {
258+
this.logManager = new MongoLogManager({
259+
directory: this.shellHomeDirectory.localPath('.'),
260+
retentionDays: 30,
261+
maxLogFileCount: +(
262+
process.env.MONGOSH_TEST_ONLY_MAX_LOG_FILE_COUNT || 100
263+
),
264+
onerror: (err: Error) => this.bus.emit('mongosh:error', err, 'log'),
265+
onwarn: (err: Error, path: string) =>
266+
this.warnAboutInaccessibleFile(err, path),
267+
});
268+
}
269+
263270
await this.logManager.cleanupOldLogFiles();
264271
markTime(TimingCategories.Logging, 'cleaned up log files');
265272
const logger = await this.logManager.createLogWriter();
@@ -269,7 +276,7 @@ export class CliRepl implements MongoshIOProvider {
269276
}
270277

271278
this.logWriter = logger;
272-
loggingAndTelemetry.setupLogger(logger);
279+
this.loggingAndTelemetry.attachLogger(logger);
273280

274281
markTime(TimingCategories.Logging, 'instantiated log writer');
275282
logger.info('MONGOSH', mongoLogId(1_000_000_000), 'log', 'Starting log', {
@@ -343,7 +350,7 @@ export class CliRepl implements MongoshIOProvider {
343350

344351
markTime(TimingCategories.Telemetry, 'created analytics instance');
345352

346-
const loggingAndTelemetry = new MongoshLoggingAndTelemetry(
353+
this.loggingAndTelemetry = new MongoshLoggingAndTelemetry(
347354
this.bus,
348355
this.toggleableAnalytics,
349356
{
@@ -354,7 +361,7 @@ export class CliRepl implements MongoshIOProvider {
354361
},
355362
version
356363
);
357-
loggingAndTelemetry.setup();
364+
this.loggingAndTelemetry.setup();
358365

359366
markTime(TimingCategories.Telemetry, 'completed telemetry setup');
360367

@@ -374,10 +381,7 @@ export class CliRepl implements MongoshIOProvider {
374381
this.globalConfig = await this.loadGlobalConfigFile();
375382
markTime(TimingCategories.UserConfigLoading, 'read global config files');
376383

377-
const disableLogging = await this.getConfig('disableLogging');
378-
if (disableLogging !== true) {
379-
await this.startLogging(loggingAndTelemetry);
380-
}
384+
await this.setLoggingEnabled(!(await this.getConfig('disableLogging')));
381385

382386
// Needs to happen after loading the mongosh config file(s)
383387
void this.fetchMongoshUpdateUrl();
@@ -622,6 +626,19 @@ export class CliRepl implements MongoshIOProvider {
622626
);
623627
}
624628

629+
async setLoggingEnabled(enabled: boolean): Promise<void> {
630+
if (enabled) {
631+
if (!this.logWriter) {
632+
await this.startLogging();
633+
}
634+
// Do nothing if there is already an active log writer.
635+
} else {
636+
this.loggingAndTelemetry?.detachLogger();
637+
this.logWriter?.destroy();
638+
this.logWriter = undefined;
639+
}
640+
}
641+
625642
setTelemetryEnabled(enabled: boolean): void {
626643
if (this.globalConfig === null) {
627644
// This happens when the per-user config file is loaded before we have
@@ -908,6 +925,9 @@ export class CliRepl implements MongoshIOProvider {
908925
anonymousId: this.config.telemetryAnonymousId,
909926
});
910927
}
928+
if (key === 'disableLogging') {
929+
await this.setLoggingEnabled(!value);
930+
}
911931
try {
912932
await this.configDirectory.writeConfigFile(this.config);
913933
} catch (err: any) {

packages/e2e-tests/test/e2e.spec.ts

Lines changed: 62 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { once } from 'events';
2222
import type { AddressInfo } from 'net';
2323
const { EJSON } = bson;
2424
import { sleep } from './util-helpers';
25+
import type { LogEntry } from '@mongosh/shell-api/src/log-entry';
2526

2627
const jsContextFlagCombinations: `--jsContext=${'plain-vm' | 'repl'}`[][] = [
2728
[],
@@ -1360,10 +1361,9 @@ describe('e2e', function () {
13601361
let shell: TestShell;
13611362
let configPath: string;
13621363
let logBasePath: string;
1363-
let logPath: string;
13641364
let historyPath: string;
13651365
let readConfig: () => Promise<any>;
1366-
let readLogfile: () => Promise<any[]>;
1366+
let readLogfile: () => Promise<LogEntry[]>;
13671367
let startTestShell: (...extraArgs: string[]) => Promise<TestShell>;
13681368

13691369
beforeEach(function () {
@@ -1400,7 +1400,13 @@ describe('e2e', function () {
14001400
}
14011401
readConfig = async () =>
14021402
EJSON.parse(await fs.readFile(configPath, 'utf8'));
1403-
readLogfile = async () => readReplLogfile(logPath);
1403+
readLogfile = async () => {
1404+
if (!shell.logId) {
1405+
throw new Error('Shell does not have a logId associated with it');
1406+
}
1407+
const logPath = path.join(logBasePath, `${shell.logId}_log`);
1408+
return readReplLogfile(logPath);
1409+
};
14041410
startTestShell = async (...extraArgs: string[]) => {
14051411
const shell = this.startTestShell({
14061412
args: ['--nodb', ...extraArgs],
@@ -1431,7 +1437,6 @@ describe('e2e', function () {
14311437
beforeEach(async function () {
14321438
await fs.mkdir(homedir, { recursive: true });
14331439
shell = await startTestShell();
1434-
logPath = path.join(logBasePath, `${shell.logId}_log`);
14351440
});
14361441

14371442
describe('config file', function () {
@@ -1510,14 +1515,61 @@ describe('e2e', function () {
15101515
});
15111516

15121517
describe('log file', function () {
1518+
it('does not create a log if global config has disableLogging', async function () {
1519+
const globalConfig = path.join(homedir, 'globalconfig.conf');
1520+
await fs.writeFile(globalConfig, 'mongosh:\n disableLogging: true');
1521+
shell = this.startTestShell({
1522+
args: ['--nodb'],
1523+
env: {
1524+
...env,
1525+
MONGOSH_GLOBAL_CONFIG_FILE_FOR_TESTING: globalConfig,
1526+
},
1527+
forceTerminal: true,
1528+
});
1529+
await shell.waitForPrompt();
1530+
expect(
1531+
await shell.executeLine('config.get("disableLogging")')
1532+
).to.include('true');
1533+
shell.assertNoErrors();
1534+
1535+
expect(await shell.executeLine('print(123 + 456)')).to.include('579');
1536+
expect(shell.logId).equals(null);
1537+
});
1538+
15131539
it('creates a log file that keeps track of session events', async function () {
15141540
expect(await shell.executeLine('print(123 + 456)')).to.include('579');
1515-
await eventually(async () => {
1516-
const log = await readLogfile();
1517-
expect(
1518-
log.filter((logEntry) => /Evaluating input/.test(logEntry.msg))
1519-
).to.have.lengthOf(1);
1520-
});
1541+
const log = await readLogfile();
1542+
expect(
1543+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
1544+
log.filter(
1545+
(logEntry) => logEntry.attr?.input === 'print(123 + 456)'
1546+
)
1547+
).to.have.lengthOf(1);
1548+
});
1549+
1550+
it('does not write to the log file after disableLogging is set to true', async function () {
1551+
expect(await shell.executeLine('print(123 + 456)')).to.include('579');
1552+
const log = await readLogfile();
1553+
expect(
1554+
log.filter(
1555+
(logEntry) => logEntry.attr?.input === 'print(123 + 456)'
1556+
)
1557+
).to.have.lengthOf(1);
1558+
1559+
await shell.executeLine(`config.set("disableLogging", true)`);
1560+
expect(await shell.executeLine('print(579 - 123)')).to.include('456');
1561+
1562+
const logAfterDisabling = await readLogfile();
1563+
expect(
1564+
logAfterDisabling.filter(
1565+
(logEntry) => logEntry.attr?.input === 'print(579 - 123)'
1566+
)
1567+
).to.have.lengthOf(0);
1568+
expect(
1569+
logAfterDisabling.filter(
1570+
(logEntry) => logEntry.attr?.input === 'print(123 + 456)'
1571+
)
1572+
).to.have.lengthOf(1);
15211573
});
15221574

15231575
it('includes information about the driver version', async function () {

0 commit comments

Comments
 (0)