Skip to content

Commit 2007b9f

Browse files
committed
feat(logging): add option to set log location MONGOSH-1983
1 parent b340d79 commit 2007b9f

File tree

4 files changed

+141
-5
lines changed

4 files changed

+141
-5
lines changed

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,7 @@ describe('CliRepl', function () {
322322
'browser',
323323
'updateURL',
324324
'disableLogging',
325+
'logLocation',
325326
] satisfies (keyof CliUserConfig)[]);
326327
});
327328

@@ -1426,6 +1427,15 @@ describe('CliRepl', function () {
14261427
)
14271428
).to.have.lengthOf(1);
14281429
});
1430+
1431+
it('can set the log location', async function () {
1432+
const testPath = path.join('./test', 'path');
1433+
cliRepl.config.logLocation = testPath;
1434+
await cliRepl.start(await testServer.connectionString(), {});
1435+
1436+
expect(cliRepl.getConfig('logLocation')).is.true;
1437+
expect(cliRepl.logWriter?.logFilePath).equals(testPath);
1438+
});
14291439
});
14301440

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

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,9 @@ export class CliRepl implements MongoshIOProvider {
256256
}
257257

258258
this.logManager ??= new MongoLogManager({
259-
directory: this.shellHomeDirectory.localPath('.'),
259+
directory:
260+
(await this.getConfig('logLocation')) ||
261+
this.shellHomeDirectory.localPath('.'),
260262
retentionDays: 30,
261263
maxLogFileCount: +(
262264
process.env.MONGOSH_TEST_ONLY_MAX_LOG_FILE_COUNT || 100

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

Lines changed: 121 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,11 @@ import { promisify } from 'util';
1515
import path from 'path';
1616
import os from 'os';
1717
import type { MongoLogEntryFromFile } from './repl-helpers';
18-
import { readReplLogFile, setTemporaryHomeDirectory } from './repl-helpers';
18+
import {
19+
readReplLogFile,
20+
setTemporaryHomeDirectory,
21+
useTmpdir,
22+
} from './repl-helpers';
1923
import { bson } from '@mongosh/service-provider-core';
2024
import type { Server as HTTPServer } from 'http';
2125
import { createServer as createHTTPServer } from 'http';
@@ -1356,7 +1360,9 @@ describe('e2e', function () {
13561360
let logBasePath: string;
13571361
let historyPath: string;
13581362
let readConfig: () => Promise<any>;
1359-
let readLogFile: <T extends MongoLogEntryFromFile>() => Promise<T[]>;
1363+
let readLogFile: <T extends MongoLogEntryFromFile>(
1364+
customBasePath?: string
1365+
) => Promise<T[]>;
13601366
let startTestShell: (...extraArgs: string[]) => Promise<TestShell>;
13611367

13621368
beforeEach(function () {
@@ -1393,11 +1399,16 @@ describe('e2e', function () {
13931399
}
13941400
readConfig = async () =>
13951401
EJSON.parse(await fs.readFile(configPath, 'utf8'));
1396-
readLogFile = async <T extends MongoLogEntryFromFile>(): Promise<T[]> => {
1402+
readLogFile = async <T extends MongoLogEntryFromFile>(
1403+
customBasePath?: string
1404+
): Promise<T[]> => {
13971405
if (!shell.logId) {
13981406
throw new Error('Shell does not have a logId associated with it');
13991407
}
1400-
const logPath = path.join(logBasePath, `${shell.logId}_log`);
1408+
const logPath = path.join(
1409+
customBasePath ?? logBasePath,
1410+
`${shell.logId}_log`
1411+
);
14011412
return readReplLogFile<T>(logPath);
14021413
};
14031414
startTestShell = async (...extraArgs: string[]) => {
@@ -1557,6 +1568,112 @@ describe('e2e', function () {
15571568
).to.have.lengthOf(1);
15581569
});
15591570

1571+
describe('with custom log location', function () {
1572+
const customLogDir = useTmpdir();
1573+
1574+
it('fails with relative or invalid paths', async function () {
1575+
const globalConfig = path.join(homedir, 'globalconfig.conf');
1576+
await fs.writeFile(
1577+
globalConfig,
1578+
`mongosh:\n logLocation: "./some-relative-path"`
1579+
);
1580+
1581+
shell = this.startTestShell({
1582+
args: ['--nodb'],
1583+
env: {
1584+
...env,
1585+
MONGOSH_GLOBAL_CONFIG_FILE_FOR_TESTING: globalConfig,
1586+
},
1587+
forceTerminal: true,
1588+
});
1589+
await shell.waitForPrompt();
1590+
shell.assertContainsOutput('Ignoring config option "logLocation"');
1591+
shell.assertContainsOutput(
1592+
'must be a valid absolute path or empty'
1593+
);
1594+
1595+
expect(
1596+
await shell.executeLine(
1597+
'config.set("logLocation", "[123123123123]")'
1598+
)
1599+
).contains(
1600+
'Cannot set option "logLocation": logLocation must be a valid absolute path or empty'
1601+
);
1602+
});
1603+
1604+
it('gets created according to logLocation, if set', async function () {
1605+
const globalConfig = path.join(homedir, 'globalconfig.conf');
1606+
await fs.writeFile(
1607+
globalConfig,
1608+
`mongosh:\n logLocation: "${customLogDir.path}"`
1609+
);
1610+
1611+
shell = this.startTestShell({
1612+
args: ['--nodb'],
1613+
env: {
1614+
...env,
1615+
MONGOSH_GLOBAL_CONFIG_FILE_FOR_TESTING: globalConfig,
1616+
},
1617+
forceTerminal: true,
1618+
});
1619+
await shell.waitForPrompt();
1620+
expect(
1621+
await shell.executeLine('config.get("logLocation")')
1622+
).contains(customLogDir.path);
1623+
1624+
try {
1625+
await readLogFile();
1626+
expect.fail('expected to throw');
1627+
} catch (error) {
1628+
expect((error as Error).message).includes(
1629+
'no such file or directory'
1630+
);
1631+
}
1632+
1633+
expect(
1634+
(await readLogFile(customLogDir.path)).some(
1635+
(log) => log.attr?.input === 'config.get("logLocation")'
1636+
)
1637+
).is.true;
1638+
});
1639+
1640+
it('setting location while running mongosh does not have an immediate effect on logging', async function () {
1641+
expect(
1642+
await shell.executeLine('config.get("logLocation")')
1643+
).does.not.contain(customLogDir.path);
1644+
const oldLogId = shell.logId;
1645+
1646+
const oldLogEntries = await readLogFile();
1647+
await shell.executeLine(
1648+
`config.set("logLocation", "${customLogDir.path}")`
1649+
);
1650+
1651+
await shell.waitForPrompt();
1652+
expect(
1653+
await shell.executeLine('config.get("logLocation")')
1654+
).contains(customLogDir.path);
1655+
1656+
expect(shell.logId).equals(oldLogId);
1657+
1658+
const currentLogEntries = await readLogFile();
1659+
1660+
try {
1661+
await readLogFile(customLogDir.path);
1662+
expect.fail('expected to throw');
1663+
} catch (error) {
1664+
expect((error as Error).message).includes(
1665+
'no such file or directory'
1666+
);
1667+
}
1668+
expect(
1669+
currentLogEntries.some(
1670+
(log) => log.attr?.input === 'config.get("logLocation")'
1671+
)
1672+
).is.true;
1673+
expect(currentLogEntries.length - oldLogEntries.length).equals(2);
1674+
});
1675+
});
1676+
15601677
it('creates a log file that keeps track of session events', async function () {
15611678
expect(await shell.executeLine('print(123 + 456)')).to.include('579');
15621679
const log = await readLogFile();

packages/types/src/index.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { ConnectEventMap } from '@mongodb-js/devtools-connect';
2+
import path from 'path';
23

34
export interface ApiEventArguments {
45
pipeline?: any[];
@@ -507,6 +508,7 @@ export class CliUserConfig extends SnippetShellUserConfig {
507508
browser: undefined | false | string = undefined;
508509
updateURL = 'https://downloads.mongodb.com/compass/mongosh.json';
509510
disableLogging = false;
511+
logLocation: string | undefined = undefined;
510512
}
511513

512514
export class CliUserConfigValidator extends SnippetShellUserConfigValidator {
@@ -579,6 +581,11 @@ export class CliUserConfigValidator extends SnippetShellUserConfigValidator {
579581
return `${key} must be a valid URL or empty`;
580582
}
581583
return null;
584+
case 'logLocation':
585+
if (typeof value !== 'string' || !path.isAbsolute(value)) {
586+
return `${key} must be a valid absolute path or empty`;
587+
}
588+
return null;
582589
default:
583590
return super.validate(
584591
key as keyof SnippetShellUserConfig,

0 commit comments

Comments
 (0)