@mongosh/logging 3.6.0 → 3.7.2
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.
- package/lib/analytics-helpers.d.ts +3 -1
- package/lib/analytics-helpers.js.map +1 -1
- package/lib/logging-and-telemetry.d.ts +30 -0
- package/lib/logging-and-telemetry.js +108 -64
- package/lib/logging-and-telemetry.js.map +1 -1
- package/lib/types.d.ts +2 -0
- package/package.json +7 -5
- package/src/analytics-helpers.ts +3 -1
- package/src/logging-and-telemetry.spec.ts +192 -17
- package/src/logging-and-telemetry.ts +152 -65
- package/src/types.ts +4 -0
|
@@ -39,6 +39,7 @@ import { MongoLogWriter } from 'mongodb-log-writer';
|
|
|
39
39
|
import { mongoLogId } from 'mongodb-log-writer';
|
|
40
40
|
import type {
|
|
41
41
|
AnalyticsIdentifyMessage,
|
|
42
|
+
AnalyticsTrackMessage,
|
|
42
43
|
MongoshAnalytics,
|
|
43
44
|
MongoshAnalyticsIdentity,
|
|
44
45
|
} from './analytics-helpers';
|
|
@@ -52,6 +53,40 @@ import type {
|
|
|
52
53
|
MongoshLoggingAndTelemetryArguments,
|
|
53
54
|
MongoshTrackingProperties,
|
|
54
55
|
} from './types';
|
|
56
|
+
import { createHmac } from 'crypto';
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* @returns A hashed, unique identifier for the running device or `"unknown"` if not known.
|
|
60
|
+
*/
|
|
61
|
+
export async function getDeviceId({
|
|
62
|
+
onError,
|
|
63
|
+
}: {
|
|
64
|
+
onError?: (error: Error) => void;
|
|
65
|
+
} = {}): Promise<string | 'unknown'> {
|
|
66
|
+
try {
|
|
67
|
+
// Create a hashed format from the all uppercase version of the machine ID
|
|
68
|
+
// to match it exactly with the denisbrodbeck/machineid library that Atlas CLI uses.
|
|
69
|
+
const originalId: string =
|
|
70
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
71
|
+
await require('native-machine-id').getMachineId({
|
|
72
|
+
raw: true,
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
if (!originalId) {
|
|
76
|
+
return 'unknown';
|
|
77
|
+
}
|
|
78
|
+
const hmac = createHmac('sha256', originalId);
|
|
79
|
+
|
|
80
|
+
/** This matches the message used to create the hashes in Atlas CLI */
|
|
81
|
+
const DEVICE_ID_HASH_MESSAGE = 'atlascli';
|
|
82
|
+
|
|
83
|
+
hmac.update(DEVICE_ID_HASH_MESSAGE);
|
|
84
|
+
return hmac.digest('hex');
|
|
85
|
+
} catch (error) {
|
|
86
|
+
onError?.(error as Error);
|
|
87
|
+
return 'unknown';
|
|
88
|
+
}
|
|
89
|
+
}
|
|
55
90
|
|
|
56
91
|
export function setupLoggingAndTelemetry(
|
|
57
92
|
props: MongoshLoggingAndTelemetryArguments
|
|
@@ -62,7 +97,8 @@ export function setupLoggingAndTelemetry(
|
|
|
62
97
|
return loggingAndTelemetry;
|
|
63
98
|
}
|
|
64
99
|
|
|
65
|
-
|
|
100
|
+
/** @internal */
|
|
101
|
+
export class LoggingAndTelemetry implements MongoshLoggingAndTelemetry {
|
|
66
102
|
private static dummyLogger = new MongoLogWriter(
|
|
67
103
|
'',
|
|
68
104
|
null,
|
|
@@ -82,30 +118,71 @@ class LoggingAndTelemetry implements MongoshLoggingAndTelemetry {
|
|
|
82
118
|
private readonly mongoshVersion: string;
|
|
83
119
|
|
|
84
120
|
private log: MongoLogWriter;
|
|
85
|
-
private
|
|
121
|
+
private pendingBusEvents: CallableFunction[] = [];
|
|
122
|
+
private pendingTelemetryEvents: CallableFunction[] = [];
|
|
86
123
|
private isSetup = false;
|
|
87
|
-
private
|
|
124
|
+
private isBufferingBusEvents = false;
|
|
125
|
+
private isBufferingTelemetryEvents = false;
|
|
126
|
+
|
|
127
|
+
private deviceId: string | undefined;
|
|
128
|
+
/** @internal */
|
|
129
|
+
public setupTelemetryPromise: Promise<void> = Promise.resolve();
|
|
130
|
+
|
|
131
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
132
|
+
private resolveDeviceId: (value: string) => void = () => {};
|
|
88
133
|
|
|
89
134
|
constructor({
|
|
90
135
|
bus,
|
|
91
136
|
analytics,
|
|
92
137
|
userTraits,
|
|
93
138
|
mongoshVersion,
|
|
139
|
+
deviceId,
|
|
94
140
|
}: MongoshLoggingAndTelemetryArguments) {
|
|
95
141
|
this.bus = bus;
|
|
96
142
|
this.analytics = analytics;
|
|
97
143
|
this.log = LoggingAndTelemetry.dummyLogger;
|
|
98
144
|
this.userTraits = userTraits;
|
|
99
145
|
this.mongoshVersion = mongoshVersion;
|
|
146
|
+
this.deviceId = deviceId;
|
|
100
147
|
}
|
|
101
148
|
|
|
102
149
|
public setup(): void {
|
|
103
150
|
if (this.isSetup) {
|
|
104
151
|
throw new Error('Setup can only be called once.');
|
|
105
152
|
}
|
|
153
|
+
this.isBufferingTelemetryEvents = true;
|
|
154
|
+
this.isBufferingBusEvents = true;
|
|
155
|
+
|
|
156
|
+
this.setupTelemetryPromise = this.setupTelemetry();
|
|
106
157
|
this.setupBusEventListeners();
|
|
158
|
+
|
|
107
159
|
this.isSetup = true;
|
|
108
|
-
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
public flush(): void {
|
|
163
|
+
// Run any telemetry events even if device ID hasn't been resolved yet
|
|
164
|
+
this.runAndClearPendingTelemetryEvents();
|
|
165
|
+
|
|
166
|
+
// Run any other pending events with the set or dummy log for telemetry purposes.
|
|
167
|
+
this.runAndClearPendingBusEvents();
|
|
168
|
+
|
|
169
|
+
this.resolveDeviceId('unknown');
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
private async setupTelemetry(): Promise<void> {
|
|
173
|
+
if (!this.deviceId) {
|
|
174
|
+
this.deviceId = await Promise.race([
|
|
175
|
+
getDeviceId({
|
|
176
|
+
onError: (error) =>
|
|
177
|
+
this.bus.emit('mongosh:error', error, 'telemetry'),
|
|
178
|
+
}),
|
|
179
|
+
new Promise<string>((resolve) => {
|
|
180
|
+
this.resolveDeviceId = resolve;
|
|
181
|
+
}),
|
|
182
|
+
]);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
this.runAndClearPendingTelemetryEvents();
|
|
109
186
|
}
|
|
110
187
|
|
|
111
188
|
public attachLogger(logger: MongoLogWriter): void {
|
|
@@ -119,24 +196,32 @@ class LoggingAndTelemetry implements MongoshLoggingAndTelemetry {
|
|
|
119
196
|
);
|
|
120
197
|
}
|
|
121
198
|
this.log = logger;
|
|
122
|
-
this.isBufferingEvents = false;
|
|
123
199
|
|
|
124
|
-
this.
|
|
200
|
+
this.runAndClearPendingBusEvents();
|
|
125
201
|
|
|
126
202
|
this.bus.emit('mongosh:log-initialized');
|
|
127
203
|
}
|
|
128
204
|
|
|
129
205
|
public detachLogger() {
|
|
130
206
|
this.log = LoggingAndTelemetry.dummyLogger;
|
|
131
|
-
|
|
132
|
-
this.
|
|
207
|
+
|
|
208
|
+
this.flush();
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
private runAndClearPendingBusEvents() {
|
|
212
|
+
let pendingEvent: CallableFunction | undefined;
|
|
213
|
+
while ((pendingEvent = this.pendingBusEvents.shift())) {
|
|
214
|
+
pendingEvent();
|
|
215
|
+
}
|
|
216
|
+
this.isBufferingBusEvents = false;
|
|
133
217
|
}
|
|
134
218
|
|
|
135
|
-
private
|
|
219
|
+
private runAndClearPendingTelemetryEvents() {
|
|
136
220
|
let pendingEvent: CallableFunction | undefined;
|
|
137
|
-
while ((pendingEvent = this.
|
|
221
|
+
while ((pendingEvent = this.pendingTelemetryEvents.shift())) {
|
|
138
222
|
pendingEvent();
|
|
139
223
|
}
|
|
224
|
+
this.isBufferingTelemetryEvents = false;
|
|
140
225
|
}
|
|
141
226
|
|
|
142
227
|
/** Information used and set by different bus events. */
|
|
@@ -171,11 +256,10 @@ class LoggingAndTelemetry implements MongoshLoggingAndTelemetry {
|
|
|
171
256
|
listener: (...args: Parameters<EventsMap[K]>) => void
|
|
172
257
|
) => {
|
|
173
258
|
this.bus.on(event, ((...args: Parameters<EventsMap[K]>) => {
|
|
174
|
-
if (this.
|
|
175
|
-
this.
|
|
259
|
+
if (this.isBufferingBusEvents) {
|
|
260
|
+
this.pendingBusEvents.push(() => listener(...args));
|
|
176
261
|
return;
|
|
177
262
|
}
|
|
178
|
-
|
|
179
263
|
listener(...args);
|
|
180
264
|
}) as MongoshBusEventsMap[K]);
|
|
181
265
|
return this.bus;
|
|
@@ -193,12 +277,52 @@ class LoggingAndTelemetry implements MongoshLoggingAndTelemetry {
|
|
|
193
277
|
|
|
194
278
|
const getTelemetryUserIdentity = (): MongoshAnalyticsIdentity => {
|
|
195
279
|
return {
|
|
280
|
+
deviceId: this.deviceId,
|
|
196
281
|
anonymousId:
|
|
197
282
|
this.busEventState.telemetryAnonymousId ??
|
|
198
283
|
(this.busEventState.userId as string),
|
|
199
284
|
};
|
|
200
285
|
};
|
|
201
286
|
|
|
287
|
+
const track = (
|
|
288
|
+
message: Pick<AnalyticsTrackMessage, 'event' | 'timestamp'> & {
|
|
289
|
+
properties?: Omit<
|
|
290
|
+
AnalyticsTrackMessage['properties'],
|
|
291
|
+
keyof MongoshTrackingProperties
|
|
292
|
+
>;
|
|
293
|
+
}
|
|
294
|
+
): void => {
|
|
295
|
+
const callback = () =>
|
|
296
|
+
this.analytics.track({
|
|
297
|
+
...getTelemetryUserIdentity(),
|
|
298
|
+
...message,
|
|
299
|
+
properties: {
|
|
300
|
+
...getTrackingProperties(),
|
|
301
|
+
...message.properties,
|
|
302
|
+
},
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
if (this.isBufferingTelemetryEvents) {
|
|
306
|
+
this.pendingTelemetryEvents.push(callback);
|
|
307
|
+
} else {
|
|
308
|
+
callback();
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
const identify = (): void => {
|
|
313
|
+
const callback = () =>
|
|
314
|
+
this.analytics.identify({
|
|
315
|
+
...getTelemetryUserIdentity(),
|
|
316
|
+
traits: getUserTraits(),
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
if (this.isBufferingTelemetryEvents) {
|
|
320
|
+
this.pendingTelemetryEvents.push(callback);
|
|
321
|
+
} else {
|
|
322
|
+
callback();
|
|
323
|
+
}
|
|
324
|
+
};
|
|
325
|
+
|
|
202
326
|
onBus('mongosh:start-mongosh-repl', (ev: StartMongoshReplEvent) => {
|
|
203
327
|
this.log.info(
|
|
204
328
|
'MONGOSH',
|
|
@@ -230,7 +354,6 @@ class LoggingAndTelemetry implements MongoshLoggingAndTelemetry {
|
|
|
230
354
|
atlas_hostname: args.is_atlas ? resolved_hostname : null,
|
|
231
355
|
};
|
|
232
356
|
const properties = {
|
|
233
|
-
...getTrackingProperties(),
|
|
234
357
|
...argsWithoutUriAndHostname,
|
|
235
358
|
...atlasHostname,
|
|
236
359
|
};
|
|
@@ -248,8 +371,7 @@ class LoggingAndTelemetry implements MongoshLoggingAndTelemetry {
|
|
|
248
371
|
}
|
|
249
372
|
);
|
|
250
373
|
|
|
251
|
-
|
|
252
|
-
...getTelemetryUserIdentity(),
|
|
374
|
+
track({
|
|
253
375
|
event: 'New Connection',
|
|
254
376
|
properties,
|
|
255
377
|
});
|
|
@@ -264,11 +386,9 @@ class LoggingAndTelemetry implements MongoshLoggingAndTelemetry {
|
|
|
264
386
|
);
|
|
265
387
|
|
|
266
388
|
const normalizedTimings = Object.fromEntries(normalizedTimingsArray);
|
|
267
|
-
|
|
268
|
-
...getTelemetryUserIdentity(),
|
|
389
|
+
track({
|
|
269
390
|
event: 'Startup Time',
|
|
270
391
|
properties: {
|
|
271
|
-
...getTrackingProperties(),
|
|
272
392
|
is_interactive: args.isInteractive,
|
|
273
393
|
js_context: args.jsContext,
|
|
274
394
|
...normalizedTimings,
|
|
@@ -284,10 +404,8 @@ class LoggingAndTelemetry implements MongoshLoggingAndTelemetry {
|
|
|
284
404
|
}
|
|
285
405
|
this.busEventState.telemetryAnonymousId =
|
|
286
406
|
newTelemetryUserIdentity.anonymousId;
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
traits: getUserTraits(),
|
|
290
|
-
});
|
|
407
|
+
|
|
408
|
+
identify();
|
|
291
409
|
}
|
|
292
410
|
);
|
|
293
411
|
|
|
@@ -303,10 +421,7 @@ class LoggingAndTelemetry implements MongoshLoggingAndTelemetry {
|
|
|
303
421
|
} else {
|
|
304
422
|
this.busEventState.userId = updatedTelemetryUserIdentity.userId;
|
|
305
423
|
}
|
|
306
|
-
|
|
307
|
-
...getTelemetryUserIdentity(),
|
|
308
|
-
traits: getUserTraits(),
|
|
309
|
-
});
|
|
424
|
+
identify();
|
|
310
425
|
this.log.info(
|
|
311
426
|
'MONGOSH',
|
|
312
427
|
mongoLogId(1_000_000_005),
|
|
@@ -334,11 +449,9 @@ class LoggingAndTelemetry implements MongoshLoggingAndTelemetry {
|
|
|
334
449
|
);
|
|
335
450
|
|
|
336
451
|
if (error.name.includes('Mongosh')) {
|
|
337
|
-
|
|
338
|
-
...getTelemetryUserIdentity(),
|
|
452
|
+
track({
|
|
339
453
|
event: 'Error',
|
|
340
454
|
properties: {
|
|
341
|
-
...getTrackingProperties(),
|
|
342
455
|
name: mongoshError.name,
|
|
343
456
|
code: mongoshError.code,
|
|
344
457
|
scope: mongoshError.scope,
|
|
@@ -388,12 +501,8 @@ class LoggingAndTelemetry implements MongoshLoggingAndTelemetry {
|
|
|
388
501
|
args
|
|
389
502
|
);
|
|
390
503
|
|
|
391
|
-
|
|
392
|
-
...getTelemetryUserIdentity(),
|
|
504
|
+
track({
|
|
393
505
|
event: 'Use',
|
|
394
|
-
properties: {
|
|
395
|
-
...getTrackingProperties(),
|
|
396
|
-
},
|
|
397
506
|
});
|
|
398
507
|
});
|
|
399
508
|
|
|
@@ -406,11 +515,9 @@ class LoggingAndTelemetry implements MongoshLoggingAndTelemetry {
|
|
|
406
515
|
args
|
|
407
516
|
);
|
|
408
517
|
|
|
409
|
-
|
|
410
|
-
...getTelemetryUserIdentity(),
|
|
518
|
+
track({
|
|
411
519
|
event: 'Show',
|
|
412
520
|
properties: {
|
|
413
|
-
...getTrackingProperties(),
|
|
414
521
|
method: args.method,
|
|
415
522
|
},
|
|
416
523
|
});
|
|
@@ -452,13 +559,11 @@ class LoggingAndTelemetry implements MongoshLoggingAndTelemetry {
|
|
|
452
559
|
args
|
|
453
560
|
);
|
|
454
561
|
|
|
455
|
-
|
|
456
|
-
...getTelemetryUserIdentity(),
|
|
562
|
+
track({
|
|
457
563
|
event: this.busEventState.hasStartedMongoshRepl
|
|
458
564
|
? 'Script Loaded'
|
|
459
565
|
: 'Script Loaded CLI',
|
|
460
566
|
properties: {
|
|
461
|
-
...getTrackingProperties(),
|
|
462
567
|
nested: args.nested,
|
|
463
568
|
...(this.busEventState.hasStartedMongoshRepl
|
|
464
569
|
? {}
|
|
@@ -475,11 +580,9 @@ class LoggingAndTelemetry implements MongoshLoggingAndTelemetry {
|
|
|
475
580
|
'Evaluating script passed on the command line'
|
|
476
581
|
);
|
|
477
582
|
|
|
478
|
-
|
|
479
|
-
...getTelemetryUserIdentity(),
|
|
583
|
+
track({
|
|
480
584
|
event: 'Script Evaluated',
|
|
481
585
|
properties: {
|
|
482
|
-
...getTrackingProperties(),
|
|
483
586
|
shell: this.busEventState.usesShellOption,
|
|
484
587
|
},
|
|
485
588
|
});
|
|
@@ -493,12 +596,8 @@ class LoggingAndTelemetry implements MongoshLoggingAndTelemetry {
|
|
|
493
596
|
'Loading .mongoshrc.js'
|
|
494
597
|
);
|
|
495
598
|
|
|
496
|
-
|
|
497
|
-
...getTelemetryUserIdentity(),
|
|
599
|
+
track({
|
|
498
600
|
event: 'Mongoshrc Loaded',
|
|
499
|
-
properties: {
|
|
500
|
-
...getTrackingProperties(),
|
|
501
|
-
},
|
|
502
601
|
});
|
|
503
602
|
});
|
|
504
603
|
|
|
@@ -510,12 +609,8 @@ class LoggingAndTelemetry implements MongoshLoggingAndTelemetry {
|
|
|
510
609
|
'Warning about .mongorc.js/.mongoshrc.js mismatch'
|
|
511
610
|
);
|
|
512
611
|
|
|
513
|
-
|
|
514
|
-
...getTelemetryUserIdentity(),
|
|
612
|
+
track({
|
|
515
613
|
event: 'Mongorc Warning',
|
|
516
|
-
properties: {
|
|
517
|
-
...getTrackingProperties(),
|
|
518
|
-
},
|
|
519
614
|
});
|
|
520
615
|
});
|
|
521
616
|
|
|
@@ -680,12 +775,8 @@ class LoggingAndTelemetry implements MongoshLoggingAndTelemetry {
|
|
|
680
775
|
);
|
|
681
776
|
|
|
682
777
|
if (ev.args[0] === 'install') {
|
|
683
|
-
|
|
684
|
-
...getTelemetryUserIdentity(),
|
|
778
|
+
track({
|
|
685
779
|
event: 'Snippet Install',
|
|
686
|
-
properties: {
|
|
687
|
-
...getTrackingProperties(),
|
|
688
|
-
},
|
|
689
780
|
});
|
|
690
781
|
}
|
|
691
782
|
});
|
|
@@ -738,21 +829,17 @@ class LoggingAndTelemetry implements MongoshLoggingAndTelemetry {
|
|
|
738
829
|
entry
|
|
739
830
|
);
|
|
740
831
|
|
|
741
|
-
|
|
742
|
-
...getTelemetryUserIdentity(),
|
|
832
|
+
track({
|
|
743
833
|
event: 'Deprecated Method',
|
|
744
834
|
properties: {
|
|
745
|
-
...getTrackingProperties(),
|
|
746
835
|
...entry,
|
|
747
836
|
},
|
|
748
837
|
});
|
|
749
838
|
}
|
|
750
839
|
for (const [entry, count] of apiCalls) {
|
|
751
|
-
|
|
752
|
-
...getTelemetryUserIdentity(),
|
|
840
|
+
track({
|
|
753
841
|
event: 'API Call',
|
|
754
842
|
properties: {
|
|
755
|
-
...getTrackingProperties(),
|
|
756
843
|
...entry,
|
|
757
844
|
count,
|
|
758
845
|
},
|
package/src/types.ts
CHANGED
|
@@ -6,6 +6,8 @@ import type { MultiSet } from './helpers';
|
|
|
6
6
|
export interface MongoshLoggingAndTelemetry {
|
|
7
7
|
attachLogger(logger: MongoLogWriter): void;
|
|
8
8
|
detachLogger(): void;
|
|
9
|
+
/** Flush any remaining log or telemetry events. */
|
|
10
|
+
flush(): void;
|
|
9
11
|
}
|
|
10
12
|
|
|
11
13
|
export type MongoshLoggingAndTelemetryArguments = {
|
|
@@ -15,6 +17,8 @@ export type MongoshLoggingAndTelemetryArguments = {
|
|
|
15
17
|
[key: string]: unknown;
|
|
16
18
|
};
|
|
17
19
|
mongoshVersion: string;
|
|
20
|
+
/** Machine-specific ID; gets set automatically when omitted */
|
|
21
|
+
deviceId?: string | undefined;
|
|
18
22
|
};
|
|
19
23
|
|
|
20
24
|
export type MongoshTrackingProperties = {
|