Skip to content

Commit 03a4dfc

Browse files
feat(logging): custom log entries MONGOSH-1989 (#2322)
* feat(logging): custom log entries MONGOSH-1989 * refactor: finalise pr * refactor: extend shell log from shell api * feat: add todo for the log help * test: extend expect cases * fix: disable lint for log * refactor: move methods to the class itself * refactor: no default export
1 parent 14699d1 commit 03a4dfc

File tree

9 files changed

+272
-1
lines changed

9 files changed

+272
-1
lines changed

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

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1506,6 +1506,46 @@ describe('e2e', function () {
15061506
).to.have.lengthOf(1);
15071507
});
15081508
});
1509+
1510+
it('writes custom log directly', async function () {
1511+
await shell.executeLine("log.info('This is a custom entry')");
1512+
expect(shell.assertNoErrors());
1513+
await eventually(async () => {
1514+
const log = await readLogfile();
1515+
const customLogEntry = log.filter((logEntry) =>
1516+
logEntry.msg.includes('This is a custom entry')
1517+
);
1518+
expect(customLogEntry).to.have.lengthOf(1);
1519+
expect(customLogEntry[0].s).to.be.equal('I');
1520+
expect(customLogEntry[0].c).to.be.equal('MONGOSH-SCRIPTS');
1521+
expect(customLogEntry[0].ctx).to.be.equal('custom-log');
1522+
});
1523+
});
1524+
1525+
it('writes custom log when loads a script', async function () {
1526+
const connectionString = await testServer.connectionString();
1527+
await shell.executeLine(
1528+
`connect(${JSON.stringify(connectionString)})`
1529+
);
1530+
const filename = path.resolve(
1531+
__dirname,
1532+
'fixtures',
1533+
'custom-log-info.js'
1534+
);
1535+
await shell.executeLine(`load(${JSON.stringify(filename)})`);
1536+
expect(shell.assertNoErrors());
1537+
await eventually(async () => {
1538+
const log = await readLogfile();
1539+
expect(
1540+
log.filter((logEntry) =>
1541+
logEntry.msg.includes('Initiating connection attemp')
1542+
)
1543+
).to.have.lengthOf(1);
1544+
expect(
1545+
log.filter((logEntry) => logEntry.msg.includes('Hi there'))
1546+
).to.have.lengthOf(1);
1547+
});
1548+
});
15091549
});
15101550

