Skip to content

Commit 5496123

Browse files
committed
extension/src: remove sampling and machine ID from telemetry service
- CL 625495 increased the telemetry prompting to 100%, sampling threshold and machine ID is no longer needed. (Previuosly, it's determined by hash<machineID> % 1000 > threshold). Clean up test that relevant to sampling threshold and machine ID. - Remove TODO to prompt telemetry regardless of vscode env isTelemetryEnabled. See https://code.visualstudio.com/api/extension-guides/telemetry#without-the-telemetry-module - VSCode-Go delegates most of telemetry logic to gopls even including the telemetry opt in prompting. VSCode-Go send a ExecuteCommandRequest to gopls and gopls returns a ShowMessageRequest back to client for prompting. It's the language server (gopls)'s responsibility to memorize the user's selection after prompting and avoid re-sending the same prompt next time. For #3697 Change-Id: I89cd0324354ed00192dc8d696996d23ba0946861 Reviewed-on: https://go-review.googlesource.com/c/vscode-go/+/660396 Reviewed-by: Madeline Kalil <[email protected]> kokoro-CI: kokoro <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent e8cc021 commit 5496123

File tree

5 files changed

+44
-99
lines changed

5 files changed

+44
-99
lines changed

extension/src/goTelemetry.ts

Lines changed: 14 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,13 @@ export class TelemetryReporter implements vscode.Disposable {
7979
private _counters: { [key: string]: number } = {};
8080
private _flushTimer: NodeJS.Timeout | undefined;
8181
private _tool = '';
82+
83+
/**
84+
* @param flushIntervalMs is the interval (in milliseconds) between periodic
85+
* `flush()` calls.
86+
* @param counterFile is the file path for writing telemetry data (used for
87+
* testing).
88+
*/
8289
constructor(flushIntervalMs = 60_000, private counterFile: string = '') {
8390
if (flushIntervalMs > 0) {
8491
// Periodically call flush.
@@ -203,32 +210,27 @@ export class TelemetryService {
203210
constructor(
204211
private languageClient: Pick<LanguageClient, 'sendRequest'> | undefined,
205212
private globalState: vscode.Memento,
206-
commands: string[] = []
213+
serverCommands: string[] = []
207214
) {
208-
if (!languageClient || !commands.includes(GOPLS_MAYBE_PROMPT_FOR_TELEMETRY)) {
209-
// we are not backed by the gopls version that supports telemetry.
215+
if (!languageClient || !serverCommands.includes(GOPLS_MAYBE_PROMPT_FOR_TELEMETRY)) {
216+
// We are not backed by the gopls version that supports telemetry.
210217
return;
211218
}
212219

213220
this.active = true;
214-
// record the first time we see the gopls with telemetry support.
221+
// Record the first time we see the gopls with telemetry support.
215222
// The timestamp will be used to avoid prompting too early.
216223
const telemetryStartTime = readTelemetryStartTime(globalState);
217224
if (!telemetryStartTime) {
218225
recordTelemetryStartTime(globalState, new Date());
219226
}
220227
}
221228

222-
async promptForTelemetry(
223-
isPreviewExtension: boolean,
224-
isVSCodeTelemetryEnabled: boolean = vscode.env.isTelemetryEnabled,
225-
samplingInterval = 1000 /* prompt N out of 1000. 1000 = 100% */
226-
) {
229+
async promptForTelemetry(isVSCodeTelemetryEnabled: boolean = vscode.env.isTelemetryEnabled) {
227230
if (!this.active) return;
228231

229-
// Do not prompt yet if the user disabled vscode's telemetry.
230-
// TODO(hyangah): remove this condition after we roll out to 100%. It's possible
231-
// users who don't want vscode's telemetry are still willing to opt in.
232+
// Do not prompt if the user disabled vscode's telemetry.
233+
// See https://code.visualstudio.com/api/extension-guides/telemetry#without-the-telemetry-module
232234
if (!isVSCodeTelemetryEnabled) return;
233235

234236
// Allow at least 7days for gopls to collect some data.
@@ -240,11 +242,6 @@ export class TelemetryService {
240242
return;
241243
}
242244

243-
// For official extension users, prompt only N out of 1000.
244-
if (!isPreviewExtension && this.hashMachineID() % 1000 >= samplingInterval) {
245-
return;
246-
}
247-
248245
try {
249246
await this.languageClient?.sendRequest(ExecuteCommandRequest.type, {
250247
command: GOPLS_MAYBE_PROMPT_FOR_TELEMETRY
@@ -253,11 +250,6 @@ export class TelemetryService {
253250
console.log(`failed to send telemetry request: ${e}`);
254251
}
255252
}
256-
257-
// exported for testing.
258-
public hashMachineID(salt?: string): number {
259-
return hashMachineID(salt);
260-
}
261253
}
262254

263255
/**

extension/src/language/goLanguageServer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1702,7 +1702,7 @@ export function maybePromptForTelemetry(goCtx: GoExtensionContext) {
17021702
setTimeout(callback, 5 * timeMinute - Math.max(idleTime, 0));
17031703
return;
17041704
}
1705-
goCtx.telemetryService?.promptForTelemetry(extensionInfo.isPreview);
1705+
goCtx.telemetryService?.promptForTelemetry();
17061706
};
17071707
callback();
17081708
}

extension/test/gopls/extension.test.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -211,11 +211,7 @@ suite('Go Extension Tests With Gopls', function () {
211211
await Promise.all([
212212
// we want to see the prompt command flowing.
213213
env.onMessageInTrace(GOPLS_MAYBE_PROMPT_FOR_TELEMETRY, 60_000),
214-
sut.promptForTelemetry(
215-
false /* not a preview */,
216-
true /* vscode telemetry not disabled */,
217-
1000 /* 1000 out of 1000 users */
218-
)
214+
sut.promptForTelemetry(true /* vscode telemetry not disabled */)
219215
]);
220216
} catch (e) {
221217
assert(false, `unexpected failure: ${e}`);

extension/test/gopls/telemetry.test.ts

Lines changed: 8 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,7 @@ describe('# prompt for telemetry', async () => {
3333
testTelemetryPrompt(
3434
{
3535
noLangClient: true,
36-
previewExtension: true,
37-
samplingInterval: 1000
36+
previewExtension: true
3837
},
3938
false
4039
)
@@ -44,8 +43,7 @@ describe('# prompt for telemetry', async () => {
4443
testTelemetryPrompt(
4544
{
4645
goplsWithoutTelemetry: true,
47-
previewExtension: true,
48-
samplingInterval: 1000
46+
previewExtension: true
4947
},
5048
false
5149
)
@@ -54,8 +52,7 @@ describe('# prompt for telemetry', async () => {
5452
'prompt when telemetry started a while ago',
5553
testTelemetryPrompt(
5654
{
57-
firstDate: new Date('2022-01-01'),
58-
samplingInterval: 1000
55+
firstDate: new Date('2022-01-01')
5956
},
6057
true
6158
)
@@ -64,8 +61,7 @@ describe('# prompt for telemetry', async () => {
6461
'do not prompt if telemetry started two days ago',
6562
testTelemetryPrompt(
6663
{
67-
firstDate: new Date(Date.now() - 2 * 24 * 60 * 60 * 1000), // two days ago!
68-
samplingInterval: 1000
64+
firstDate: new Date(Date.now() - 2 * 24 * 60 * 60 * 1000) // two days ago!
6965
},
7066
false
7167
)
@@ -74,40 +70,7 @@ describe('# prompt for telemetry', async () => {
7470
'do not prompt if gopls with telemetry never ran',
7571
testTelemetryPrompt(
7672
{
77-
firstDate: undefined, // gopls with telemetry not seen before.
78-
samplingInterval: 1000
79-
},
80-
false
81-
)
82-
);
83-
it(
84-
'do not prompt if not sampled',
85-
testTelemetryPrompt(
86-
{
87-
firstDate: new Date('2022-01-01'),
88-
samplingInterval: 0
89-
},
90-
false
91-
)
92-
);
93-
it(
94-
'prompt only if sampled (machineID = 0, samplingInterval = 1)',
95-
testTelemetryPrompt(
96-
{
97-
firstDate: new Date('2022-01-01'),
98-
samplingInterval: 1,
99-
hashMachineID: 0
100-
},
101-
true
102-
)
103-
);
104-
it(
105-
'prompt only if sampled (machineID = 1, samplingInterval = 1)',
106-
testTelemetryPrompt(
107-
{
108-
firstDate: new Date('2022-01-01'),
109-
samplingInterval: 1,
110-
hashMachineID: 1
73+
firstDate: undefined // gopls with telemetry not seen before.
11174
},
11275
false
11376
)
@@ -117,8 +80,7 @@ describe('# prompt for telemetry', async () => {
11780
testTelemetryPrompt(
11881
{
11982
firstDate: new Date('2022-01-01'),
120-
previewExtension: true,
121-
samplingInterval: 0
83+
previewExtension: true
12284
},
12385
true
12486
)
@@ -129,8 +91,7 @@ describe('# prompt for telemetry', async () => {
12991
{
13092
firstDate: new Date('2022-01-01'),
13193
vsTelemetryDisabled: true,
132-
previewExtension: true,
133-
samplingInterval: 1000
94+
previewExtension: true
13495
},
13596
false
13697
)
@@ -145,7 +106,6 @@ describe('# prompt for telemetry', async () => {
145106
testExtensionAPI.globalState.update(TELEMETRY_START_TIME_KEY, new Date(Date.now() - 7 * 24 * 60 * 60 * 1000));
146107
await testTelemetryPrompt(
147108
{
148-
samplingInterval: 1000,
149109
mementoInstance: testExtensionAPI.globalState
150110
},
151111
true
@@ -159,8 +119,6 @@ interface testCase {
159119
firstDate?: Date; // first date the extension observed gopls with telemetry feature.
160120
previewExtension?: boolean; // assume we are in dev/nightly extension.
161121
vsTelemetryDisabled?: boolean; // assume the user disabled vscode general telemetry.
162-
samplingInterval: number; // N where N out of 1000 are sampled.
163-
hashMachineID?: number; // stub the machine id hash computation function.
164122
mementoInstance?: Memento; // if set, use this instead of mock memento.
165123
}
166124

@@ -181,10 +139,7 @@ function testTelemetryPrompt(tc: testCase, wantPrompt: boolean) {
181139
const commands = tc.goplsWithoutTelemetry ? [] : [GOPLS_MAYBE_PROMPT_FOR_TELEMETRY];
182140

183141
const sut = new TelemetryService(lc, memento, commands);
184-
if (tc.hashMachineID !== undefined) {
185-
sinon.stub(sut, 'hashMachineID').returns(tc.hashMachineID);
186-
}
187-
await sut.promptForTelemetry(!!tc.previewExtension, !tc.vsTelemetryDisabled, tc.samplingInterval);
142+
await sut.promptForTelemetry(!tc.vsTelemetryDisabled);
188143
if (wantPrompt) {
189144
sinon.assert.calledOnce(spy);
190145
} else {

internal/vscgo/main.go

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -164,33 +164,32 @@ func help(name string) {
164164
}
165165

166166
// runIncCounters increments telemetry counters read from stdin.
167-
func runIncCounters(_ []string) error {
167+
// Write the counters to file provided by env var TELEMETRY_COUNTER_FILE.
168+
func runIncCounters(_ []string) (rerr error) {
168169
scanner := bufio.NewScanner(os.Stdin)
169-
if counterFile := os.Getenv("TELEMETRY_COUNTER_FILE"); counterFile != "" {
170-
return printCounter(counterFile, scanner)
171-
}
172-
return runIncCountersImpl(scanner, counter.Add)
173-
}
174170

175-
func printCounter(fname string, scanner *bufio.Scanner) (rerr error) {
176-
f, err := os.OpenFile(fname, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
177-
if err != nil {
178-
return err
179-
}
180-
defer func() {
181-
if err := f.Close(); rerr == nil {
182-
rerr = err
171+
if counterFile := os.Getenv("TELEMETRY_COUNTER_FILE"); counterFile != "" {
172+
f, err := os.OpenFile(counterFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
173+
if err != nil {
174+
return err
183175
}
184-
}()
185-
return runIncCountersImpl(scanner, func(name string, count int64) {
186-
fmt.Fprintln(f, name, count)
187-
})
176+
defer func() {
177+
if err := f.Close(); err == nil {
178+
rerr = err
179+
}
180+
}()
181+
return runIncCountersImpl(scanner, func(name string, count int64) { fmt.Fprintln(f, name, count) })
182+
} else {
183+
return runIncCountersImpl(scanner, counter.Add)
184+
}
188185
}
189186

190187
const (
191188
incCountersBadInput = "inc_counters_bad_input"
192189
)
193190

191+
// incCountersInputLength returns the counter name based on input counters
192+
// length.
194193
func incCountersInputLength(n int) string {
195194
const name = "inc_counters_num_input"
196195
for i := 1; i < 8; i *= 2 {
@@ -201,6 +200,7 @@ func incCountersInputLength(n int) string {
201200
return name + ":>=8"
202201
}
203202

203+
// incCountersDuration returns the counter name based on input duration.
204204
func incCountersDuration(duration time.Duration) string {
205205
const name = "inc_counters_duration"
206206
switch {
@@ -233,7 +233,9 @@ func runIncCountersImpl(scanner *bufio.Scanner, incCounter func(name string, cou
233233
linenum++
234234
incCounter(name, int64(count))
235235
}
236+
// Keep track of counter line number.
236237
incCounter(incCountersInputLength(linenum), 1)
238+
// Keep track of time consumed for each round of counter increment.
237239
incCounter(incCountersDuration(time.Since(start)), 1)
238240
return nil
239241
}

0 commit comments

Comments
 (0)