diff --git a/src/vs/base/common/actions.ts b/src/vs/base/common/actions.ts index 28b9b368c32..162172f2ad8 100644 --- a/src/vs/base/common/actions.ts +++ b/src/vs/base/common/actions.ts @@ -9,6 +9,7 @@ import { IEventEmitter, EventEmitter } from 'vs/base/common/eventEmitter'; import { IDisposable } from 'vs/base/common/lifecycle'; import * as Events from 'vs/base/common/events'; import Event, { Emitter } from 'vs/base/common/event'; +import { ITelemetryData } from 'vs/platform/telemetry/common/telemetry'; export interface IAction extends IDisposable { id: string; @@ -202,7 +203,7 @@ export class Action implements IAction { this._order = value; } - public run(event?: any): TPromise { + public run(event?: any, data?: ITelemetryData): TPromise { if (this._actionCallback !== void 0) { return this._actionCallback(event); } diff --git a/src/vs/platform/telemetry/common/telemetry.ts b/src/vs/platform/telemetry/common/telemetry.ts index aca834c2d6f..f1959d63000 100644 --- a/src/vs/platform/telemetry/common/telemetry.ts +++ b/src/vs/platform/telemetry/common/telemetry.ts @@ -15,6 +15,11 @@ export interface ITelemetryInfo { instanceId: string; } +export interface ITelemetryData { + from?: string; + [key: string]: any; +} + export interface ITelemetryExperiments { showNewUserWatermark: boolean; openUntitledFile: boolean; @@ -30,7 +35,7 @@ export interface ITelemetryService { * Sends a telemetry event that has been privacy approved. * Do not call this unless you have been given approval. */ - publicLog(eventName: string, data?: any): TPromise; + publicLog(eventName: string, data?: ITelemetryData): TPromise; getTelemetryInfo(): TPromise; diff --git a/src/vs/platform/telemetry/common/telemetryService.ts b/src/vs/platform/telemetry/common/telemetryService.ts index 2b886246083..aa7e3f4a754 100644 --- a/src/vs/platform/telemetry/common/telemetryService.ts +++ b/src/vs/platform/telemetry/common/telemetryService.ts @@ -7,7 +7,7 @@ import { localize } from 'vs/nls'; import { escapeRegExpCharacters } from 'vs/base/common/strings'; -import { ITelemetryService, ITelemetryInfo, ITelemetryExperiments } from 'vs/platform/telemetry/common/telemetry'; +import { ITelemetryService, ITelemetryInfo, ITelemetryExperiments, ITelemetryData } from 'vs/platform/telemetry/common/telemetry'; import { ITelemetryAppender, defaultExperiments } from 'vs/platform/telemetry/common/telemetryUtils'; import { optional } from 'vs/platform/instantiation/common/instantiation'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -100,7 +100,7 @@ export class TelemetryService implements ITelemetryService { this._disposables = dispose(this._disposables); } - publicLog(eventName: string, data?: any): TPromise { + publicLog(eventName: string, data?: ITelemetryData): TPromise { // don't send events when the user is optout if (!this._userOptIn) { return TPromise.as(undefined); diff --git a/src/vs/platform/telemetry/common/telemetryUtils.ts b/src/vs/platform/telemetry/common/telemetryUtils.ts index 8ef9acb59ef..10be3544472 100644 --- a/src/vs/platform/telemetry/common/telemetryUtils.ts +++ b/src/vs/platform/telemetry/common/telemetryUtils.ts @@ -14,7 +14,7 @@ import { IKeybindingService, KeybindingSource } from 'vs/platform/keybinding/com import { ILifecycleService, ShutdownReason } from 'vs/platform/lifecycle/common/lifecycle'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { ITelemetryService, ITelemetryExperiments, ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry'; +import { ITelemetryService, ITelemetryExperiments, ITelemetryInfo, ITelemetryData } from 'vs/platform/telemetry/common/telemetry'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { StorageService } from 'vs/platform/storage/common/storageService'; import * as objects from 'vs/base/common/objects'; @@ -28,7 +28,7 @@ export const defaultExperiments: ITelemetryExperiments = { export const NullTelemetryService = { _serviceBrand: undefined, _experiments: defaultExperiments, - publicLog(eventName: string, data?: any) { + publicLog(eventName: string, data?: ITelemetryData) { return TPromise.as(null); }, isOptedIn: true, diff --git a/src/vs/workbench/common/actionRegistry.ts b/src/vs/workbench/common/actionRegistry.ts index e16dd395bcd..95669c4d86e 100644 --- a/src/vs/workbench/common/actionRegistry.ts +++ b/src/vs/workbench/common/actionRegistry.ts @@ -168,14 +168,15 @@ export function triggerAndDisposeAction(instantitationService: IInstantiationSer return undefined; } + const from = args && args.from || 'keybinding'; if (telemetryService) { - telemetryService.publicLog('workbenchActionExecuted', { id: actionInstance.id, from: args && args.from || 'keybinding' }); + telemetryService.publicLog('workbenchActionExecuted', { id: actionInstance.id, from }); } // run action when workbench is created return partService.joinCreation().then(() => { try { - return TPromise.as(actionInstance.run()).then(() => { + return TPromise.as(actionInstance.run(undefined, { from })).then(() => { actionInstance.dispose(); }, (err) => { actionInstance.dispose(); diff --git a/src/vs/workbench/parts/git/electron-browser/gitActions.ts b/src/vs/workbench/parts/git/electron-browser/gitActions.ts index 1a809c3fbab..b168acb47bd 100644 --- a/src/vs/workbench/parts/git/electron-browser/gitActions.ts +++ b/src/vs/workbench/parts/git/electron-browser/gitActions.ts @@ -16,6 +16,8 @@ import { IGitService } from 'vs/workbench/parts/git/common/git'; import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; import * as url from 'url'; import { remote } from 'electron'; +import { ITelemetryService, ITelemetryData } from 'vs/platform/telemetry/common/telemetry'; +import { isPromiseCanceledError } from 'vs/base/common/errors'; const dialog = remote.dialog; @@ -29,12 +31,13 @@ export class CloneAction extends Action { @IQuickOpenService private quickOpenService: IQuickOpenService, @IMessageService private messageService: IMessageService, @IWindowsService private windowsService: IWindowsService, + @ITelemetryService private telemetryService: ITelemetryService, @IWorkspaceContextService private workspaceService: IWorkspaceContextService ) { super(id, label); } - run(): TPromise { + run(event?: any, data?: ITelemetryData): TPromise { return this.quickOpenService.input({ prompt: localize('valid', "Provide a valid git repository URL"), placeHolder: localize('url', "Repository URL"), @@ -50,6 +53,7 @@ export class CloneAction extends Action { }) .then(url => { if (!url) { + this.telemetryService.publicLog('gitClone', { ...data, outcome: 'no_URL' }); return TPromise.as(null); } @@ -59,6 +63,7 @@ export class CloneAction extends Action { }); if (!result || result.length === 0) { + this.telemetryService.publicLog('gitClone', { ...data, outcome: 'no_directory' }); return TPromise.as(null); } @@ -69,14 +74,17 @@ export class CloneAction extends Action { const clone = always(this.gitService.clone(url, result[0]), () => promise.cancel()); return clone.then(path => { + this.telemetryService.publicLog('gitClone', { ...data, outcome: 'success' }); const forceNewWindow = this.workspaceService.hasWorkspace(); return this.windowsService.openWindow([path], { forceNewWindow, forceReuseWindow: !forceNewWindow }); }).then(null, e => { if (/already exists and is not an empty directory/.test(e.stderr || '')) { + this.telemetryService.publicLog('gitClone', { ...data, outcome: 'directory_not_empty' }); return TPromise.wrapError(localize('already exists', "Destination repository already exists, please pick another directory to clone to.")); } + this.telemetryService.publicLog('gitClone', { ...data, outcome: isPromiseCanceledError(e) ? 'canceled' : 'error' }); return TPromise.wrapError(e); }); });