Skip to content

Commit ff95caf

Browse files
authored
Merge pull request #3853 from github/koesie10/remove-telemetry-setting
Remove extension telemetry setting and use VS Code setting instead
2 parents da108fb + de9fd27 commit ff95caf

File tree

10 files changed

+81
-566
lines changed

10 files changed

+81
-566
lines changed

extensions/ql-vscode/CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22

33
## [UNRELEASED]
44

5-
- Add a palette command that allows importing all databases directly inside of a parent folder. [3797](https://github.com/github/vscode-codeql/pull/3797)
5+
- Add a palette command that allows importing all databases directly inside of a parent folder. [#3797](https://github.com/github/vscode-codeql/pull/3797)
6+
- Only use VS Code telemetry settings instead of using `codeQL.telemetry.enableTelemetry` [#3853](https://github.com/github/vscode-codeql/pull/3853)
67

78
## 1.16.1 - 6 November 2024
89

extensions/ql-vscode/package.json

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -497,16 +497,6 @@
497497
"title": "Telemetry",
498498
"order": 11,
499499
"properties": {
500-
"codeQL.telemetry.enableTelemetry": {
501-
"type": "boolean",
502-
"default": false,
503-
"scope": "application",
504-
"markdownDescription": "Specifies whether to send CodeQL usage telemetry. This setting AND the one of the global telemetry settings (`#telemetry.enableTelemetry#` or `#telemetry.telemetryLevel#`) must be enabled for telemetry to be sent to GitHub. For more information, see the [telemetry documentation](https://codeql.github.com/docs/codeql-for-visual-studio-code/about-telemetry-in-codeql-for-visual-studio-code)",
505-
"tags": [
506-
"telemetry",
507-
"usesOnlineServices"
508-
]
509-
},
510500
"codeQL.telemetry.logTelemetry": {
511501
"type": "boolean",
512502
"default": false,

extensions/ql-vscode/src/common/vscode/dialog.ts

Lines changed: 1 addition & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { env, Uri, window } from "vscode";
1+
import { window } from "vscode";
22

33
/**
44
* Opens a modal dialog for the user to make a yes/no choice.
@@ -34,50 +34,6 @@ export async function showBinaryChoiceDialog(
3434
return chosenItem?.title === yesItem.title;
3535
}
3636

37-
/**
38-
* Opens a modal dialog for the user to make a yes/no choice.
39-
*
40-
* @param message The message to show.
41-
* @param modal If true (the default), show a modal dialog box, otherwise dialog is non-modal and can
42-
* be closed even if the user does not make a choice.
43-
*
44-
* @return
45-
* `true` if the user clicks 'Yes',
46-
* `false` if the user clicks 'No' or cancels the dialog,
47-
* `undefined` if the dialog is closed without the user making a choice.
48-
*/
49-
export async function showBinaryChoiceWithUrlDialog(
50-
message: string,
51-
url: string,
52-
): Promise<boolean | undefined> {
53-
const urlItem = { title: "More Information", isCloseAffordance: false };
54-
const yesItem = { title: "Yes", isCloseAffordance: false };
55-
const noItem = { title: "No", isCloseAffordance: true };
56-
let chosenItem;
57-
58-
// Keep the dialog open as long as the user is clicking the 'more information' option.
59-
// To prevent an infinite loop, if the user clicks 'more information' 5 times, close the dialog and return cancelled
60-
let count = 0;
61-
do {
62-
chosenItem = await window.showInformationMessage(
63-
message,
64-
{ modal: true },
65-
urlItem,
66-
yesItem,
67-
noItem,
68-
);
69-
if (chosenItem === urlItem) {
70-
await env.openExternal(Uri.parse(url, true));
71-
}
72-
count++;
73-
} while (chosenItem === urlItem && count < 5);
74-
75-
if (!chosenItem || chosenItem.title === urlItem.title) {
76-
return undefined;
77-
}
78-
return chosenItem.title === yesItem.title;
79-
}
80-
8137
/**
8238
* Show an information message with a customisable action.
8339
* @param message The message to show.
Lines changed: 71 additions & 147 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,15 @@
1-
import type {
2-
Extension,
3-
ExtensionContext,
4-
ConfigurationChangeEvent,
5-
} from "vscode";
6-
import { ConfigurationTarget, env } from "vscode";
1+
import type { Extension, ExtensionContext } from "vscode";
2+
import { ConfigurationTarget, env, Uri, window } from "vscode";
73
import TelemetryReporter from "vscode-extension-telemetry";
8-
import {
9-
ConfigListener,
10-
CANARY_FEATURES,
11-
ENABLE_TELEMETRY,
12-
LOG_TELEMETRY,
13-
isIntegrationTestMode,
14-
isCanary,
15-
} from "../../config";
4+
import { ENABLE_TELEMETRY, isCanary, LOG_TELEMETRY } from "../../config";
165
import type { TelemetryClient } from "applicationinsights";
176
import { extLogger } from "../logging/vscode";
187
import { UserCancellationException } from "./progress";
19-
import { showBinaryChoiceWithUrlDialog } from "./dialog";
208
import type { RedactableError } from "../errors";
219
import type { SemVer } from "semver";
2210
import type { AppTelemetry } from "../telemetry";
2311
import type { EnvelopeTelemetry } from "applicationinsights/out/Declarations/Contracts";
12+
import type { Disposable } from "../disposable-object";
2413

2514
// Key is injected at build time through the APP_INSIGHTS_KEY environment variable.
2615
const key = "REPLACE-APP-INSIGHTS-KEY";
@@ -55,80 +44,25 @@ const baseDataPropertiesToRemove = [
5544

5645
const NOT_SET_CLI_VERSION = "not-set";
5746

58-
export class ExtensionTelemetryListener
59-
extends ConfigListener
60-
implements AppTelemetry
61-
{
62-
private reporter?: TelemetryReporter;
47+
export class ExtensionTelemetryListener implements AppTelemetry, Disposable {
48+
private readonly reporter: TelemetryReporter;
6349

6450
private cliVersionStr = NOT_SET_CLI_VERSION;
6551

66-
constructor(
67-
private readonly id: string,
68-
private readonly version: string,
69-
private readonly key: string,
70-
private readonly ctx: ExtensionContext,
71-
) {
72-
super();
73-
74-
env.onDidChangeTelemetryEnabled(async () => {
75-
await this.initialize();
76-
});
77-
}
78-
79-
/**
80-
* This function handles changes to relevant configuration elements. There are 2 configuration
81-
* ids that this function cares about:
82-
*
83-
* * `codeQL.telemetry.enableTelemetry`: If this one has changed, then we need to re-initialize
84-
* the reporter and the reporter may wind up being removed.
85-
* * `codeQL.canary`: A change here could possibly re-trigger a dialog popup.
86-
*
87-
* Note that the global telemetry setting also gate-keeps whether or not to send telemetry events
88-
* to Application Insights. However, this gatekeeping happens inside of the vscode-extension-telemetry
89-
* package. So, this does not need to be handled here.
90-
*
91-
* @param e the configuration change event
92-
*/
93-
async handleDidChangeConfiguration(
94-
e: ConfigurationChangeEvent,
95-
): Promise<void> {
96-
if (e.affectsConfiguration(ENABLE_TELEMETRY.qualifiedName)) {
97-
await this.initialize();
98-
}
99-
100-
// Re-request telemetry so that users can see the dialog again.
101-
// Re-request if codeQL.canary is being set to `true` and telemetry
102-
// is not currently enabled.
103-
if (
104-
e.affectsConfiguration(CANARY_FEATURES.qualifiedName) &&
105-
CANARY_FEATURES.getValue() &&
106-
!ENABLE_TELEMETRY.getValue()
107-
) {
108-
await this.setTelemetryRequested(false);
109-
await this.requestTelemetryPermission();
110-
}
111-
}
112-
113-
async initialize() {
114-
await this.requestTelemetryPermission();
115-
116-
this.disposeReporter();
117-
118-
if (ENABLE_TELEMETRY.getValue<boolean>()) {
119-
this.createReporter();
120-
}
121-
}
122-
123-
private createReporter() {
52+
constructor(id: string, version: string, key: string) {
53+
// We can always initialize this and send events using it because the vscode-extension-telemetry will check
54+
// whether the `telemetry.telemetryLevel` setting is enabled.
12455
this.reporter = new TelemetryReporter(
125-
this.id,
126-
this.version,
127-
this.key,
56+
id,
57+
version,
58+
key,
12859
/* anonymize stack traces */ true,
12960
);
130-
this.push(this.reporter);
13161

62+
this.addTelemetryProcessor();
63+
}
64+
65+
private addTelemetryProcessor() {
13266
// The appInsightsClient field is private but we want to access it anyway
13367
const client = this.reporter["appInsightsClient"] as TelemetryClient;
13468
if (client) {
@@ -151,14 +85,10 @@ export class ExtensionTelemetryListener
15185
}
15286

15387
dispose() {
154-
super.dispose();
155-
void this.reporter?.dispose();
88+
void this.reporter.dispose();
15689
}
15790

15891
sendCommandUsage(name: string, executionTime: number, error?: Error): void {
159-
if (!this.reporter) {
160-
return;
161-
}
16292
const status = !error
16393
? CommandCompletion.Success
16494
: error instanceof UserCancellationException
@@ -178,10 +108,6 @@ export class ExtensionTelemetryListener
178108
}
179109

180110
sendUIInteraction(name: string): void {
181-
if (!this.reporter) {
182-
return;
183-
}
184-
185111
this.reporter.sendTelemetryEvent(
186112
"ui-interaction",
187113
{
@@ -197,10 +123,6 @@ export class ExtensionTelemetryListener
197123
error: RedactableError,
198124
extraProperties?: { [key: string]: string },
199125
): void {
200-
if (!this.reporter) {
201-
return;
202-
}
203-
204126
const properties: { [key: string]: string } = {
205127
isCanary: isCanary().toString(),
206128
cliVersion: this.cliVersionStr,
@@ -215,10 +137,6 @@ export class ExtensionTelemetryListener
215137
}
216138

217139
sendConfigInformation(config: Record<string, string>): void {
218-
if (!this.reporter) {
219-
return;
220-
}
221-
222140
this.reporter.sendTelemetryEvent(
223141
"config",
224142
{
@@ -230,37 +148,6 @@ export class ExtensionTelemetryListener
230148
);
231149
}
232150

233-
/**
234-
* Displays a popup asking the user if they want to enable telemetry
235-
* for this extension.
236-
*/
237-
async requestTelemetryPermission() {
238-
if (!this.wasTelemetryRequested()) {
239-
// if global telemetry is disabled, avoid showing the dialog or making any changes
240-
let result = undefined;
241-
if (
242-
env.isTelemetryEnabled &&
243-
// Avoid showing the dialog if we are in integration test mode.
244-
!isIntegrationTestMode()
245-
) {
246-
// Extension won't start until this completes.
247-
result = await showBinaryChoiceWithUrlDialog(
248-
"Does the CodeQL Extension by GitHub have your permission to collect usage data and metrics to help us improve CodeQL for VSCode?",
249-
"https://codeql.github.com/docs/codeql-for-visual-studio-code/about-telemetry-in-codeql-for-visual-studio-code",
250-
);
251-
}
252-
if (result !== undefined) {
253-
await Promise.all([
254-
this.setTelemetryRequested(true),
255-
ENABLE_TELEMETRY.updateValue<boolean>(
256-
result,
257-
ConfigurationTarget.Global,
258-
),
259-
]);
260-
}
261-
}
262-
}
263-
264151
/**
265152
* Exposed for testing
266153
*/
@@ -271,21 +158,45 @@ export class ExtensionTelemetryListener
271158
set cliVersion(version: SemVer | undefined) {
272159
this.cliVersionStr = version ? version.toString() : NOT_SET_CLI_VERSION;
273160
}
161+
}
274162

275-
private disposeReporter() {
276-
if (this.reporter) {
277-
void this.reporter.dispose();
278-
this.reporter = undefined;
163+
async function notifyTelemetryChange() {
164+
const continueItem = { title: "Continue", isCloseAffordance: false };
165+
const vsCodeTelemetryItem = {
166+
title: "More Information about VS Code Telemetry",
167+
isCloseAffordance: false,
168+
};
169+
const codeqlTelemetryItem = {
170+
title: "More Information about CodeQL Telemetry",
171+
isCloseAffordance: false,
172+
};
173+
let chosenItem;
174+
175+
do {
176+
chosenItem = await window.showInformationMessage(
177+
"The CodeQL extension now follows VS Code's telemetry settings. VS Code telemetry is currently enabled. Learn how to update your telemetry settings by clicking the links below.",
178+
{ modal: true },
179+
continueItem,
180+
vsCodeTelemetryItem,
181+
codeqlTelemetryItem,
182+
);
183+
if (chosenItem === vsCodeTelemetryItem) {
184+
await env.openExternal(
185+
Uri.parse(
186+
"https://code.visualstudio.com/docs/getstarted/telemetry",
187+
true,
188+
),
189+
);
279190
}
280-
}
281-
282-
private wasTelemetryRequested(): boolean {
283-
return !!this.ctx.globalState.get<boolean>("telemetry-request-viewed");
284-
}
285-
286-
private async setTelemetryRequested(newValue: boolean): Promise<void> {
287-
await this.ctx.globalState.update("telemetry-request-viewed", newValue);
288-
}
191+
if (chosenItem === codeqlTelemetryItem) {
192+
await env.openExternal(
193+
Uri.parse(
194+
"https://docs.github.com/en/code-security/codeql-for-vs-code/using-the-advanced-functionality-of-the-codeql-for-vs-code-extension/telemetry-in-codeql-for-visual-studio-code",
195+
true,
196+
),
197+
);
198+
}
199+
} while (chosenItem !== continueItem);
289200
}
290201

291202
/**
@@ -301,15 +212,28 @@ export async function initializeTelemetry(
301212
if (telemetryListener !== undefined) {
302213
throw new Error("Telemetry is already initialized");
303214
}
215+
216+
if (ENABLE_TELEMETRY.getValue<boolean | undefined>() === false) {
217+
if (env.isTelemetryEnabled) {
218+
// Await this so that the user is notified before any telemetry is sent
219+
await notifyTelemetryChange();
220+
}
221+
222+
// Remove the deprecated telemetry setting
223+
ENABLE_TELEMETRY.updateValue(undefined, ConfigurationTarget.Global);
224+
ENABLE_TELEMETRY.updateValue(undefined, ConfigurationTarget.Workspace);
225+
ENABLE_TELEMETRY.updateValue(
226+
undefined,
227+
ConfigurationTarget.WorkspaceFolder,
228+
);
229+
}
230+
304231
telemetryListener = new ExtensionTelemetryListener(
305232
extension.id,
306233
extension.packageJSON.version,
307234
key,
308-
ctx,
309235
);
310-
// do not await initialization, since doing so will sometimes cause a modal popup.
311-
// this is a particular problem during integration tests, which will hang if a modal popup is displayed.
312-
void telemetryListener.initialize();
313236
ctx.subscriptions.push(telemetryListener);
237+
314238
return telemetryListener;
315239
}

extensions/ql-vscode/src/config.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,8 @@ const ROOT_SETTING = new Setting("codeQL");
165165
const TELEMETRY_SETTING = new Setting("telemetry", ROOT_SETTING);
166166

167167
export const LOG_TELEMETRY = new Setting("logTelemetry", TELEMETRY_SETTING);
168+
169+
// Legacy setting that is no longer used, but is used for showing a message when the user upgrades.
168170
export const ENABLE_TELEMETRY = new Setting(
169171
"enableTelemetry",
170172
TELEMETRY_SETTING,

0 commit comments

Comments
 (0)