15111551
describe('history file', function () {
@@ -1931,7 +1971,7 @@ describe('e2e', function () {
19311971
__dirname,
19321972
'..',
19331973
'..',
1934-
'cli-repl',
1974+
'e2e-tests',
19351975
'test',
19361976
'fixtures',
19371977
'simple-console-log.js'
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// eslint-disable-next-line no-undef
2+
log.info('Hi there');

packages/i18n/src/locales/en_US.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,35 @@ const translations: Catalog = {
132132
'service-provider-node-driver': {},
133133
'shell-api': {
134134
classes: {
135+
ShellLog: {
136+
help: {
137+
description: 'Shell log methods',
138+
link: '#',
139+
attributes: {
140+
// TODO(MONGOSH-1995): Print help for the global log property.
141+
info: {
142+
description: 'Writes a custom info message to the log file',
143+
example: 'log.info("Custom info message")',
144+
},
145+
warn: {
146+
description: 'Writes a custom warning message to the log file',
147+
example: 'log.warn("Custom warning message")',
148+
},
149+
error: {
150+
description: 'Writes a custom error message to the log file',
151+
example: 'log.error("Custom error message")',
152+
},
153+
fatal: {
154+
description: 'Writes a custom fatal message to the log file',
155+
example: 'log.fatal("Custom fatal message")',
156+
},
157+
debug: {
158+
description: 'Writes a custom debug message to the log file',
159+
example: 'log.debug("Custom debug message")',
160+
},
161+
},
162+
},
163+
},
135164
ShellApi: {
136165
help: {
137166
description: 'Shell Help',

packages/logging/src/setup-logger-and-telemetry.spec.ts

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -822,4 +822,115 @@ describe('setupLoggerAndTelemetry', function () {
822822
expect(logOutput).to.have.lengthOf(0);
823823
expect(analyticsOutput).to.be.empty;
824824
});
825+
826+
it('tracks custom logging events', function () {
827+
setupLoggerAndTelemetry(
828+
bus,
829+
logger,
830+
analytics,
831+
{
832+
platform: process.platform,
833+
arch: process.arch,
834+
},
835+
'1.0.0'
836+
);
837+
expect(logOutput).to.have.lengthOf(0);
838+
expect(analyticsOutput).to.be.empty;
839+
840+
bus.emit('mongosh:connect', {
841+
uri: 'mongodb://localhost/',
842+
is_localhost: true,
843+
is_atlas: false,
844+
resolved_hostname: 'localhost',
845+
node_version: 'v12.19.0',
846+
});
847+
848+
bus.emit('mongosh:write-custom-log', {
849+
method: 'info',
850+
message: 'This is an info message',
851+
attr: { some: 'value' },
852+
});
853+
854+
bus.emit('mongosh:write-custom-log', {
855+
method: 'warn',
856+
message: 'This is a warn message',
857+
});
858+
859+
bus.emit('mongosh:write-custom-log', {
860+
method: 'error',
861+
message: 'Error!',
862+
});
863+
864+
bus.emit('mongosh:write-custom-log', {
865+
method: 'fatal',
866+
message: 'Fatal!',
867+
});
868+
869+
bus.emit('mongosh:write-custom-log', {
870+
method: 'debug',
871+
message: 'Debug with level',
872+
level: 1,
873+
});
874+
875+
bus.emit('mongosh:write-custom-log', {
876+
method: 'debug',
877+
message: 'Debug without level',
878+
});
879+
880+
expect(logOutput[0].msg).to.equal('Connecting to server');
881+
expect(logOutput[0].attr.connectionUri).to.equal('mongodb://localhost/');
882+
expect(logOutput[0].attr.is_localhost).to.equal(true);
883+
expect(logOutput[0].attr.is_atlas).to.equal(false);
884+
expect(logOutput[0].attr.atlas_hostname).to.equal(null);
885+
expect(logOutput[0].attr.node_version).to.equal('v12.19.0');
886+
887+
expect(logOutput[1].s).to.equal('I');
888+
expect(logOutput[1].c).to.equal('MONGOSH-SCRIPTS');
889+
expect(logOutput[1].ctx).to.equal('custom-log');
890+
expect(logOutput[1].msg).to.equal('This is an info message');
891+
expect(logOutput[1].attr.some).to.equal('value');
892+
893+
expect(logOutput[2].s).to.equal('W');
894+
expect(logOutput[2].c).to.equal('MONGOSH-SCRIPTS');
895+
expect(logOutput[2].ctx).to.equal('custom-log');
896+
expect(logOutput[2].msg).to.equal('This is a warn message');
897+
898+
expect(logOutput[3].s).to.equal('E');
899+
expect(logOutput[3].c).to.equal('MONGOSH-SCRIPTS');
900+
expect(logOutput[3].ctx).to.equal('custom-log');
901+
expect(logOutput[3].msg).to.equal('Error!');
902+
903+
expect(logOutput[4].s).to.equal('F');
904+
expect(logOutput[4].c).to.equal('MONGOSH-SCRIPTS');
905+
expect(logOutput[4].ctx).to.equal('custom-log');
906+
expect(logOutput[4].msg).to.equal('Fatal!');
907+
908+
expect(logOutput[5].s).to.equal('D1');
909+
expect(logOutput[5].c).to.equal('MONGOSH-SCRIPTS');
910+
expect(logOutput[5].ctx).to.equal('custom-log');
911+
expect(logOutput[5].msg).to.equal('Debug with level');
912+
913+
expect(logOutput[6].s).to.equal('D1');
914+
expect(logOutput[6].c).to.equal('MONGOSH-SCRIPTS');
915+
expect(logOutput[6].ctx).to.equal('custom-log');
916+
expect(logOutput[6].msg).to.equal('Debug without level');
917+
918+
expect(analyticsOutput).to.deep.equal([
919+
[
920+
'track',
921+
{
922+
anonymousId: undefined,
923+
event: 'New Connection',
924+
properties: {
925+
mongosh_version: '1.0.0',
926+
session_id: '5fb3c20ee1507e894e5340f3',
927+
is_localhost: true,
928+
is_atlas: false,
929+
atlas_hostname: null,
930+
node_version: 'v12.19.0',
931+
},
932+
},
933+
],
934+
]);
935+
});
825936
});

packages/logging/src/setup-logger-and-telemetry.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import type {
3131
FetchingUpdateMetadataEvent,
3232
FetchingUpdateMetadataCompleteEvent,
3333
SessionStartedEvent,
34+
WriteCustomLogEvent,
3435
} from '@mongosh/types';
3536
import { inspect } from 'util';
3637
import type { MongoLogWriter } from 'mongodb-log-writer';
@@ -140,6 +141,17 @@ export function setupLoggerAndTelemetry(
140141
}
141142
);
142143

144+
bus.on('mongosh:write-custom-log', (event: WriteCustomLogEvent) => {
145+
log[event.method](
146+
'MONGOSH-SCRIPTS',
147+
mongoLogId(1_000_000_054),
148+
'custom-log',
149+
event.message,
150+
event.attr,
151+
event.level
152+
);
153+
});
154+
143155
bus.on('mongosh:connect', function (args: ConnectEvent) {
144156
const { uri, resolved_hostname, ...argsWithoutUriAndHostname } = args;
145157
const connectionUri = uri && redactURICredentials(uri);

packages/shell-api/src/shell-instance-state.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import NoDatabase from './no-db';
3535
import type { ShellBson } from './shell-bson';
3636
import constructShellBson from './shell-bson';
3737
import { Streams } from './streams';
38+
import { ShellLog } from './shell-log';
3839

3940
/**
4041
* The subset of CLI options that is relevant for the shell API's behavior itself.
@@ -159,6 +160,7 @@ export default class ShellInstanceState {
159160
public context: any;
160161
public mongos: Mongo[];
161162
public shellApi: ShellApi;
163+
public shellLog: ShellLog;
162164
public shellBson: ShellBson;
163165
public cliOptions: ShellCliOptions;
164166
public evaluationListener: EvaluationListener;
@@ -187,6 +189,7 @@ export default class ShellInstanceState {
187189
this.initialServiceProvider = initialServiceProvider;
188190
this.messageBus = messageBus;
189191
this.shellApi = new ShellApi(this);
192+
this.shellLog = new ShellLog(this);
190193
this.shellBson = constructShellBson(
191194
initialServiceProvider.bsonLibrary,
192195
(msg: string) => {
@@ -362,6 +365,8 @@ export default class ShellInstanceState {
362365
});
363366
}
364367

368+
contextObject.log = this.shellLog;
369+
365370
this.messageBus.emit('mongosh:setCtx', { method: 'setCtx', arguments: {} });
366371
}
367372

packages/shell-api/src/shell-log.ts

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import type ShellInstanceState from './shell-instance-state';
2+
import { shellApiClassDefault, ShellApiClass } from './decorators';
3+
4+
const instanceStateSymbol = Symbol.for('@@mongosh.instanceState');
5+
6+
/**
7+
* This class contains the *global log* property that is considered part of the immediate shell API.
8+
*/
9+
@shellApiClassDefault
10+
export class ShellLog extends ShellApiClass {
11+
// Use symbols to make sure these are *not* among the things copied over into
12+
// the global scope.
13+
[instanceStateSymbol]: ShellInstanceState;
14+
15+
get _instanceState(): ShellInstanceState {
16+
return this[instanceStateSymbol];
17+
}
18+
19+
constructor(instanceState: ShellInstanceState) {
20+
super();
21+
this[instanceStateSymbol] = instanceState;
22+
}
23+
24+
info(message: string, attr?: unknown) {
25+
this[instanceStateSymbol].messageBus.emit('mongosh:write-custom-log', {
26+
method: 'info',
27+
message,
28+
attr,
29+
});
30+
}
31+
warn(message: string, attr?: unknown) {
32+
this[instanceStateSymbol].messageBus.emit('mongosh:write-custom-log', {
33+
method: 'warn',
34+
message,
35+
attr,
36+
});
37+
}
38+
error(message: string, attr?: unknown) {
39+
this[instanceStateSymbol].messageBus.emit('mongosh:write-custom-log', {
40+
method: 'error',
41+
message,
42+
attr,
43+
});
44+
}
45+
fatal(message: string, attr?: unknown) {
46+
this[instanceStateSymbol].messageBus.emit('mongosh:write-custom-log', {
47+
method: 'fatal',
48+
message,
49+
attr,
50+
});
51+
}
52+
debug(message: string, attr?: unknown, level?: 1 | 2 | 3 | 4 | 5) {
53+
this[instanceStateSymbol].messageBus.emit('mongosh:write-custom-log', {
54+
method: 'debug',
55+
message,
56+
attr,
57+
level,
58+
});
59+
}
60+
}

packages/types/src/index.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,13 @@ export interface SessionStartedEvent {
187187
};
188188
}
189189

190+
export interface WriteCustomLogEvent {
191+
method: 'info' | 'error' | 'warn' | 'fatal' | 'debug';
192+
message: string;
193+
attr?: unknown;
194+
level?: 1 | 2 | 3 | 4 | 5;
195+
}
196+
190197
export interface MongoshBusEventsMap extends ConnectEventMap {
191198
/**
192199
* Signals a connection to a MongoDB instance has been established
@@ -267,6 +274,11 @@ export interface MongoshBusEventsMap extends ConnectEventMap {
267274
'mongosh:start-loading-cli-scripts': (
268275
event: StartLoadingCliScriptsEvent
269276
) => void;
277+
/**
278+
* Signals to start writing log to the disc after MongoLogManager is initialized.
279+
*/
280+
'mongosh:write-custom-log': (ev: WriteCustomLogEvent) => void;
281+
270282
/**
271283
* Signals the successful startup of the mongosh REPL after initial files and configuration
272284
* have been loaded.

0 commit comments

Comments
 (0)