Skip to content

Commit 072c0ae

Browse files
committed
add telemetry service to main process, for update
1 parent 2aa270d commit 072c0ae

2 files changed

Lines changed: 147 additions & 122 deletions

File tree

src/vs/code/electron-main/main.ts

Lines changed: 141 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,13 @@ import { getPathLabel } from 'vs/base/common/labels';
4747
import { IURLService } from 'vs/platform/url/common/url';
4848
import { URLChannel } from 'vs/platform/url/common/urlIpc';
4949
import { URLService } from 'vs/platform/url/electron-main/urlService';
50+
import { ITelemetryService, NullTelemetryService } from 'vs/platform/telemetry/common/telemetry';
51+
import { ITelemetryAppenderChannel, TelemetryAppenderClient } from 'vs/platform/telemetry/common/telemetryIpc';
52+
import { TelemetryService, ITelemetryServiceConfig } from 'vs/platform/telemetry/common/telemetryService';
53+
import { resolveCommonProperties } from 'vs/platform/telemetry/node/commonProperties';
54+
import { getDelayedChannel } from 'vs/base/parts/ipc/common/ipc';
5055
import product from 'vs/platform/product';
51-
56+
import pkg from 'vs/platform/package';
5257
import * as fs from 'original-fs';
5358
import * as cp from 'child_process';
5459
import * as path from 'path';
@@ -73,6 +78,7 @@ function quit(accessor: ServicesAccessor, arg?: any) {
7378
process.exit(exitCode); // in main, process.exit === app.exit
7479
}
7580

