Skip to content

Commit df745e5

Browse files
authored
fix(cli-repl): json-clone objects before passing them to pino MONGOSH-884 (#1002)
1 parent 45adc1c commit df745e5

File tree

2 files changed

+33
-7
lines changed

2 files changed

+33
-7
lines changed

packages/cli-repl/src/setup-logger-and-telemetry.spec.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@ describe('setupLoggerAndTelemetry', () => {
5656
bus.emit('mongosh:evaluate-input', { input: '1+1' });
5757
bus.emit('mongosh:driver-initialized', { driver: { name: 'nodejs', version: '3.6.1' } });
5858

59+
const circular: any = {};
60+
circular.circular = circular;
61+
bus.emit('mongosh:api-call', { method: 'circulararg', arguments: { options: { circular } } });
62+
expect(circular.circular).to.equal(circular); // Make sure the argument is still intact afterwards
63+
5964
bus.emit('mongosh:start-loading-cli-scripts', { usesShellOption: true });
6065
bus.emit('mongosh:api-load-file', { nested: true, filename: 'foobar.js' });
6166
bus.emit('mongosh:start-mongosh-repl', { version: '1.0.0' });
@@ -79,7 +84,7 @@ describe('setupLoggerAndTelemetry', () => {
7984
bus.emit('mongosh-snippets:snippet-command', { args: ['install', 'foo'] });
8085
bus.emit('mongosh-snippets:transform-error', { error: 'failed', addition: 'oh no', name: 'foo' });
8186

82-
bus.emit('mongosh-sp:connect-heartbeat-failure', { connectionId: 'localhost', failure: new Error(), isFailFast: true, isKnownServer: true });
87+
bus.emit('mongosh-sp:connect-heartbeat-failure', { connectionId: 'localhost', failure: new Error('cause'), isFailFast: true, isKnownServer: true });
8388
bus.emit('mongosh-sp:connect-heartbeat-succeeded', { connectionId: 'localhost' });
8489
bus.emit('mongosh-sp:connect-fail-early');
8590
bus.emit('mongosh-sp:connect-attempt-finished');
@@ -116,6 +121,7 @@ describe('setupLoggerAndTelemetry', () => {
116121
expect(logOutput[i].msg).to.match(/^mongosh:evaluate-input/);
117122
expect(logOutput[i++].msg).to.match(/"input":"1\+1"/);
118123
expect(logOutput[i++].msg).to.match(/"version":"3.6.1"/);
124+
expect(logOutput[i++].msg).to.match(/^mongosh:api-call \{"_inspected":"\{.+circular:.+\}/);
119125
expect(logOutput[i++].msg).to.equal('mongosh:start-loading-cli-scripts');
120126
expect(logOutput[i].msg).to.match(/^mongosh:api-load-file/);
121127
expect(logOutput[i].msg).to.match(/"nested":true/);
@@ -143,7 +149,7 @@ describe('setupLoggerAndTelemetry', () => {
143149
expect(logOutput[i++].msg).to.equal('mongosh-snippets:load-snippet {"source":"load-all","name":"foo"}');
144150
expect(logOutput[i++].msg).to.equal('mongosh-snippets:snippet-command {"args":["install","foo"]}');
145151
expect(logOutput[i++].msg).to.equal('mongosh-snippets:transform-error {"error":"failed","addition":"oh no","name":"foo"}');
146-
expect(logOutput[i++].msg).to.equal('mongosh-sp:connect-heartbeat-failure {"connectionId":"localhost","failure":{},"isFailFast":true,"isKnownServer":true}');
152+
expect(logOutput[i++].msg).to.equal('mongosh-sp:connect-heartbeat-failure {"connectionId":"localhost","failure":"cause","isFailFast":true,"isKnownServer":true}');
147153
expect(logOutput[i++].msg).to.equal('mongosh-sp:connect-heartbeat-succeeded {"connectionId":"localhost"}');
148154
expect(logOutput[i++].msg).to.equal('mongosh-sp:connect-fail-early');
149155
expect(logOutput[i++].msg).to.equal('mongosh-sp:connect-attempt-finished');

packages/cli-repl/src/setup-logger-and-telemetry.ts

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import type {
3232
SpResolveSrvSucceededEvent,
3333
SpMissingOptionalDependencyEvent
3434
} from '@mongosh/types';
35+
import { inspect } from 'util';
3536

3637
interface MongoshAnalytics {
3738
identify(message: {
@@ -49,6 +50,22 @@ interface MongoshAnalytics {
4950
}): void;
5051
}
5152

53+
// The default serializer for pino replaces circular properties it finds
54+
// anywhere in the object graph. In extreme cases, e.g. when a mongosh cursor
55+
// is passed to it, it follows the graph all the way to the network socket and
56+
// replaces random properties of its internal state with the string
57+
// '"[Circular"]':
58+
// https://github.com/davidmarkclements/fast-safe-stringify/blob/0e011f068962e8f8974133a47afcabfc003f2183/index.js#L36
59+
// As a workaround, we do a JSON-based clone for any input that can contain
60+
// arbitrary values.
61+
function jsonClone<T>(input: T): T | { _inspected: string } {
62+
try {
63+
return JSON.parse(JSON.stringify(input));
64+
} catch (error) {
65+
return { _inspected: inspect(input) };
66+
}
67+
}
68+
5269
// set up a noop, in case we are not able to connect to segment.
5370
class NoopAnalytics implements MongoshAnalytics {
5471
identify(_info: any): void {} // eslint-disable-line @typescript-eslint/no-unused-vars
@@ -102,7 +119,7 @@ export default function setupLoggerAndTelemetry(
102119
const connectionUri = redactCredentials(args.uri);
103120
const { uri: _uri, ...argsWithoutUri } = args; // eslint-disable-line @typescript-eslint/no-unused-vars
104121
const params = { session_id: logId, userId, connectionUri, ...argsWithoutUri };
105-
log.info('mongosh:connect', params);
122+
log.info('mongosh:connect', jsonClone(params));
106123

107124
if (telemetry) {
108125
analytics.track({
@@ -118,7 +135,7 @@ export default function setupLoggerAndTelemetry(
118135
});
119136

120137
bus.on('mongosh:driver-initialized', function(driverMetadata: any) {
121-
log.info('mongosh:driver-initialized', driverMetadata);
138+
log.info('mongosh:driver-initialized', jsonClone(driverMetadata));
122139
});
123140

124141
bus.on('mongosh:new-user', function(id: string, enableTelemetry: boolean) {
@@ -186,11 +203,11 @@ export default function setupLoggerAndTelemetry(
186203
});
187204

188205
bus.on('mongosh:setCtx', function(args: ApiEvent) {
189-
log.info('mongosh:setCtx', args);
206+
log.info('mongosh:setCtx', jsonClone(args));
190207
});
191208

192209
bus.on('mongosh:api-call', function(args: ApiEvent) {
193-
log.info('mongosh:api-call', redactInfo(args));
210+
log.info('mongosh:api-call', redactInfo(jsonClone(args)));
194211
});
195212

196213
bus.on('mongosh:api-load-file', function(args: ScriptLoadFileEvent) {
@@ -355,7 +372,10 @@ export default function setupLoggerAndTelemetry(
355372
});
356373

357374
bus.on('mongosh-sp:connect-heartbeat-failure', function(ev: SpConnectHeartbeatFailureEvent) {
358-
log.info('mongosh-sp:connect-heartbeat-failure', ev);
375+
log.info('mongosh-sp:connect-heartbeat-failure', {
376+
...ev,
377+
failure: ev.failure?.message
378+
});
359379
});
360380

361381
bus.on('mongosh-sp:connect-heartbeat-succeeded', function(ev: SpConnectHeartbeatSucceededEvent) {

0 commit comments

Comments
 (0)