forked from microsoft/vscode-pull-request-github
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtelemetry.ts
More file actions
169 lines (147 loc) · 5.56 KB
/
Copy pathtelemetry.ts
File metadata and controls
169 lines (147 loc) · 5.56 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
import * as vscode from 'vscode';
import Logger from './logger';
import { StatsStore, AppName, ISettings, IStatsDatabase, IMetrics, getYearMonthDay } from 'telemetry-github';
import { ITelemetry } from '../github/interface';
const TELEMETRY_KEY = 'vscode-pull-request-github.telemetry';
export class Telemetry implements ITelemetry {
private _version; string;
private _telemetry: StatsStore;
constructor(private readonly _context: vscode.ExtensionContext) {
this._version = vscode.extensions.getExtension('GitHub.vscode-pull-request-github').packageJSON.version;
const database = new MementoDatabase(this._context, () => this._telemetry.createReport());
this._telemetry = new StatsStore(AppName.VSCode, this._version,
() => '',
new VSSettings(this._context),
database
);
}
public on(action: string): Promise<void> {
return this._telemetry.incrementCounter(action).catch(e => Logger.appendLine(e));
}
public shutdown(): Promise<void> {
return this._telemetry.shutdown().catch(e => Logger.appendLine(e));
}
}
/** This backend provides access to data such as:
* - last time stats were reported (stored in memento)
* - whether the user has opted out from telemetry reports (stored in vscode settings)
* */
class VSSettings implements ISettings {
private _config: vscode.WorkspaceConfiguration;
constructor(private readonly _context: vscode.ExtensionContext) {
this._config = vscode.workspace.getConfiguration('telemetry');
}
getItem(key: string): Promise<string> {
switch (key) {
case 'last-daily-stats-report':
return Promise.resolve(this._context.globalState.get<string>(`${TELEMETRY_KEY}.last`));
case 'stats-guid':
return Promise.resolve(this._context.globalState.get<string>(`${TELEMETRY_KEY}.guid`));
case 'has-sent-stats-opt-in-ping':
return Promise.resolve(this._context.globalState.get<string>(`${TELEMETRY_KEY}.pinged`));
case 'stats-opt-out':
return Promise.resolve(this._config.get('optout'));
}
return Promise.resolve(this._config.get(key));
}
setItem(key: string, value: string): Promise<void> {
switch (key) {
case 'last-daily-stats-report':
return Promise.resolve(this._context.globalState.update(`${TELEMETRY_KEY}.last`, value));
case 'stats-guid':
return Promise.resolve(this._context.globalState.update(`${TELEMETRY_KEY}.guid`, value));
case 'has-sent-stats-opt-in-ping':
return Promise.resolve(this._context.globalState.update(`${TELEMETRY_KEY}.pinged`, value));
case 'stats-opt-out':
return Promise.resolve(this._config.update('optout', value));
}
return Promise.resolve(this._config.update(key, value));
}
}
interface DBEntry {
date: number;
instanceId: string;
metrics: IMetrics;
}
const now = () => new Date(Date.now()).toISOString();
/** This stores the telemetry data if the user has not opted out and until it is sent out */
class MementoDatabase implements IStatsDatabase {
constructor(private readonly _context: vscode.ExtensionContext, private readonly _createReport: () => IMetrics) { }
public close(): Promise<void> {
return Promise.resolve();
}
public async addCustomEvent(instanceId: string, eventType: string, customEvent: any): Promise<void> {
const report = await this.getCurrentDBEntry(instanceId);
customEvent.date = now();
customEvent.eventType = eventType;
report.metrics.customEvents.push(customEvent);
await this.update(report);
}
public async incrementCounter(instanceId: string, counterName: string): Promise<void> {
const report = await this.getCurrentDBEntry(instanceId);
if (!report.metrics.measures.hasOwnProperty(counterName)) {
report.metrics.measures[counterName] = 0;
}
report.metrics.measures[counterName]++;
await this.update(report);
}
public async addTiming(instanceId: string, eventType: string, durationInMilliseconds: number, metadata = {}): Promise<void> {
const report = await this.getCurrentDBEntry(instanceId);
report.metrics.timings.push({ eventType, durationInMilliseconds, metadata, date: now() });
await this.update(report);
}
/** Clears all values that exist in the database.
* returns nothing.
*/
public async clearData(date?: Date): Promise<void> {
if (!date) {
this.metrics = [];
} else {
const today = getYearMonthDay(date);
this.metrics = this.metrics.filter(x => x.date >= today);
}
}
public async getMetrics(beforeDate?: Date): Promise<IMetrics[]> {
if (beforeDate) {
const today = getYearMonthDay(beforeDate);
let metrics = this.metrics.filter(x => x.date < today).map(x => x.metrics);
return metrics;
} else {
return this.metrics.map(x => x.metrics);
}
}
public getCurrentMetrics(instanceId: string): Promise<IMetrics> {
return this.getCurrentDBEntry(instanceId).then(x => x.metrics);
}
private async getCurrentDBEntry(instanceId: string): Promise<DBEntry> {
let report = this.metrics.find(x => x.instanceId === instanceId);
if (!report) {
let newReport = this._createReport();
const today = getYearMonthDay(new Date(Date.now()));
report = { date: today, instanceId, metrics: newReport };
let metrics = this.metrics;
metrics.push(report);
this.metrics = metrics;
}
return report;
}
private update(report: DBEntry) {
let metrics = this.metrics;
for (let i = 0; i < metrics.length; i++) {
if (metrics[i].instanceId === report.instanceId) {
metrics[i] = report;
break;
}
}
this.metrics = metrics;
}
private get metrics() {
try {
return (this._context.globalState.get<DBEntry[]>(TELEMETRY_KEY) || []);
} catch { }
return [];
}
private set metrics(entries: DBEntry[]) {
this._context.globalState.update(TELEMETRY_KEY, entries);
}
}