Skip to content

Commit 5b4adc7

Browse files
committed
remove electron telemetry service, move optin logic to telemetry service, introduce commonProperties.ts for *all* common properties, tests!, getting closer to microsoft#1000
1 parent 3f60a25 commit 5b4adc7

7 files changed

Lines changed: 346 additions & 301 deletions

File tree

src/vs/platform/telemetry/browser/telemetryService.ts

Lines changed: 48 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -5,62 +5,29 @@
55

66
'use strict';
77

8-
import * as Platform from 'vs/base/common/platform';
9-
import * as uuid from 'vs/base/common/uuid';
8+
import {localize} from 'vs/nls';
109
import {escapeRegExpCharacters} from 'vs/base/common/strings';
11-
import {IWorkspaceContextService, IEnvironment} from 'vs/platform/workspace/common/workspace';
1210
import {ITelemetryService, ITelemetryAppender, ITelemetryInfo} from 'vs/platform/telemetry/common/telemetry';
11+
import {optional} from 'vs/platform/instantiation/common/instantiation';
12+
import {IConfigurationService} from 'vs/platform/configuration/common/configuration';
13+
import {IConfigurationRegistry, Extensions} from 'vs/platform/configuration/common/configurationRegistry';
1314
import ErrorTelemetry from 'vs/platform/telemetry/common/errorTelemetry';
1415
import {IdleMonitor, UserStatus} from 'vs/base/browser/idleMonitor';
1516
import {TPromise} from 'vs/base/common/winjs.base';
1617
import {IDisposable, dispose} from 'vs/base/common/lifecycle';
1718
import {TimeKeeper, ITimerEvent} from 'vs/base/common/timer';
18-
import {withDefaults, cloneAndChange, mixin} from 'vs/base/common/objects';
19+
import {cloneAndChange, mixin} from 'vs/base/common/objects';
20+
import {Registry} from 'vs/platform/platform';
1921

2022
export interface ITelemetryServiceConfig {
2123
appender: ITelemetryAppender[];
22-
commonProperties?: TPromise<{ [name: string]: any }>[];
24+
commonProperties?: TPromise<{ [name: string]: any }>;
2325
piiPaths?: string[];
2426
userOptIn?: boolean;
2527
enableHardIdle?: boolean;
2628
enableSoftIdle?: boolean;
2729
}
2830

