@mongosh/logging 1.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,433 @@
1
+ /* eslint-disable camelcase */
2
+ import redactInfo from 'mongodb-redact';
3
+ import { redactURICredentials } from '@mongosh/history';
4
+ import type {
5
+ MongoshBus,
6
+ ApiEvent,
7
+ UseEvent,
8
+ EvaluateInputEvent,
9
+ ShowEvent,
10
+ ConnectEvent,
11
+ ScriptLoadFileEvent,
12
+ StartLoadingCliScriptsEvent,
13
+ StartMongoshReplEvent,
14
+ MongocryptdTrySpawnEvent,
15
+ MongocryptdLogEvent,
16
+ MongocryptdErrorEvent,
17
+ SnippetsCommandEvent,
18
+ SnippetsErrorEvent,
19
+ SnippetsFetchIndexErrorEvent,
20
+ SnippetsFetchIndexEvent,
21
+ SnippetsLoadedEvent,
22
+ SnippetsLoadSnippetEvent,
23
+ SnippetsNpmDownloadActiveEvent,
24
+ SnippetsNpmDownloadFailedEvent,
25
+ SnippetsNpmLookupEvent,
26
+ SnippetsRunNpmEvent,
27
+ SnippetsTransformErrorEvent,
28
+ SpConnectAttemptInitializedEvent,
29
+ SpConnectHeartbeatFailureEvent,
30
+ SpConnectHeartbeatSucceededEvent,
31
+ SpResolveSrvErrorEvent,
32
+ SpResolveSrvSucceededEvent,
33
+ SpMissingOptionalDependencyEvent,
34
+ EditorRunEditCommandEvent,
35
+ EditorReadVscodeExtensionsDoneEvent,
36
+ EditorReadVscodeExtensionsFailedEvent
37
+ } from '@mongosh/types';
38
+ import { inspect } from 'util';
39
+ import { MongoLogWriter, mongoLogId } from 'mongodb-log-writer';
40
+
41
+ /**
42
+ * General interface for an Analytics provider that mongosh can use.
43
+ */
44
+ export interface MongoshAnalytics {
45
+ identify(message: {
46
+ userId: string,
47
+ traits: { platform: string }
48
+ }): void;
49
+
50
+ track(message: {
51
+ userId: string,
52
+ event: string,
53
+ properties: {
54
+ mongosh_version: string,
55
+ [key: string]: any;
56
+ }
57
+ }): void;
58
+ }
59
+
60
+ /**
61
+ * A no-op implementation of MongoshAnalytics that can be used when
62
+ * actually connecting to the telemetry provider is not possible
63
+ * (e.g. because we are running without an API key).
64
+ */
65
+ class NoopAnalytics implements MongoshAnalytics {
66
+ identify(_info: any): void {} // eslint-disable-line @typescript-eslint/no-unused-vars
67
+ track(_info: any): void {} // eslint-disable-line @typescript-eslint/no-unused-vars
68
+ }
69
+
70
+ /**
71
+ * Connect a MongoshBus instance that emits events to logging and analytics providers.
72
+ *
73
+ * @param bus A MongoshBus instance
74
+ * @param log A MongoLogWriter instance
75
+ * @param makeAnalytics A function that returns an analytics provider (or throws otherwise)
76
+ */
77
+ export function setupLoggerAndTelemetry(
78
+ bus: MongoshBus,
79
+ log: MongoLogWriter,
80
+ makeAnalytics: () => MongoshAnalytics,
81
+ userTraits: any,
82
+ mongosh_version: string): void {
83
+ const { logId } = log;
84
+ let userId: string;
85
+ let telemetry: boolean;
86
+
87
+ let analytics: MongoshAnalytics = new NoopAnalytics();
88
+ try {
89
+ analytics = makeAnalytics();
90
+ } catch (e) {
91
+ log.error('MONGOSH', mongoLogId(1_000_000_001), 'analytics', 'Failed to instantiate analytics provider', e);
92
+ }
93
+
94
+ // We emit different analytics events for loading files and evaluating scripts
95
+ // depending on whether we're already in the REPL or not yet. We store the
96
+ // state here so that the places where the events are emitted don't have to
97
+ // be aware of this distinction.
98
+ let hasStartedMongoshRepl = false;
99
+ bus.on('mongosh:start-mongosh-repl', (ev: StartMongoshReplEvent) => {
100
+ log.info('MONGOSH', mongoLogId(1_000_000_002), 'repl', 'Started REPL', ev);
101
+ hasStartedMongoshRepl = true;
102
+ });
103
+
104
+ let usesShellOption = false;
105
+ bus.on('mongosh:start-loading-cli-scripts', (event: StartLoadingCliScriptsEvent) => {
106
+ log.info('MONGOSH', mongoLogId(1_000_000_003), 'repl', 'Start loading CLI scripts');
107
+ usesShellOption = event.usesShellOption;
108
+ });
109
+
110
+ bus.on('mongosh:connect', function(args: ConnectEvent) {
111
+ const connectionUri = redactURICredentials(args.uri);
112
+ const { uri: _uri, ...argsWithoutUri } = args; // eslint-disable-line @typescript-eslint/no-unused-vars
113
+ const params = { session_id: logId, userId, connectionUri, ...argsWithoutUri };
114
+ log.info('MONGOSH', mongoLogId(1_000_000_004), 'connect', 'Connecting to server', params);
115
+
116
+ if (telemetry) {
117
+ analytics.track({
118
+ userId,
119
+ event: 'New Connection',
120
+ properties: {
121
+ mongosh_version,
122
+ session_id: logId,
123
+ ...argsWithoutUri
124
+ }
125
+ });
126
+ }
127
+ });
128
+
129
+ bus.on('mongosh:new-user', function(id: string, enableTelemetry: boolean) {
130
+ userId = id;
131
+ telemetry = enableTelemetry;
132
+ if (telemetry) analytics.identify({ userId, traits: userTraits });
133
+ });
134
+
135
+ bus.on('mongosh:update-user', function(id: string, enableTelemetry: boolean) {
136
+ userId = id;
137
+ telemetry = enableTelemetry;
138
+ if (telemetry) analytics.identify({ userId, traits: userTraits });
139
+ log.info('MONGOSH', mongoLogId(1_000_000_005), 'config', 'User updated', { enableTelemetry });
140
+ });
141
+
142
+ bus.on('mongosh:error', function(error: any, context: string) {
143
+ if (context === 'fatal') {
144
+ log.fatal('MONGOSH', mongoLogId(1_000_000_006), context, `${error.name}: ${error.message}`, error);
145
+ } else {
146
+ log.error('MONGOSH', mongoLogId(1_000_000_006), context, `${error.name}: ${error.message}`, error);
147
+ }
148
+
149
+ if (telemetry && error.name.includes('Mongosh')) {
150
+ analytics.track({
151
+ userId,
152
+ event: 'Error',
153
+ properties: {
154
+ mongosh_version,
155
+ name: error.name,
156
+ code: error.code,
157
+ scope: error.scope,
158
+ metadata: error.metadata
159
+ }
160
+ });
161
+ }
162
+ });
163
+
164
+ bus.on('mongosh:evaluate-input', function(args: EvaluateInputEvent) {
165
+ log.info('MONGOSH', mongoLogId(1_000_000_007), 'repl', 'Evaluating input', args);
166
+ });
167
+
168
+ bus.on('mongosh:use', function(args: UseEvent) {
169
+ log.info('MONGOSH', mongoLogId(1_000_000_008), 'shell-api', 'Used "use" command', args);
170
+
171
+ if (telemetry) {
172
+ analytics.track({
173
+ userId,
174
+ event: 'Use',
175
+ properties: {
176
+ mongosh_version
177
+ }
178
+ });
179
+ }
180
+ });
181
+
182
+ bus.on('mongosh:show', function(args: ShowEvent) {
183
+ log.info('MONGOSH', mongoLogId(1_000_000_009), 'shell-api', 'Used "show" command', args);
184
+
185
+ if (telemetry) {
186
+ analytics.track({
187
+ userId,
188
+ event: 'Show',
189
+ properties: {
190
+ mongosh_version,
191
+ method: args.method
192
+ }
193
+ });
194
+ }
195
+ });
196
+
197
+ bus.on('mongosh:setCtx', function(args: ApiEvent) {
198
+ log.info('MONGOSH', mongoLogId(1_000_000_010), 'shell-api', 'Initialized context', args);
199
+ });
200
+
201
+ bus.on('mongosh:api-call', function(args: ApiEvent) {
202
+ // TODO: redactInfo cannot handle circular or otherwise nontrivial input
203
+ let arg;
204
+ try {
205
+ arg = JSON.parse(JSON.stringify(args));
206
+ } catch {
207
+ arg = { _inspected: inspect(args) };
208
+ }
209
+ log.info('MONGOSH', mongoLogId(1_000_000_011), 'shell-api', 'Performed API call', redactInfo(arg));
210
+ });
211
+
212
+ bus.on('mongosh:api-load-file', function(args: ScriptLoadFileEvent) {
213
+ log.info('MONGOSH', mongoLogId(1_000_000_012), 'shell-api', 'Loading file via load()', args);
214
+
215
+ if (telemetry) {
216
+ analytics.track({
217
+ userId,
218
+ event: hasStartedMongoshRepl ? 'Script Loaded' : 'Script Loaded CLI',
219
+ properties: {
220
+ mongosh_version,
221
+ nested: args.nested,
222
+ ...(hasStartedMongoshRepl ? {} : { shell: usesShellOption })
223
+ }
224
+ });
225
+ }
226
+ });
227
+
228
+ bus.on('mongosh:eval-cli-script', function() {
229
+ log.info('MONGOSH', mongoLogId(1_000_000_013), 'repl', 'Evaluating script passed on the command line');
230
+
231
+ if (telemetry) {
232
+ analytics.track({
233
+ userId,
234
+ event: 'Script Evaluated',
235
+ properties: {
236
+ mongosh_version,
237
+ shell: usesShellOption
238
+ }
239
+ });
240
+ }
241
+ });
242
+
243
+ bus.on('mongosh:mongoshrc-load', function() {
244
+ log.info('MONGOSH', mongoLogId(1_000_000_014), 'repl', 'Loading .mongoshrc.js');
245
+
246
+ if (telemetry) {
247
+ analytics.track({
248
+ userId,
249
+ event: 'Mongoshrc Loaded',
250
+ properties: {
251
+ mongosh_version
252
+ }
253
+ });
254
+ }
255
+ });
256
+
257
+ bus.on('mongosh:mongoshrc-mongorc-warn', function() {
258
+ log.info('MONGOSH', mongoLogId(1_000_000_015), 'repl', 'Warning about .mongorc.js/.mongoshrc.js mismatch');
259
+
260
+ if (telemetry) {
261
+ analytics.track({
262
+ userId,
263
+ event: 'Mongorc Warning',
264
+ properties: {
265
+ mongosh_version
266
+ }
267
+ });
268
+ }
269
+ });
270
+
271
+ bus.on('mongosh:mongocryptd-tryspawn', function(ev: MongocryptdTrySpawnEvent) {
272
+ log.info('MONGOCRYPTD', mongoLogId(1_000_000_016), 'mongocryptd', 'Trying to spawn mongocryptd', ev);
273
+ });
274
+
275
+ bus.on('mongosh:mongocryptd-error', function(ev: MongocryptdErrorEvent) {
276
+ log.warn('MONGOCRYPTD', mongoLogId(1_000_000_017), 'mongocryptd', 'Error running mongocryptd', ev);
277
+ });
278
+
279
+ bus.on('mongosh:mongocryptd-log', function(ev: MongocryptdLogEvent) {
280
+ log.info('MONGOCRYPTD', mongoLogId(1_000_000_018), 'mongocryptd', 'mongocryptd log message', ev);
281
+ });
282
+
283
+ bus.on('mongosh-snippets:loaded', function(ev: SnippetsLoadedEvent) {
284
+ log.info('MONGOSH-SNIPPETS', mongoLogId(1_000_000_019), 'snippets', 'Loaded snippets', ev);
285
+ });
286
+
287
+ bus.on('mongosh-snippets:npm-lookup', function(ev: SnippetsNpmLookupEvent) {
288
+ log.info('MONGOSH-SNIPPETS', mongoLogId(1_000_000_020), 'snippets', 'Performing npm lookup', ev);
289
+ });
290
+
291
+ bus.on('mongosh-snippets:npm-lookup-stopped', function() {
292
+ log.info('MONGOSH-SNIPPETS', mongoLogId(1_000_000_021), 'snippets', 'npm lookup stopped');
293
+ });
294
+
295
+ bus.on('mongosh-snippets:npm-download-failed', function(ev: SnippetsNpmDownloadFailedEvent) {
296
+ log.info('MONGOSH-SNIPPETS', mongoLogId(1_000_000_022), 'snippets', 'npm download failed', ev);
297
+ });
298
+
299
+ bus.on('mongosh-snippets:npm-download-active', function(ev: SnippetsNpmDownloadActiveEvent) {
300
+ log.info('MONGOSH-SNIPPETS', mongoLogId(1_000_000_023), 'snippets', 'npm download active', ev);
301
+ });
302
+
303
+ bus.on('mongosh-snippets:fetch-index', function(ev: SnippetsFetchIndexEvent) {
304
+ log.info('MONGOSH-SNIPPETS', mongoLogId(1_000_000_024), 'snippets', 'Fetching snippet index', ev);
305
+ });
306
+
307
+ bus.on('mongosh-snippets:fetch-cache-invalid', function() {
308
+ log.info('MONGOSH-SNIPPETS', mongoLogId(1_000_000_025), 'snippets', 'Snippet cache invalid');
309
+ });
310
+
311
+ bus.on('mongosh-snippets:fetch-index-error', function(ev: SnippetsFetchIndexErrorEvent) {
312
+ log.info('MONGOSH-SNIPPETS', mongoLogId(1_000_000_026), 'snippets', 'Fetching snippet index failed', ev);
313
+ });
314
+
315
+ bus.on('mongosh-snippets:fetch-index-done', function() {
316
+ log.info('MONGOSH-SNIPPETS', mongoLogId(1_000_000_027), 'snippets', 'Fetching snippet index done');
317
+ });
318
+
319
+ bus.on('mongosh-snippets:package-json-edit-error', function(ev: SnippetsErrorEvent) {
320
+ log.info('MONGOSH-SNIPPETS', mongoLogId(1_000_000_028), 'snippets', 'Modifying snippets package.json failed', ev);
321
+ });
322
+
323
+ bus.on('mongosh-snippets:spawn-child', function(ev: SnippetsRunNpmEvent) {
324
+ log.info('MONGOSH-SNIPPETS', mongoLogId(1_000_000_029), 'snippets', 'Spawning helper', ev);
325
+ });
326
+
327
+ bus.on('mongosh-snippets:load-snippet', function(ev: SnippetsLoadSnippetEvent) {
328
+ log.info('MONGOSH-SNIPPETS', mongoLogId(1_000_000_030), 'snippets', 'Loading snippet', ev);
329
+ });
330
+
331
+ bus.on('mongosh-snippets:snippet-command', function(ev: SnippetsCommandEvent) {
332
+ log.info('MONGOSH-SNIPPETS', mongoLogId(1_000_000_031), 'snippets', 'Running snippet command', ev);
333
+
334
+ if (telemetry && ev.args[0] === 'install') {
335
+ analytics.track({
336
+ userId,
337
+ event: 'Snippet Install',
338
+ properties: {
339
+ mongosh_version
340
+ }
341
+ });
342
+ }
343
+ });
344
+
345
+ bus.on('mongosh-snippets:transform-error', function(ev: SnippetsTransformErrorEvent) {
346
+ log.info('MONGOSH-SNIPPETS', mongoLogId(1_000_000_032), 'snippets', 'Rewrote error message', ev);
347
+ });
348
+
349
+ const deprecatedApiCalls = new Set<string>();
350
+ bus.on('mongosh:deprecated-api-call', function(ev: ApiEvent) {
351
+ deprecatedApiCalls.add(`${ev.class}#${ev.method}`);
352
+ });
353
+ bus.on('mongosh:evaluate-finished', function() {
354
+ deprecatedApiCalls.forEach(e => {
355
+ const [clazz, method] = e.split('#');
356
+ log.warn('MONGOSH', mongoLogId(1_000_000_033), 'shell-api', 'Deprecated API call', { class: clazz, method });
357
+
358
+ if (telemetry) {
359
+ analytics.track({
360
+ userId,
361
+ event: 'Deprecated Method',
362
+ properties: {
363
+ mongosh_version,
364
+ class: clazz,
365
+ method
366
+ }
367
+ });
368
+ }
369
+ });
370
+ deprecatedApiCalls.clear();
371
+ });
372
+
373
+ bus.on('mongosh-sp:connect-attempt-initialized', function(ev: SpConnectAttemptInitializedEvent) {
374
+ log.info('MONGOSH-SP', mongoLogId(1_000_000_042), 'connect', 'Initiating connection attempt', ev);
375
+ });
376
+
377
+ bus.on('mongosh-sp:connect-heartbeat-failure', function(ev: SpConnectHeartbeatFailureEvent) {
378
+ log.warn('MONGOSH-SP', mongoLogId(1_000_000_034), 'connect', 'Server heartbeat failure', {
379
+ ...ev,
380
+ failure: ev.failure?.message
381
+ });
382
+ });
383
+
384
+ bus.on('mongosh-sp:connect-heartbeat-succeeded', function(ev: SpConnectHeartbeatSucceededEvent) {
385
+ log.info('MONGOSH-SP', mongoLogId(1_000_000_035), 'connect', 'Server heartbeat succeeded', ev);
386
+ });
387
+
388
+ bus.on('mongosh-sp:connect-fail-early', function() {
389
+ log.warn('MONGOSH-SP', mongoLogId(1_000_000_036), 'connect', 'Aborting connection attempt as irrecoverable');
390
+ });
391
+
392
+ bus.on('mongosh-sp:connect-attempt-finished', function() {
393
+ log.info('MONGOSH-SP', mongoLogId(1_000_000_037), 'connect', 'Connection attempt finished');
394
+ });
395
+
396
+ bus.on('mongosh-sp:resolve-srv-error', function(ev: SpResolveSrvErrorEvent) {
397
+ log.error('MONGOSH-SP', mongoLogId(1_000_000_038), 'connect', 'Resolving SRV record failed', {
398
+ from: redactURICredentials(ev.from),
399
+ error: ev.error?.message,
400
+ duringLoad: ev.duringLoad
401
+ });
402
+ });
403
+
404
+ bus.on('mongosh-sp:resolve-srv-succeeded', function(ev: SpResolveSrvSucceededEvent) {
405
+ log.info('MONGOSH-SP', mongoLogId(1_000_000_039), 'connect', 'Resolving SRV record succeeded', {
406
+ from: redactURICredentials(ev.from),
407
+ to: redactURICredentials(ev.to)
408
+ });
409
+ });
410
+
411
+ bus.on('mongosh-sp:reset-connection-options', function() {
412
+ log.info('MONGOSH-SP', mongoLogId(1_000_000_040), 'connect', 'Reconnect because of changed connection options');
413
+ });
414
+
415
+ bus.on('mongosh-sp:missing-optional-dependency', function(ev: SpMissingOptionalDependencyEvent) {
416
+ log.error('MONGOSH-SP', mongoLogId(1_000_000_041), 'deps', 'Missing optional dependency', {
417
+ name: ev.name,
418
+ error: ev?.error.message
419
+ });
420
+ });
421
+
422
+ bus.on('mongosh-editor:run-edit-command', function(ev: EditorRunEditCommandEvent) {
423
+ log.error('MONGOSH-EDITOR', mongoLogId(1_000_000_042), 'editor', 'Open external editor', ev);
424
+ });
425
+
426
+ bus.on('mongosh-editor:read-vscode-extensions-done', function(ev: EditorReadVscodeExtensionsDoneEvent) {
427
+ log.error('MONGOSH-EDITOR', mongoLogId(1_000_000_043), 'editor', 'Reading vscode extensions from disc succeeded', ev);
428
+ });
429
+
430
+ bus.on('mongosh-editor:read-vscode-extensions-failed', function(ev: EditorReadVscodeExtensionsFailedEvent) {
431
+ log.error('MONGOSH-EDITOR', mongoLogId(1_000_000_044), 'editor', 'Reading vscode extensions from disc failed', ev);
432
+ });
433
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,13 @@
1
+ {
2
+ "extends": "../../config/tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "outDir": "./lib",
5
+ "allowJs": true
6
+ },
7
+ "include": [
8
+ "./src/**/*"
9
+ ],
10
+ "exclude": [
11
+ "./src/**/*.spec.*"
12
+ ]
13
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "include": [
4
+ "./src/**/*",
5
+ "./test/**/*"
6
+ ],
7
+ "exclude": []
8
+ }