81+
// TODO@Joao wow this is huge, clean up!
7682
function main(accessor: ServicesAccessor, mainIpcServer: Server, userEnv: platform.IProcessEnvironment): void {
7783
const instantiationService = accessor.get(IInstantiationService);
7884
const logService = accessor.get(ILogService);
@@ -130,10 +136,6 @@ function main(accessor: ServicesAccessor, mainIpcServer: Server, userEnv: platfo
130136
const electronIpcServer = new ElectronIPCServer();
131137

132138
// Register Electron IPC services
133-
const updateService = accessor.get(IUpdateService);
134-
const updateChannel = new UpdateChannel(updateService);
135-
electronIpcServer.registerChannel('update', updateChannel);
136-
137139
const urlService = accessor.get(IURLService);
138140
const urlChannel = instantiationService.createInstance(URLChannel, urlService);
139141
electronIpcServer.registerChannel('url', urlChannel);
@@ -151,123 +153,150 @@ function main(accessor: ServicesAccessor, mainIpcServer: Server, userEnv: platfo
151153

152154
let sharedProcessDisposable;
153155

154-
spawnSharedProcess(initData, options).done(disposable => {
156+
const sharedProcess = spawnSharedProcess(initData, options).then(disposable => {
155157
sharedProcessDisposable = disposable;
156-
157-
connect(environmentService.sharedIPCHandle, 'main')
158-
.done(client => client.registerChannel('windowEvent', windowEventChannel));
158+
return connect(environmentService.sharedIPCHandle, 'main');
159159
});
160160

161-
// Make sure we associate the program with the app user model id
162-
// This will help Windows to associate the running program with
163-
// any shortcut that is pinned to the taskbar and prevent showing
164-
// two icons in the taskbar for the same app.
165-
if (platform.isWindows && product.win32AppUserModelId) {
166-
app.setAppUserModelId(product.win32AppUserModelId);
161+
// Create a new service collection, because the telemetry service
162+
// requires a connection to shared process, which was only established
163+
// now.
164+
const services = new ServiceCollection();
165+
services.set(IUpdateService, new SyncDescriptor(UpdateService));
166+
167+
if (environmentService.isBuilt && !environmentService.extensionDevelopmentPath && !!product.enableTelemetry) {
168+
const channel = getDelayedChannel<ITelemetryAppenderChannel>(sharedProcess.then(c => c.getChannel('telemetryAppender')));
169+
const appender = new TelemetryAppenderClient(channel);
170+
const commonProperties = resolveCommonProperties(product.commit, pkg.version);
171+
const piiPaths = [environmentService.appRoot, environmentService.extensionsPath];
172+
const config: ITelemetryServiceConfig = { appender, commonProperties, piiPaths };
173+
services.set(ITelemetryService, new SyncDescriptor(TelemetryService, config));
174+
} else {
175+
services.set(ITelemetryService, NullTelemetryService);
167176
}
168177

169-
function dispose() {
170-
if (mainIpcServer) {
171-
mainIpcServer.dispose();
172-
mainIpcServer = null;
173-
}
178+
const instantiationService2 = instantiationService.createChild(services);
174179

175-
if (sharedProcessDisposable) {
176-
sharedProcessDisposable.dispose();
177-
}
180+
instantiationService2.invokeFunction(accessor => {
181+
// Register more Electron IPC services
182+
const updateService = accessor.get(IUpdateService);
183+
const updateChannel = new UpdateChannel(updateService);
184+
electronIpcServer.registerChannel('update', updateChannel);
178185

179-
if (windowsMutex) {
180-
windowsMutex.release();
181-
}
186+
// Register windowEvent
187+
sharedProcess.done(client => client.registerChannel('windowEvent', windowEventChannel));
182188

183-
configurationService.dispose();
184-
}
189+
// Make sure we associate the program with the app user model id
190+
// This will help Windows to associate the running program with
191+
// any shortcut that is pinned to the taskbar and prevent showing
192+
// two icons in the taskbar for the same app.
193+
if (platform.isWindows && product.win32AppUserModelId) {
194+
app.setAppUserModelId(product.win32AppUserModelId);
195+
}
185196

186-
// Dispose on app quit
187-
app.on('will-quit', () => {
188-
logService.log('App#will-quit: disposing resources');
197+
function dispose() {
198+
if (mainIpcServer) {
199+
mainIpcServer.dispose();
200+
mainIpcServer = null;
201+
}
189202

190-
dispose();
191-
});
203+
if (sharedProcessDisposable) {
204+
sharedProcessDisposable.dispose();
205+
}
192206

193-
// Dispose on vscode:exit
194-
ipc.on('vscode:exit', (event, code: number) => {
195-
logService.log('IPC#vscode:exit', code);
207+
if (windowsMutex) {
208+
windowsMutex.release();
209+
}
196210

197-
dispose();
198-
process.exit(code); // in main, process.exit === app.exit
199-
});
211+
configurationService.dispose();
212+
}
200213

201-
// Lifecycle
202-
lifecycleService.ready();
214+
// Dispose on app quit
215+
app.on('will-quit', () => {
216+
logService.log('App#will-quit: disposing resources');
203217

204-
// Propagate to clients
205-
windowsMainService.ready(userEnv);
218+
dispose();
219+
});
206220

207-
// Install Menu
208-
const menu = instantiationService.createInstance(VSCodeMenu);
209-
menu.ready();
221+
// Dispose on vscode:exit
222+
ipc.on('vscode:exit', (event, code: number) => {
223+
logService.log('IPC#vscode:exit', code);
210224

211-
// Install JumpList on Windows
212-
if (platform.isWindows) {
213-
const jumpList: Electron.JumpListCategory[] = [];
214-
215-
// Tasks
216-
jumpList.push({
217-
type: 'tasks',
218-
items: [
219-
{
220-
type: 'task',
221-
title: nls.localize('newWindow', "New Window"),
222-
description: nls.localize('newWindowDesc', "Opens a new window"),
223-
program: process.execPath,
224-
args: '-n', // force new window
225-
iconPath: process.execPath,
226-
iconIndex: 0
227-
}
228-
]
225+
dispose();
226+
process.exit(code); // in main, process.exit === app.exit
229227
});
230228

231-
// Recent Folders
232-
const folders = windowsMainService.getRecentPathsList().folders;
233-
if (folders.length > 0) {
229+
// Lifecycle
230+
lifecycleService.ready();
231+
232+
// Propagate to clients
233+
windowsMainService.ready(userEnv);
234+
235+
// Install Menu
236+
const menu = instantiationService2.createInstance(VSCodeMenu);
237+
menu.ready();
238+
239+
// Install JumpList on Windows
240+
if (platform.isWindows) {
241+
const jumpList: Electron.JumpListCategory[] = [];
242+
243+
// Tasks
234244
jumpList.push({
235-
type: 'custom',
236-
name: 'Recent Folders',
237-
items: windowsMainService.getRecentPathsList().folders.slice(0, 7 /* limit number of entries here */).map(folder => {
238-
return <Electron.JumpListItem>{
245+
type: 'tasks',
246+
items: [
247+
{
239248
type: 'task',
240-
title: getPathLabel(folder),
241-
description: nls.localize('folderDesc', "{0} {1}", path.basename(folder), getPathLabel(path.dirname(folder))),
249+
title: nls.localize('newWindow', "New Window"),
250+
description: nls.localize('newWindowDesc', "Opens a new window"),
242251
program: process.execPath,
243-
args: folder, // open folder,
244-
iconPath: 'explorer.exe', // simulate folder icon
252+
args: '-n', // force new window
253+
iconPath: process.execPath,
245254
iconIndex: 0
246-
};
247-
})
255+
}
256+
]
248257
});
249-
}
250258

251-
// Recent
252-
jumpList.push({
253-
type: 'recent' // this enables to show files in the "recent" category
254-
});
259+
// Recent Folders
260+
const folders = windowsMainService.getRecentPathsList().folders;
261+
if (folders.length > 0) {
262+
jumpList.push({
263+
type: 'custom',
264+
name: 'Recent Folders',
265+
items: windowsMainService.getRecentPathsList().folders.slice(0, 7 /* limit number of entries here */).map(folder => {
266+
return <Electron.JumpListItem>{
267+
type: 'task',
268+
title: getPathLabel(folder),
269+
description: nls.localize('folderDesc', "{0} {1}", path.basename(folder), getPathLabel(path.dirname(folder))),
270+
program: process.execPath,
271+
args: folder, // open folder,
272+
iconPath: 'explorer.exe', // simulate folder icon
273+
iconIndex: 0
274+
};
275+
})
276+
});
277+
}
255278

256-
try {
257-
app.setJumpList(jumpList);
258-
} catch (error) {
259-
logService.log('#setJumpList', error); // since setJumpList is relatively new API, make sure to guard for errors
279+
// Recent
280+
jumpList.push({
281+
type: 'recent' // this enables to show files in the "recent" category
282+
});
283+
284+
try {
285+
app.setJumpList(jumpList);
286+
} catch (error) {
287+
logService.log('#setJumpList', error); // since setJumpList is relatively new API, make sure to guard for errors
288+
}
260289
}
261-
}
262290

263-
// Open our first window
264-
if (environmentService.args['new-window'] && environmentService.args._.length === 0) {
265-
windowsMainService.open({ cli: environmentService.args, forceNewWindow: true, forceEmpty: true }); // new window if "-n" was used without paths
266-
} else if (global.macOpenFiles && global.macOpenFiles.length && (!environmentService.args._ || !environmentService.args._.length)) {
267-
windowsMainService.open({ cli: environmentService.args, pathsToOpen: global.macOpenFiles }); // mac: open-file event received on startup
268-
} else {
269-
windowsMainService.open({ cli: environmentService.args, forceNewWindow: environmentService.args['new-window'], diffMode: environmentService.args.diff }); // default: read paths from cli
270-
}
291+
// Open our first window
292+
if (environmentService.args['new-window'] && environmentService.args._.length === 0) {
293+
windowsMainService.open({ cli: environmentService.args, forceNewWindow: true, forceEmpty: true }); // new window if "-n" was used without paths
294+
} else if (global.macOpenFiles && global.macOpenFiles.length && (!environmentService.args._ || !environmentService.args._.length)) {
295+
windowsMainService.open({ cli: environmentService.args, pathsToOpen: global.macOpenFiles }); // mac: open-file event received on startup
296+
} else {
297+
windowsMainService.open({ cli: environmentService.args, forceNewWindow: environmentService.args['new-window'], diffMode: environmentService.args.diff }); // default: read paths from cli
298+
}
299+
});
271300
}
272301

273302
function setupIPC(accessor: ServicesAccessor): TPromise<Server> {
@@ -437,19 +466,7 @@ function createPaths(environmentService: IEnvironmentService): TPromise<any> {
437466
return TPromise.join(paths.map(p => mkdirp(p))) as TPromise<any>;
438467
}
439468

440-
function start(): void {
441-
let args: ParsedArgs;
442-
443-
try {
444-
args = parseMainProcessArgv(process.argv);
445-
args = validatePaths(args);
446-
} catch (err) {
447-
console.error(err.message);
448-
process.exit(1);
449-
return;
450-
}
451-
452-
// TODO: isolate
469+
function createServices(args): IInstantiationService {
453470
const services = new ServiceCollection();
454471

455472
services.set(IEnvironmentService, new SyncDescriptor(EnvironmentService, args, process.execPath));
@@ -460,10 +477,24 @@ function start(): void {
460477
services.set(IStorageService, new SyncDescriptor(StorageService));
461478
services.set(IConfigurationService, new SyncDescriptor(ConfigurationService));
462479
services.set(IRequestService, new SyncDescriptor(RequestService));
463-
services.set(IUpdateService, new SyncDescriptor(UpdateService));
464480
services.set(IURLService, new SyncDescriptor(URLService, args['open-url']));
465481

466-
const instantiationService = new InstantiationService(services);
482+
return new InstantiationService(services);
483+
}
484+
485+
function start(): void {
486+
let args: ParsedArgs;
487+
488+
try {
489+
args = parseMainProcessArgv(process.argv);
490+
args = validatePaths(args);
491+
} catch (err) {
492+
console.error(err.message);
493+
process.exit(1);
494+
return;
495+
}
496+
497+
const instantiationService = createServices(args);
467498

468499
// On some platforms we need to manually read from the global environment variables
469500
// and assign them to the process environment (e.g. when doubleclick app on Mac)

src/vs/platform/update/electron-main/updateService.ts

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,10 @@ import { Win32AutoUpdaterImpl } from './auto-updater.win32';
1818
import { LinuxAutoUpdaterImpl } from './auto-updater.linux';
1919
import { ILifecycleService } from 'vs/code/electron-main/lifecycle';
2020
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
21-
import { IRequestService } from 'vs/platform/request/common/request';
2221
import product from 'vs/platform/product';
2322
import { TPromise } from 'vs/base/common/winjs.base';
2423
import { IUpdateService, State, IAutoUpdater, IUpdate, IRawUpdate } from 'vs/platform/update/common/update';
25-
26-
// TODO@Joao: add telemetry
27-
//
28-
// this.updateService.onUpdateReady(update => {
29-
// this.sendToFocused('vscode:telemetry', { eventName: 'update:downloaded', data: { version: update.version } });
30-
// });
31-
32-
// this.updateService.onUpdateNotAvailable(explicit => {
33-
// this.sendToFocused('vscode:telemetry', { eventName: 'update:notAvailable', data: { explicit } });
34-
// });
24+
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
3525

3626
export class UpdateService implements IUpdateService {
3727

@@ -97,7 +87,7 @@ export class UpdateService implements IUpdateService {
9787
@IInstantiationService instantiationService: IInstantiationService,
9888
@ILifecycleService private lifecycleService: ILifecycleService,
9989
@IConfigurationService private configurationService: IConfigurationService,
100-
@IRequestService requestService: IRequestService
90+
@ITelemetryService private telemetryService: ITelemetryService
10191
) {
10292
if (process.platform === 'win32') {
10393
this.raw = instantiationService.createInstance(Win32AutoUpdaterImpl);
@@ -109,6 +99,8 @@ export class UpdateService implements IUpdateService {
10999
return;
110100
}
111101

102+
telemetryService.publicLog('whattt', { yeah: 123 });
103+
112104
const channel = this.getUpdateChannel();
113105
const feedUrl = this.getUpdateFeedUrl(channel);
114106

@@ -167,6 +159,7 @@ export class UpdateService implements IUpdateService {
167159
if (!update) {
168160
this._onUpdateNotAvailable.fire(explicit);
169161
this.state = State.Idle;
162+
this.telemetryService.publicLog('update:notAvailable', { explicit });
170163

171164
} else if (update.url) {
172165
const data: IUpdate = {
@@ -190,6 +183,7 @@ export class UpdateService implements IUpdateService {
190183
this._availableUpdate = data;
191184
this._onUpdateReady.fire(data);
192185
this.state = State.UpdateDownloaded;
186+
this.telemetryService.publicLog('update:downloaded', { version: update.version });
193187
}
194188

195189
return update;

0 commit comments

Comments
 (0)