29-
function createDefaultProperties(environment: IEnvironment): { [name: string]: any } {
30-
31-
let seq = 0;
32-
const startTime = Date.now();
33-
const sessionID = uuid.generateUuid() + Date.now();
34-
35-
let result = {
36-
sessionID,
37-
commitHash: environment.commitHash,
38-
version: environment.version
39-
};
40-
41-
// complex names and dynamic values
42-
Object.defineProperties(result, {
43-
'timestamp': {
44-
get: () => new Date(),
45-
enumerable: true
46-
},
47-
'common.timesincesessionstart': {
48-
get: () => Date.now() - startTime,
49-
enumerable: true
50-
},
51-
'common.platform': {
52-
get: () => Platform.Platform[Platform.platform],
53-
enumerable: true
54-
},
55-
'common.sequence': {
56-
get: () => seq++,
57-
enumerable: true
58-
}
59-
});
60-
61-
return result;
62-
}
63-
6431
export class TelemetryService implements ITelemetryService {
6532

6633
// how long of inactivity before a user is considered 'inactive' - 2 minutes
@@ -71,31 +38,25 @@ export class TelemetryService implements ITelemetryService {
7138

7239
public serviceId = ITelemetryService;
7340

74-
protected _configuration: ITelemetryServiceConfig;
75-
protected _disposables: IDisposable[] = [];
76-
41+
private _configuration: ITelemetryServiceConfig;
42+
private _disposables: IDisposable[] = [];
7743
private _timeKeeper: TimeKeeper;
7844
private _hardIdleMonitor: IdleMonitor;
7945
private _softIdleMonitor: IdleMonitor;
80-
81-
private _commonProperties: TPromise<{ [name: string]: any }>;
8246
private _cleanupPatterns: [RegExp, string][] = [];
8347

8448
constructor(
8549
config: ITelemetryServiceConfig,
86-
@IWorkspaceContextService contextService: IWorkspaceContextService
50+
@optional(IConfigurationService) private _configurationService: IConfigurationService
8751
) {
88-
this._configuration = withDefaults(config, <ITelemetryServiceConfig>{
52+
this._configuration = mixin(config, <ITelemetryServiceConfig>{
8953
appender: [],
90-
commonProperties: [],
54+
commonProperties: TPromise.as({}),
9155
piiPaths: [],
9256
enableHardIdle: true,
9357
enableSoftIdle: true,
9458
userOptIn: true
95-
});
96-
97-
this._commonProperties = TPromise.join(this._configuration.commonProperties)
98-
.then(values => values.reduce((p, c) => mixin(p, c), createDefaultProperties(contextService.getConfiguration().env)));
59+
}, false);
9960

10061
// static cleanup patterns for:
10162
// #1 `file:///DANGEROUS/PATH/resources/app/Useful/Information`
@@ -127,6 +88,17 @@ export class TelemetryService implements ITelemetryService {
12788
this._softIdleMonitor.addOneTimeIdleListener(() => this._onUserIdle());
12889
this._disposables.push(this._softIdleMonitor);
12990
}
91+
92+
if (this._configurationService) {
93+
this._updateUserOptIn();
94+
this._configurationService.onDidUpdateConfiguration(this._updateUserOptIn, this, this._disposables);
95+
this.publicLog('optInStatus', { optIn: this._configuration.userOptIn });
96+
}
97+
}
98+
99+
private _updateUserOptIn(): void {
100+
const config = this._configurationService.getConfiguration<any>(TELEMETRY_SECTION_ID);
101+
this._configuration.userOptIn = config ? config.enableTelemetry : this._configuration.userOptIn;
130102
}
131103

132104
private _onUserIdle(): void {
@@ -153,7 +125,7 @@ export class TelemetryService implements ITelemetryService {
153125
}
154126

155127
public getTelemetryInfo(): TPromise<ITelemetryInfo> {
156-
return this._commonProperties.then(values => {
128+
return this._configuration.commonProperties.then(values => {
157129
// well known properties
158130
let sessionId = values['sessionID'];
159131
let instanceId = values['common.instanceId'];
@@ -179,23 +151,20 @@ export class TelemetryService implements ITelemetryService {
179151
return event;
180152
}
181153

182-
public publicLog(eventName: string, data?: any): void {
183-
this._handleEvent(eventName, data);
184-
}
154+
public publicLog(eventName: string, data?: any): TPromise<any> {
185155

186-
private _handleEvent(eventName: string, data?: any): void {
187156
if (this._hardIdleMonitor && this._hardIdleMonitor.getStatus() === UserStatus.Idle) {
188-
return;
157+
return TPromise.as(undefined);
189158
}
190159

191160
// don't send events when the user is optout unless the event is the opt{in|out} signal
192161
if (!this._configuration.userOptIn && eventName !== 'optInStatus') {
193-
return;
162+
return TPromise.as(undefined);
194163
}
195164

196-
// (first) add common properties
197-
this._commonProperties.then(values => {
165+
return this._configuration.commonProperties.then(values => {
198166

167+
// (first) add common properties
199168
data = mixin(data, values);
200169

201170
// (last) remove all PII from data
@@ -209,7 +178,8 @@ export class TelemetryService implements ITelemetryService {
209178
appender.log(eventName, data);
210179
}
211180

212-
}).done(undefined, err => {
181+
}, err => {
182+
// unsure what to do now...
213183
console.error(err);
214184
});
215185
}
@@ -226,3 +196,19 @@ export class TelemetryService implements ITelemetryService {
226196
}
227197
}
228198

199+
200+
const TELEMETRY_SECTION_ID = 'telemetry';
201+
202+
Registry.as<IConfigurationRegistry>(Extensions.Configuration).registerConfiguration({
203+
'id': TELEMETRY_SECTION_ID,
204+
'order': 20,
205+
'type': 'object',
206+
'title': localize('telemetryConfigurationTitle', "Telemetry configuration"),
207+
'properties': {
208+
'telemetry.enableTelemetry': {
209+
'type': 'boolean',
210+
'description': localize('telemetry.enableTelemetry', "Enable usage data and errors to be sent to Microsoft."),
211+
'default': true
212+
}
213+
}
214+
});

src/vs/platform/telemetry/common/telemetry.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export interface ITelemetryService {
2424
* Sends a telemetry event that has been privacy approved.
2525
* Do not call this unless you have been given approval.
2626
*/
27-
publicLog(eventName: string, data?: any): void;
27+
publicLog(eventName: string, data?: any): any;
2828

2929
/**
3030
* Starts a telemetry timer. Call stop() to send the event.

src/vs/platform/telemetry/electron-browser/electronTelemetryService.ts renamed to src/vs/platform/telemetry/node/commonProperties.ts

Lines changed: 41 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,17 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6-
import getmac = require('getmac');
7-
import crypto = require('crypto');
8-
import winreg = require('winreg');
9-
import os = require('os');
10-
import { TPromise } from 'vs/base/common/winjs.base';
11-
import * as nls from 'vs/nls';
6+
import * as getmac from 'getmac';
7+
import * as crypto from 'crypto';
8+
import * as winreg from 'winreg';
9+
import * as Platform from 'vs/base/common/platform';
10+
import * as os from 'os';
11+
import {TPromise} from 'vs/base/common/winjs.base';
1212
import * as errors from 'vs/base/common/errors';
1313
import * as uuid from 'vs/base/common/uuid';
14-
import {TelemetryService, ITelemetryServiceConfig} from 'vs/platform/telemetry/browser/telemetryService';
15-
import {ITelemetryService} from 'vs/platform/telemetry/common/telemetry';
1614
import {IStorageService} from 'vs/platform/storage/common/storage';
1715
import {IWorkspaceContextService} from 'vs/platform/workspace/common/workspace';
18-
import {IConfigurationService} from 'vs/platform/configuration/common/configuration';
19-
import {IConfigurationRegistry, Extensions} from 'vs/platform/configuration/common/configurationRegistry';
20-
import {Registry} from 'vs/platform/platform';
2116

22-
const TELEMETRY_SECTION_ID = 'telemetry';
2317

2418
namespace StorageKeys {
2519
export const MachineId = 'telemetry.machineId';
@@ -31,10 +25,15 @@ namespace StorageKeys {
3125
export const SQM_KEY: string = '\\Software\\Microsoft\\SQMClient';
3226
}
3327

34-
export function getDefaultProperties(storageService: IStorageService): TPromise<{ [name: string]: string }> {
28+
export function resolveCommonProperties(storageService: IStorageService, contextService: IWorkspaceContextService): TPromise<{ [name: string]: string }> {
29+
3530
let result: { [name: string]: any } = Object.create(null);
3631

32+
result['sessionID'] = uuid.generateUuid() + Date.now();
33+
result['commitHash'] = contextService.getConfiguration().env.commitHash;
34+
3735
// add shell & render version
36+
result['version'] = contextService.getConfiguration().env.version;
3837
result['common.version.shell'] = process.versions && (<any>process).versions['electron'];
3938
result['common.version.renderer'] = process.versions && (<any>process).versions['chrome'];
4039
result['common.osVersion'] = os.release();
@@ -48,15 +47,39 @@ export function getDefaultProperties(storageService: IStorageService): TPromise<
4847
result['common.lastSessionDate'] = lastSessionDate;
4948
result['common.isNewSession'] = !lastSessionDate ? 1 : 0;
5049

50+
51+
// dynamic properties which value differs on each call
52+
let seq = 0;
53+
const startTime = Date.now();
54+
Object.defineProperties(result, {
55+
'timestamp': {
56+
get: () => new Date(),
57+
enumerable: true
58+
},
59+
'common.timesincesessionstart': {
60+
get: () => Date.now() - startTime,
61+
enumerable: true
62+
},
63+
'common.platform': {
64+
get: () => Platform.Platform[Platform.platform],
65+
enumerable: true
66+
},
67+
'common.sequence': {
68+
get: () => seq++,
69+
enumerable: true
70+
}
71+
});
72+
5173
// promise based properties
52-
result['common.instanceId'] = getOrCreateInstanceId(storageService);
53-
result['common.machineId'] = getOrCreateMachineId(storageService);
74+
let promises: TPromise<any>[] = [];
75+
promises.push(getOrCreateInstanceId(storageService).then(value => result['common.instanceId'] = value));
76+
promises.push(getOrCreateMachineId(storageService).then(value => result['common.machineId'] = value));
5477
if (process.platform === 'win32') {
55-
result['common.sqm.userid'] = getSqmUserId(storageService);
56-
result['common.sqm.machineid'] = getSqmMachineId(storageService);
78+
promises.push(getSqmUserId(storageService).then(value => result['common.sqm.userid']= value));
79+
promises.push(getSqmMachineId(storageService).then(value => result['common.sqm.machineid']= value));
5780
}
5881

59-
return TPromise.join(result);
82+
return TPromise.join(promises).then(_ => result);
6083
}
6184

6285
function getOrCreateInstanceId( storageService: IStorageService): TPromise<string> {
@@ -142,37 +165,3 @@ function getWinRegKeyData(key: string, name: string, hive: string): TPromise<str
142165
return undefined;
143166
});
144167
}
145-
146-
export class ElectronTelemetryService extends TelemetryService implements ITelemetryService {
147-
148-
constructor(
149-
configuration: ITelemetryServiceConfig,
150-
@IConfigurationService private _configurationService: IConfigurationService,
151-
@IWorkspaceContextService contextService:IWorkspaceContextService
152-
) {
153-
super(configuration, contextService);
154-
155-
this._updateUserOptIn();
156-
this._configurationService.onDidUpdateConfiguration(this._updateUserOptIn, this, this._disposables);
157-
this.publicLog('optInStatus', { optIn: this._configuration.userOptIn });
158-
}
159-
160-
private _updateUserOptIn(): void {
161-
const config = this._configurationService.getConfiguration<any>(TELEMETRY_SECTION_ID);
162-
this._configuration.userOptIn = config ? config.enableTelemetry : this._configuration.userOptIn;
163-
}
164-
}
165-
166-
Registry.as<IConfigurationRegistry>(Extensions.Configuration).registerConfiguration({
167-
'id': TELEMETRY_SECTION_ID,
168-
'order': 20,
169-
'type': 'object',
170-
'title': nls.localize('telemetryConfigurationTitle', "Telemetry configuration"),
171-
'properties': {
172-
'telemetry.enableTelemetry': {
173-
'type': 'boolean',
174-
'description': nls.localize('telemetry.enableTelemetry', "Enable usage data and errors to be sent to Microsoft."),
175-
'default': true
176-
}
177-
}
178-
});

0 commit comments

Comments
 (0)