electronTelemetryService.ts 5.4 KB
Newer Older
E
Erich Gamma 已提交
1 2 3 4 5 6 7 8
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

import getmac = require('getmac');
import crypto = require('crypto');

9 10 11
import * as nls from 'vs/nls';
import * as errors from 'vs/base/common/errors';
import * as uuid from 'vs/base/common/uuid';
12 13
import {MainTelemetryService} from 'vs/platform/telemetry/browser/mainTelemetryService';
import {ITelemetryService, ITelemetryInfo, ITelemetryServiceConfig} from 'vs/platform/telemetry/common/telemetry';
S
Sofian Hnaide 已提交
14
import {IStorageService} from 'vs/platform/storage/common/storage';
15 16 17 18 19 20
import {IConfigurationRegistry, Extensions} from 'vs/platform/configuration/common/configurationRegistry';
import {IConfigurationService, IConfigurationServiceEvent, ConfigurationServiceEventTypes} from 'vs/platform/configuration/common/configuration';
import {Registry} from 'vs/platform/platform';


const TELEMETRY_SECTION_ID = 'telemetry';
E
Erich Gamma 已提交
21 22

class StorageKeys {
B
Benjamin Pasero 已提交
23 24
	public static MachineId: string = 'telemetry.machineId';
	public static InstanceId: string = 'telemetry.instanceId';
E
Erich Gamma 已提交
25 26
}

S
Sofian Hnaide 已提交
27 28 29 30 31
interface ITelemetryEvent {
	eventName: string;
	data?: any;
}

E
Erich Gamma 已提交
32 33
export class ElectronTelemetryService extends MainTelemetryService implements ITelemetryService {

S
Sofian Hnaide 已提交
34 35
	private static MAX_BUFFER_SIZE = 100;

S
Sofian Hnaide 已提交
36
	private _setupIds: Promise<ITelemetryInfo>;
S
Sofian Hnaide 已提交
37 38
	private _buffer: ITelemetryEvent[];
	private _optInStatusLoaded: boolean;
S
Sofian Hnaide 已提交
39

40
	constructor(@IConfigurationService private configurationService: IConfigurationService, @IStorageService private storageService: IStorageService, config?: ITelemetryServiceConfig) {
E
Erich Gamma 已提交
41 42
		super(config);

S
Sofian Hnaide 已提交
43 44 45
		this._buffer = [];
		this._optInStatusLoaded = false;

46
		this.loadOptinSettings();
S
Sofian Hnaide 已提交
47 48 49 50 51 52 53 54
		this._setupIds = this.setupIds();
	}

	/**
	 * override the base getTelemetryInfo to make sure this information is not retrieved before it's ready
	 */
	public getTelemetryInfo(): Promise<ITelemetryInfo> {
		return this._setupIds;
E
Erich Gamma 已提交
55
	}
S
Sofian Hnaide 已提交
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
	/**
	 * override the base publicLog to prevent reporting any events before the optIn status is read from configuration
	 */
	public publicLog(eventName:string, data?: any): void {
		if (this._optInStatusLoaded) {
			super.publicLog(eventName, data);
		} else {
			// in case loading configuration is delayed, make sure the buffer does not grow beyond MAX_BUFFER_SIZE
			if (this._buffer.length > ElectronTelemetryService.MAX_BUFFER_SIZE) {
				this._buffer = [];
			}
			this._buffer.push({eventName: eventName, data: data});
		}
	}

	private flushBuffer(): void {
		let event: ITelemetryEvent = null;
		while(event = this._buffer.pop()) {
			super.publicLog(event.eventName, event.data);
		}
	}
E
Erich Gamma 已提交
77

78
	private loadOptinSettings(): void {
S
Sofian Hnaide 已提交
79 80 81 82 83 84 85
		this.configurationService.loadConfiguration(TELEMETRY_SECTION_ID).done(config => {
			this.config.userOptIn = config ? config.enableTelemetry : this.config.userOptIn;
			this._optInStatusLoaded = true;
			this.publicLog('optInStatus', {optIn: this.config.userOptIn});
			this.flushBuffer();
		});

86 87 88 89 90
		this.toUnbind.push(this.configurationService.addListener(ConfigurationServiceEventTypes.UPDATED, (e: IConfigurationServiceEvent) => {
			this.config.userOptIn = e.config && e.config[TELEMETRY_SECTION_ID] ? e.config[TELEMETRY_SECTION_ID].enableTelemetry : this.config.userOptIn;
		}));
	}

S
Sofian Hnaide 已提交
91
	private setupIds(): Promise<ITelemetryInfo> {
B
Benjamin Pasero 已提交
92 93 94 95 96 97
		return Promise.all([this.setupInstanceId(), this.setupMachineId()]).then(() => {
			return {
				machineId: this.machineId,
				instanceId: this.instanceId,
				sessionId: this.sessionId
			};
S
Sofian Hnaide 已提交
98 99 100 101
		});
	}

	private setupInstanceId(): Promise<string> {
B
Benjamin Pasero 已提交
102 103
		let instanceId = this.storageService.get(StorageKeys.InstanceId);
		if (!instanceId) {
E
Erich Gamma 已提交
104 105 106 107
			instanceId = uuid.generateUuid();
			this.storageService.store(StorageKeys.InstanceId, instanceId);
		}
		this.instanceId = instanceId;
S
Sofian Hnaide 已提交
108
		return Promise.resolve(this.instanceId);
E
Erich Gamma 已提交
109 110
	}

S
Sofian Hnaide 已提交
111
	private setupMachineId(): Promise<string> {
B
Benjamin Pasero 已提交
112
		let machineId = this.storageService.get(StorageKeys.MachineId);
E
Erich Gamma 已提交
113 114
		if (machineId) {
			this.machineId = machineId;
S
Sofian Hnaide 已提交
115
			return Promise.resolve(this.machineId);
E
Erich Gamma 已提交
116
		} else {
S
Sofian Hnaide 已提交
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
			return new Promise((resolve, reject) => {
				try {
					// add a unique machine id as a hash of the macAddress
					getmac.getMac((error, macAddress) => {
						if (!error) {
							// crypt machine id
							machineId = crypto.createHash('sha256').update(macAddress, 'utf8').digest('hex');
						} else {
							// generate a UUID
							machineId = uuid.generateUuid();
						}

						this.machineId = machineId;
						this.storageService.store(StorageKeys.MachineId, machineId);
						resolve(this.machineId);
					});
				} catch (err) {
					errors.onUnexpectedError(err);
E
Erich Gamma 已提交
135

S
Sofian Hnaide 已提交
136 137
					// generate a UUID
					machineId = uuid.generateUuid();
E
Erich Gamma 已提交
138 139
					this.machineId = machineId;
					this.storageService.store(StorageKeys.MachineId, machineId);
S
Sofian Hnaide 已提交
140 141 142
					resolve(this.machineId);
				}
			});
E
Erich Gamma 已提交
143 144 145

		}
	}
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
}

const configurationRegistry = <IConfigurationRegistry>Registry.as(Extensions.Configuration);
configurationRegistry.registerConfiguration({
	'id': TELEMETRY_SECTION_ID,
	'order': 20,
	'type': 'object',
	'title': nls.localize('telemetryConfigurationTitle', "Telemetry configuration"),
	'properties': {
		'telemetry.enableTelemetry': {
			'type': 'boolean',
			'description': nls.localize('telemetry.enableTelemetry', "Enable usage data and errors to be sent to Microsoft."),
			'default': true
		}
	}
});