microsoft/vscode-react-native

Public

mirrored from https://github.com/microsoft/vscode-react-nativeAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
0.1.0

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

src/common/telemetryHelper.ts

238lines · modeblame

d976d077Meena Kunnathur Balakrishnan10 years ago1// Copyright (c) Microsoft Corporation. All rights reserved.
2// Licensed under the MIT license. See LICENSE file in the project root for details.
3
4import * as Q from "q";
5import {Telemetry} from "./telemetry";
6cd1e194Meena Kunnathur Balakrishnan10 years ago6
7export interface ITelemetryPropertyInfo {
8value: any;
9isPii: boolean;
10}
11
12export interface ICommandTelemetryProperties {
13[propertyName: string]: ITelemetryPropertyInfo;
14}
15
16export interface IExternalTelemetryProvider {
17sendTelemetry: (event: string, props: Telemetry.ITelemetryProperties, error?: Error) => void;
18}
19
20interface IDictionary<T> {
21[key: string]: T;
22}
23
24interface IHasErrorCode {
25errorCode: number;
26}
27
28export abstract class TelemetryGeneratorBase {
29protected telemetryProperties: ICommandTelemetryProperties = {};
30private componentName: string;
31private currentStepStartTime: number[];
d976d077Meena Kunnathur Balakrishnan10 years ago32private currentStep: string = "initialStep";
6cd1e194Meena Kunnathur Balakrishnan10 years ago33private errorIndex: number = -1; // In case we have more than one error (We start at -1 because we increment it before using it)
34
35constructor(componentName: string) {
36this.componentName = componentName;
37this.currentStepStartTime = process.hrtime();
38}
39
40protected abstract sendTelemetryEvent(telemetryEvent: Telemetry.TelemetryEvent): void;
41
42public add(baseName: string, value: any, isPii: boolean): TelemetryGeneratorBase {
43return this.addWithPiiEvaluator(baseName, value, () => isPii);
44}
45
46public addWithPiiEvaluator(baseName: string, value: any, piiEvaluator: { (value: string, name: string): boolean }): TelemetryGeneratorBase {
47// We have 3 cases:
48// * Object is an array, we add each element as baseNameNNN
49// * Object is a hash, we add each element as baseName.KEY
50// * Object is a value, we add the element as baseName
51try {
52if (Array.isArray(value)) {
53this.addArray(baseName, <any[]> value, piiEvaluator);
d976d077Meena Kunnathur Balakrishnan10 years ago54} else if (!!value && (typeof value === "object" || typeof value === "function")) {
6cd1e194Meena Kunnathur Balakrishnan10 years ago55this.addHash(baseName, <IDictionary<any>> value, piiEvaluator);
56} else {
57this.addString(baseName, String(value), piiEvaluator);
58}
59} catch (error) {
d976d077Meena Kunnathur Balakrishnan10 years ago60// We don"t want to crash the functionality if the telemetry fails.
61// This error message will be a javascript error message, so it"s not pii
62this.addString("telemetryGenerationError." + baseName, String(error), () => false);
6cd1e194Meena Kunnathur Balakrishnan10 years ago63}
64
65return this;
66}
67
68public addError(error: Error): TelemetryGeneratorBase {
d976d077Meena Kunnathur Balakrishnan10 years ago69this.add("error.message" + ++this.errorIndex, error.message, /*isPii*/ true);
70let errorWithErrorCode: IHasErrorCode = <IHasErrorCode> <Object> error;
6cd1e194Meena Kunnathur Balakrishnan10 years ago71if (errorWithErrorCode.errorCode) {
d976d077Meena Kunnathur Balakrishnan10 years ago72this.add("error.code" + this.errorIndex, errorWithErrorCode.errorCode, /*isPii*/ false);
6cd1e194Meena Kunnathur Balakrishnan10 years ago73}
74
75return this;
76}
77
78public time<T>(name: string, codeToMeasure: { (): Thenable<T> }): Q.Promise<T> {
d976d077Meena Kunnathur Balakrishnan10 years ago79let startTime: number[] = process.hrtime();
80return Q(codeToMeasure())
81.finally(() => this.finishTime(name, startTime))
82.fail((reason: any): Q.Promise<T> => {
6cd1e194Meena Kunnathur Balakrishnan10 years ago83this.addError(reason);
d976d077Meena Kunnathur Balakrishnan10 years ago84return Q.reject<T>(reason);
6cd1e194Meena Kunnathur Balakrishnan10 years ago85});
86}
87
88public step(name: string): TelemetryGeneratorBase {
89// First we finish measuring this step time, and we send a telemetry event for this step
90this.finishTime(this.currentStep, this.currentStepStartTime);
91this.sendCurrentStep();
92
93// Then we prepare to start gathering information about the next step
94this.currentStep = name;
95this.telemetryProperties = {};
96this.currentStepStartTime = process.hrtime();
97return this;
98}
99
100public send(): void {
101if (this.currentStep) {
d976d077Meena Kunnathur Balakrishnan10 years ago102this.add("lastStepExecuted", this.currentStep, /*isPii*/ false);
6cd1e194Meena Kunnathur Balakrishnan10 years ago103}
104
105this.step(null); // Send the last step
106}
107
108private sendCurrentStep(): void {
d976d077Meena Kunnathur Balakrishnan10 years ago109this.add("step", this.currentStep, /*isPii*/ false);
110let telemetryEvent: Telemetry.TelemetryEvent = new Telemetry.TelemetryEvent(Telemetry.appName + "/" + this.componentName);
6cd1e194Meena Kunnathur Balakrishnan10 years ago111TelemetryHelper.addTelemetryEventProperties(telemetryEvent, this.telemetryProperties);
112this.sendTelemetryEvent(telemetryEvent);
113}
114
115private addArray(baseName: string, array: any[], piiEvaluator: { (value: string, name: string): boolean }): void {
116// Object is an array, we add each element as baseNameNNN
d976d077Meena Kunnathur Balakrishnan10 years ago117let elementIndex: number = 1; // We send telemetry properties in a one-based index
6cd1e194Meena Kunnathur Balakrishnan10 years ago118array.forEach((element: any) => this.addWithPiiEvaluator(baseName + elementIndex++, element, piiEvaluator));
119}
120
121private addHash(baseName: string, hash: IDictionary<any>, piiEvaluator: { (value: string, name: string): boolean }): void {
122// Object is a hash, we add each element as baseName.KEY
d976d077Meena Kunnathur Balakrishnan10 years ago123Object.keys(hash).forEach((key: string) => this.addWithPiiEvaluator(baseName + "." + key, hash[key], piiEvaluator));
6cd1e194Meena Kunnathur Balakrishnan10 years ago124}
125
126private addString(name: string, value: string, piiEvaluator: { (value: string, name: string): boolean }): void {
127this.telemetryProperties[name] = TelemetryHelper.telemetryProperty(value, piiEvaluator(value, name));
128}
129
130private combine(...components: string[]): string {
d976d077Meena Kunnathur Balakrishnan10 years ago131let nonNullComponents: string[] = components.filter((component: string) => component !== null);
132return nonNullComponents.join(".");
6cd1e194Meena Kunnathur Balakrishnan10 years ago133}
134
135private finishTime(name: string, startTime: number[]): void {
d976d077Meena Kunnathur Balakrishnan10 years ago136let endTime: number[] = process.hrtime(startTime);
137this.add(this.combine(name, "time"), String(endTime[0] * 1000 + endTime[1] / 1000000), /*isPii*/ false);
6cd1e194Meena Kunnathur Balakrishnan10 years ago138}
139}
140
141export class TelemetryGenerator extends TelemetryGeneratorBase {
142protected sendTelemetryEvent(telemetryEvent: Telemetry.TelemetryEvent): void {
143Telemetry.send(telemetryEvent);
144}
145}
146
147export class TelemetryHelper {
642490c1Jimmy Thomson10 years ago148public static sendSimpleEvent(eventName: string, properties?: Telemetry.ITelemetryProperties): void {
149const event = TelemetryHelper.createTelemetryEvent(eventName, properties);
150Telemetry.send(event);
151}
152public static createTelemetryEvent(eventName: string, properties?: Telemetry.ITelemetryProperties): Telemetry.TelemetryEvent {
153return new Telemetry.TelemetryEvent(Telemetry.appName + "/" + eventName, properties);
6cd1e194Meena Kunnathur Balakrishnan10 years ago154}
155
156public static telemetryProperty(propertyValue: any, pii?: boolean): ITelemetryPropertyInfo {
157return { value: String(propertyValue), isPii: pii || false };
158}
159
160public static addTelemetryEventProperties(event: Telemetry.TelemetryEvent, properties: ICommandTelemetryProperties): void {
161if (!properties) {
162return;
163}
164
165Object.keys(properties).forEach(function (propertyName: string): void {
166TelemetryHelper.addTelemetryEventProperty(event, propertyName, properties[propertyName].value, properties[propertyName].isPii);
167});
168}
169
170public static sendCommandSuccessTelemetry(commandName: string, commandProperties: ICommandTelemetryProperties, args: string[] = null): void {
d976d077Meena Kunnathur Balakrishnan10 years ago171let successEvent: Telemetry.TelemetryEvent = TelemetryHelper.createBasicCommandTelemetry(commandName, args);
6cd1e194Meena Kunnathur Balakrishnan10 years ago172
173TelemetryHelper.addTelemetryEventProperties(successEvent, commandProperties);
174
175Telemetry.send(successEvent);
176}
177
178public static addTelemetryEventProperty(event: Telemetry.TelemetryEvent, propertyName: string, propertyValue: any, isPii: boolean): void {
179if (Array.isArray(propertyValue)) {
180TelemetryHelper.addMultiValuedTelemetryEventProperty(event, propertyName, propertyValue, isPii);
181} else {
182TelemetryHelper.setTelemetryEventProperty(event, propertyName, propertyValue, isPii);
183}
184}
185
d976d077Meena Kunnathur Balakrishnan10 years ago186public static addPropertiesFromOptions(telemetryProperties: ICommandTelemetryProperties, knownOptions: any, commandOptions: {[flag: string]: any}, nonPiiOptions: string[] = []): ICommandTelemetryProperties {
6cd1e194Meena Kunnathur Balakrishnan10 years ago187// We parse only the known options, to avoid potential private information that may appear on the command line
d976d077Meena Kunnathur Balakrishnan10 years ago188let unknownOptionIndex: number = 1;
6cd1e194Meena Kunnathur Balakrishnan10 years ago189Object.keys(commandOptions).forEach((key: string) => {
d976d077Meena Kunnathur Balakrishnan10 years ago190let value: any = commandOptions[key];
6cd1e194Meena Kunnathur Balakrishnan10 years ago191if (Object.keys(knownOptions).indexOf(key) >= 0) {
d976d077Meena Kunnathur Balakrishnan10 years ago192// This is a known option. We"ll check the list to decide if it"s pii or not
193if (typeof (value) !== "undefined") {
6cd1e194Meena Kunnathur Balakrishnan10 years ago194// We encrypt all options values unless they are specifically marked as nonPii
d976d077Meena Kunnathur Balakrishnan10 years ago195telemetryProperties["options." + key] = this.telemetryProperty(value, nonPiiOptions.indexOf(key) < 0);
6cd1e194Meena Kunnathur Balakrishnan10 years ago196}
197} else {
d976d077Meena Kunnathur Balakrishnan10 years ago198// This is a not known option. We"ll assume that both the option and the value are pii
199telemetryProperties["unknownOption" + unknownOptionIndex + ".name"] = this.telemetryProperty(key, /*isPii*/ true);
200telemetryProperties["unknownOption" + unknownOptionIndex++ + ".value"] = this.telemetryProperty(value, /*isPii*/ true);
6cd1e194Meena Kunnathur Balakrishnan10 years ago201}
202});
203return telemetryProperties;
204}
205
206public static generate<T>(name: string, codeGeneratingTelemetry: { (telemetry: TelemetryGenerator): Thenable<T> }): Q.Promise<T> {
d976d077Meena Kunnathur Balakrishnan10 years ago207let generator: TelemetryGenerator = new TelemetryGenerator(name);
6cd1e194Meena Kunnathur Balakrishnan10 years ago208return generator.time(null, () => codeGeneratingTelemetry(generator)).finally(() => generator.send());
209}
210
211private static createBasicCommandTelemetry(commandName: string, args: string[] = null): Telemetry.TelemetryEvent {
d976d077Meena Kunnathur Balakrishnan10 years ago212let commandEvent: Telemetry.TelemetryEvent = new Telemetry.TelemetryEvent(Telemetry.appName + "/" + (commandName || "command"));
6cd1e194Meena Kunnathur Balakrishnan10 years ago213
214if (!commandName && args && args.length > 0) {
d976d077Meena Kunnathur Balakrishnan10 years ago215commandEvent.setPiiProperty("command", args[0]);
6cd1e194Meena Kunnathur Balakrishnan10 years ago216}
217
218if (args) {
d976d077Meena Kunnathur Balakrishnan10 years ago219TelemetryHelper.addTelemetryEventProperty(commandEvent, "argument", args, true);
6cd1e194Meena Kunnathur Balakrishnan10 years ago220}
221
222return commandEvent;
223}
224
225private static setTelemetryEventProperty(event: Telemetry.TelemetryEvent, propertyName: string, propertyValue: string, isPii: boolean): void {
226if (isPii) {
227event.setPiiProperty(propertyName, String(propertyValue));
228} else {
229event.properties[propertyName] = String(propertyValue);
230}
231}
232
233private static addMultiValuedTelemetryEventProperty(event: Telemetry.TelemetryEvent, propertyName: string, propertyValue: string, isPii: boolean): void {
d976d077Meena Kunnathur Balakrishnan10 years ago234for (let i: number = 0; i < propertyValue.length; i++) {
6cd1e194Meena Kunnathur Balakrishnan10 years ago235TelemetryHelper.setTelemetryEventProperty(event, propertyName + i, propertyValue[i], isPii);
236}
237}
238};