Skip to content

Commit 6fa8647

Browse files
authored
feat: report os info telemetry COMPASS-5820 (#3086)
* feat: report os info * make test pass * remove unused package * fix tests * account for quotes in releases file * remove unused export * add channel and distribution to identify * add common properties * remove compass_full_version from e2e
1 parent 91589cc commit 6fa8647

File tree

5 files changed

+157
-19
lines changed

5 files changed

+157
-19
lines changed

packages/compass-e2e-tests/helpers/commands/listen-for-telemetry-events.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export async function listenForTelemetryEvents(
2929
const ev = lookupNewEvent(eventName);
3030
const properties = { ...ev.properties };
3131
delete properties.compass_version;
32+
delete properties.compass_full_version;
3233
delete properties.compass_distribution;
3334
delete properties.compass_channel;
3435
return properties;

packages/compass/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,8 +164,8 @@
164164
"clipboard": "^2.0.6",
165165
"kerberos": "^2.0.0",
166166
"keytar": "^7.7.0",
167-
"os-dns-native": "^1.2.0",
168167
"mongodb-client-encryption": "^2.2.0-alpha.0",
168+
"os-dns-native": "^1.2.0",
169169
"system-ca": "^1.0.2"
170170
},
171171
"devDependencies": {
@@ -272,8 +272,8 @@
272272
"web-vitals": "^2.1.2"
273273
},
274274
"optionalDependencies": {
275-
"vscode-windows-registry": "1.0.2",
276275
"macos-export-certificate-and-key": "^1.1.1",
276+
"vscode-windows-registry": "1.0.2",
277277
"win-export-certificate-and-key": "^1.1.1"
278278
},
279279
"engines": {
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { expect } from 'chai';
2+
import os from 'os';
3+
import { promises as fs } from 'fs';
4+
5+
import { getOsInfo } from './get-os-info';
6+
7+
describe('get-os-info', function () {
8+
let osInfo;
9+
beforeEach(async function () {
10+
osInfo = await getOsInfo();
11+
});
12+
13+
it('returns info from "os" module', function () {
14+
const { os_arch, os_type, os_version, os_release } = osInfo;
15+
expect({ os_arch, os_type, os_version, os_release }).to.deep.equal({
16+
os_arch: os.arch(),
17+
os_type: os.type(),
18+
os_version: os.version(),
19+
os_release: os.release(),
20+
});
21+
});
22+
23+
describe('on linux', function () {
24+
beforeEach(function () {
25+
if (process.platform !== 'linux') {
26+
this.skip();
27+
}
28+
});
29+
30+
it('returns info from /etc/releases', async function () {
31+
const etcRelease = await fs.readFile('/etc/os-release', 'utf-8');
32+
33+
const releaseKv = etcRelease
34+
.split('\n')
35+
.map((l) => l.trim())
36+
.filter(Boolean)
37+
.map((l) => l.split('='));
38+
39+
const distroId = releaseKv
40+
.find(([k]) => k === 'ID')[1]
41+
.replace(/["']/g, '');
42+
const distroVer = releaseKv
43+
.find(([k]) => k === 'VERSION_ID')[1]
44+
.replace(/["']/g, '');
45+
46+
// check that we test against actual values and not just an empty string
47+
expect(distroId).to.match(/^(rhel|ubuntu|debian)$/);
48+
expect(distroVer).to.match(/^\d+/);
49+
50+
const { os_linux_dist, os_linux_release } = osInfo;
51+
expect({ os_linux_dist, os_linux_release }).to.deep.equal({
52+
os_linux_dist: distroId,
53+
os_linux_release: distroVer,
54+
});
55+
});
56+
});
57+
});
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import os from 'os';
2+
import { promises as fs } from 'fs';
3+
4+
type OsInfo = {
5+
os_type: string;
6+
os_version: string;
7+
os_arch: string;
8+
os_release: string;
9+
os_linux_dist?: string;
10+
os_linux_release?: string;
11+
};
12+
13+
async function getLinuxInfo(): Promise<{
14+
os_linux_dist: string;
15+
os_linux_release: string;
16+
}> {
17+
try {
18+
const releaseFile = '/etc/os-release';
19+
const etcRelease = await fs.readFile(releaseFile, 'utf-8');
20+
21+
const releaseKv = etcRelease
22+
.split('\n')
23+
.map((l) => l.trim())
24+
.filter(Boolean)
25+
.map((l) => l.split('='));
26+
27+
const distId = releaseKv
28+
.find(([k]) => k === 'ID')?.[1]
29+
.replace(/["']/g, '');
30+
const distVer = releaseKv
31+
.find(([k]) => k === 'VERSION_ID')?.[1]
32+
.replace(/["']/g, '');
33+
34+
return {
35+
os_linux_dist: distId || 'unknown',
36+
os_linux_release: distVer || 'unknown',
37+
};
38+
} catch (e) {
39+
// couldn't read /etc/os-release
40+
}
41+
42+
return {
43+
os_linux_dist: 'unknown',
44+
os_linux_release: 'unknown',
45+
};
46+
}
47+
48+
export async function getOsInfo(): Promise<OsInfo> {
49+
return {
50+
os_type: os.type(),
51+
os_version: os.version(),
52+
os_arch: os.arch(),
53+
os_release: os.release(),
54+
...(process.platform === 'linux' ? await getLinuxInfo() : {}),
55+
};
56+
}

packages/compass/src/main/telemetry.ts

Lines changed: 41 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ import { ipcMain } from 'hadron-ipc';
44
import { createLoggerAndTelemetry } from '@mongodb-js/compass-logging';
55
import type { CompassApplication } from './application';
66
import type { EventEmitter } from 'events';
7+
import { getOsInfo } from './get-os-info';
78

8-
const { log, mongoLogId } =
9-
createLoggerAndTelemetry('COMPASS-TELEMETRY');
9+
const { log, mongoLogId } = createLoggerAndTelemetry('COMPASS-TELEMETRY');
1010

1111
interface EventInfo {
1212
event: string;
@@ -41,14 +41,24 @@ class CompassTelemetry {
4141

4242
private static initPromise: Promise<void> | null = null;
4343

44-
private static _track(info: EventInfo) {
45-
const commonProperties = {
46-
compass_version: app.getVersion().split('.').slice(0, 2).join('.'), // only major.minor
44+
private static _getCommonProperties() {
45+
// Used in both track and identify to add common traits
46+
// to any event that we send to segment
47+
return {
48+
compass_version: app.getVersion().split('.').slice(0, 2).join('.'),
49+
compass_full_version: app.getVersion(),
4750
compass_distribution: process.env.HADRON_DISTRIBUTION,
4851
compass_channel: process.env.HADRON_CHANNEL,
4952
};
53+
}
54+
55+
private static _track(info: EventInfo) {
56+
const commonProperties = this._getCommonProperties();
5057

51-
if (this.state === 'waiting-for-user-config' || !this.telemetryAnonymousId) {
58+
if (
59+
this.state === 'waiting-for-user-config' ||
60+
!this.telemetryAnonymousId
61+
) {
5262
this.queuedEvents.push(info);
5363
return;
5464
}
@@ -86,15 +96,28 @@ class CompassTelemetry {
8696
queuedEvents: this.queuedEvents.length,
8797
}
8898
);
89-
if (this.state === 'enabled' && this.analytics && this.telemetryAnonymousId) {
90-
this.analytics.identify({
91-
userId: this.currentUserId,
92-
anonymousId: this.telemetryAnonymousId,
93-
traits: {
94-
platform: process.platform,
95-
arch: process.arch,
96-
},
97-
});
99+
if (
100+
this.state === 'enabled' &&
101+
this.analytics &&
102+
this.telemetryAnonymousId
103+
) {
104+
void getOsInfo()
105+
.catch(() => ({})) // still identify even if getOsInfo fails
106+
.then((osInfo) => {
107+
this.analytics?.identify({
108+
userId: this.currentUserId,
109+
anonymousId: this.telemetryAnonymousId,
110+
traits: {
111+
...this._getCommonProperties(),
112+
platform: process.platform,
113+
arch: process.arch,
114+
...osInfo,
115+
},
116+
});
117+
})
118+
.catch(() => {
119+
//
120+
});
98121
}
99122

100123
let event: EventInfo | undefined;
@@ -114,7 +137,7 @@ class CompassTelemetry {
114137

115138
ipcMain.respondTo(
116139
'compass:usage:identify',
117-
(evt, meta: { currentUserId?: string, telemetryAnonymousId: string }) => {
140+
(evt, meta: { currentUserId?: string; telemetryAnonymousId: string }) => {
118141
// This always happens after the first enable/disable call.
119142
this.currentUserId = meta.currentUserId;
120143
this.telemetryAnonymousId = meta.telemetryAnonymousId;
@@ -165,7 +188,8 @@ class CompassTelemetry {
165188
}
166189

167190
static init(app: typeof CompassApplication): Promise<void> {
168-
return (this.initPromise ??= this._init(app));
191+
this.initPromise ??= Promise.resolve(this._init(app));
192+
return this.initPromise;
169193
}
170194

171195
static track(info: EventInfo): void {

0 commit comments

Comments
 (0)