Skip to content

Commit 0f9d127

Browse files
authored
Merge pull request #2513 from github/koesie10/show-telemetry-without-vscode
Make the `showAndLogExceptionWithTelemetry` function work without the `vscode` module
2 parents f9a415c + 7a76e20 commit 0f9d127

34 files changed

Lines changed: 237 additions & 115 deletions

File tree

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@ import { AppEventEmitter } from "./events";
44
import { NotificationLogger } from "./logging";
55
import { Memento } from "./memento";
66
import { AppCommandManager } from "./commands";
7+
import { AppTelemetry } from "./telemetry";
78

89
export interface App {
910
createEventEmitter<T>(): AppEventEmitter<T>;
1011
readonly mode: AppMode;
1112
readonly logger: NotificationLogger;
13+
readonly telemetry?: AppTelemetry;
1214
readonly subscriptions: Disposable[];
1315
readonly extensionPath: string;
1416
readonly globalStoragePath: string;

extensions/ql-vscode/src/common/logging/notifications.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import { NotificationLogger } from "./notification-logger";
2+
import { AppTelemetry } from "../telemetry";
3+
import { RedactableError } from "../../pure/errors";
24

35
export interface ShowAndLogOptions {
46
/**
@@ -87,3 +89,28 @@ async function internalShowAndLog(
8789
void logger.log(fullMessage || message);
8890
await fn.bind(logger)(message);
8991
}
92+
93+
interface ShowAndLogExceptionOptions extends ShowAndLogOptions {
94+
/** Custom properties to include in the telemetry report. */
95+
extraTelemetryProperties?: { [key: string]: string };
96+
}
97+
98+
/**
99+
* Show an error message, log it to the console, and emit redacted information as telemetry
100+
*
101+
* @param logger The logger that will receive the message.
102+
* @param telemetry The telemetry instance to use for reporting.
103+
* @param error The error to show. Only redacted information will be included in the telemetry.
104+
* @param options See individual fields on `ShowAndLogExceptionOptions` type.
105+
*
106+
* @return A promise that resolves to the selected item or undefined when being dismissed.
107+
*/
108+
export async function showAndLogExceptionWithTelemetry(
109+
logger: NotificationLogger,
110+
telemetry: AppTelemetry | undefined,
111+
error: RedactableError,
112+
options: ShowAndLogExceptionOptions = {},
113+
): Promise<void> {
114+
telemetry?.sendError(error, options.extraTelemetryProperties);
115+
return showAndLogErrorMessage(logger, error.fullMessage, options);
116+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { RedactableError } from "../pure/errors";
2+
3+
export interface AppTelemetry {
4+
sendCommandUsage(name: string, executionTime: number, error?: Error): void;
5+
sendUIInteraction(name: string): void;
6+
sendError(
7+
error: RedactableError,
8+
extraProperties?: { [key: string]: string },
9+
): void;
10+
}

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

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
extLogger,
55
NotificationLogger,
66
showAndLogWarningMessage,
7+
showAndLogExceptionWithTelemetry,
78
} from "../logging";
89
import {
910
asError,
@@ -12,18 +13,21 @@ import {
1213
} from "../../pure/helpers-pure";
1314
import { redactableError } from "../../pure/errors";
1415
import { UserCancellationException } from "./progress";
15-
import { telemetryListener } from "../../telemetry";
16-
import { showAndLogExceptionWithTelemetry } from "./logging";
16+
import { telemetryListener } from "./telemetry";
17+
import { AppTelemetry } from "../telemetry";
1718

1819
/**
1920
* Create a command manager for VSCode, wrapping registerCommandWithErrorHandling
2021
* and vscode.executeCommand.
2122
*/
2223
export function createVSCodeCommandManager<
2324
Commands extends Record<string, CommandFunction>,
24-
>(logger?: NotificationLogger): CommandManager<Commands> {
25+
>(
26+
logger?: NotificationLogger,
27+
telemetry?: AppTelemetry,
28+
): CommandManager<Commands> {
2529
return new CommandManager((commandId, task) => {
26-
return registerCommandWithErrorHandling(commandId, task, logger);
30+
return registerCommandWithErrorHandling(commandId, task, logger, telemetry);
2731
}, wrapExecuteCommand);
2832
}
2933

@@ -34,11 +38,13 @@ export function createVSCodeCommandManager<
3438
* @param task The task to run. It is passed directly to `commands.registerCommand`. Any
3539
* arguments to the command handler are passed on to the task.
3640
* @param logger The logger to use for error reporting.
41+
* @param telemetry The telemetry listener to use for error reporting.
3742
*/
3843
export function registerCommandWithErrorHandling(
3944
commandId: string,
4045
task: (...args: any[]) => Promise<any>,
4146
logger: NotificationLogger = extLogger,
47+
telemetry: AppTelemetry | undefined = telemetryListener,
4248
): Disposable {
4349
return commands.registerCommand(commandId, async (...args: any[]) => {
4450
const startTime = Date.now();
@@ -64,7 +70,7 @@ export function registerCommandWithErrorHandling(
6470
const fullMessage = errorStack
6571
? `${errorMessage.fullMessage}\n${errorStack}`
6672
: errorMessage.fullMessage;
67-
void showAndLogExceptionWithTelemetry(logger, errorMessage, {
73+
void showAndLogExceptionWithTelemetry(logger, telemetry, errorMessage, {
6874
fullMessage,
6975
extraTelemetryProperties: {
7076
command: commandId,

extensions/ql-vscode/src/common/vscode/external-files.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ import {
77
getErrorMessage,
88
getErrorStack,
99
} from "../../pure/helpers-pure";
10-
import { extLogger } from "../logging";
11-
import { showAndLogExceptionWithTelemetry } from "./logging";
10+
import { extLogger, showAndLogExceptionWithTelemetry } from "../logging";
11+
import { telemetryListener } from "./telemetry";
1212

1313
export async function tryOpenExternalFile(
1414
commandManager: AppCommandManager,
@@ -36,6 +36,7 @@ the file in the file explorer and dragging it into the workspace.`,
3636
} catch (e) {
3737
void showAndLogExceptionWithTelemetry(
3838
extLogger,
39+
telemetryListener,
3940
redactableError(
4041
asError(e),
4142
)`Failed to reveal file in OS: ${getErrorMessage(e)}`,
@@ -45,6 +46,7 @@ the file in the file explorer and dragging it into the workspace.`,
4546
} else {
4647
void showAndLogExceptionWithTelemetry(
4748
extLogger,
49+
telemetryListener,
4850
redactableError(asError(e))`Could not open file ${fileLocation}`,
4951
{
5052
fullMessage: `${getErrorMessage(e)}\n${getErrorStack(e)}`,

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

Lines changed: 0 additions & 30 deletions
This file was deleted.

extensions/ql-vscode/src/telemetry.ts renamed to extensions/ql-vscode/src/common/vscode/telemetry.ts

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,14 @@ import {
1313
LOG_TELEMETRY,
1414
isIntegrationTestMode,
1515
isCanary,
16-
} from "./config";
16+
} from "../../config";
1717
import * as appInsights from "applicationinsights";
18-
import { extLogger } from "./common";
19-
import { UserCancellationException } from "./common/vscode/progress";
20-
import { showBinaryChoiceWithUrlDialog } from "./common/vscode/dialog";
21-
import { RedactableError } from "./pure/errors";
18+
import { extLogger } from "../index";
19+
import { UserCancellationException } from "./progress";
20+
import { showBinaryChoiceWithUrlDialog } from "./dialog";
21+
import { RedactableError } from "../../pure/errors";
2222
import { SemVer } from "semver";
23+
import { AppTelemetry } from "../telemetry";
2324

2425
// Key is injected at build time through the APP_INSIGHTS_KEY environment variable.
2526
const key = "REPLACE-APP-INSIGHTS-KEY";
@@ -54,7 +55,10 @@ const baseDataPropertiesToRemove = [
5455

5556
const NOT_SET_CLI_VERSION = "not-set";
5657

57-
export class TelemetryListener extends ConfigListener {
58+
export class ExtensionTelemetryListener
59+
extends ConfigListener
60+
implements AppTelemetry
61+
{
5862
static relevantSettings = [ENABLE_TELEMETRY, CANARY_FEATURES];
5963

6064
private reporter?: TelemetryReporter;
@@ -152,7 +156,7 @@ export class TelemetryListener extends ConfigListener {
152156
void this.reporter?.dispose();
153157
}
154158

155-
sendCommandUsage(name: string, executionTime: number, error?: Error) {
159+
sendCommandUsage(name: string, executionTime: number, error?: Error): void {
156160
if (!this.reporter) {
157161
return;
158162
}
@@ -174,7 +178,7 @@ export class TelemetryListener extends ConfigListener {
174178
);
175179
}
176180

177-
sendUIInteraction(name: string) {
181+
sendUIInteraction(name: string): void {
178182
if (!this.reporter) {
179183
return;
180184
}
@@ -193,7 +197,7 @@ export class TelemetryListener extends ConfigListener {
193197
sendError(
194198
error: RedactableError,
195199
extraProperties?: { [key: string]: string },
196-
) {
200+
): void {
197201
if (!this.reporter) {
198202
return;
199203
}
@@ -272,16 +276,16 @@ export class TelemetryListener extends ConfigListener {
272276
/**
273277
* The global Telemetry instance
274278
*/
275-
export let telemetryListener: TelemetryListener | undefined;
279+
export let telemetryListener: ExtensionTelemetryListener | undefined;
276280

277281
export async function initializeTelemetry(
278282
extension: Extension<any>,
279283
ctx: ExtensionContext,
280-
): Promise<TelemetryListener> {
284+
): Promise<ExtensionTelemetryListener> {
281285
if (telemetryListener !== undefined) {
282286
throw new Error("Telemetry is already initialized");
283287
}
284-
telemetryListener = new TelemetryListener(
288+
telemetryListener = new ExtensionTelemetryListener(
285289
extension.id,
286290
extension.packageJSON.version,
287291
key,

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import { VSCodeAppEventEmitter } from "./events";
99
import { AppCommandManager, QueryServerCommandManager } from "../commands";
1010
import { createVSCodeCommandManager } from "./commands";
1111
import { AppEnvironmentContext } from "./environment-context";
12+
import { AppTelemetry } from "../telemetry";
13+
import { telemetryListener } from "./telemetry";
1214

1315
export class ExtensionApp implements App {
1416
public readonly credentials: VSCodeCredentials;
@@ -59,6 +61,10 @@ export class ExtensionApp implements App {
5961
return extLogger;
6062
}
6163

64+
public get telemetry(): AppTelemetry | undefined {
65+
return telemetryListener;
66+
}
67+
6268
public createEventEmitter<T>(): AppEventEmitter<T> {
6369
return new VSCodeAppEventEmitter<T>();
6470
}

extensions/ql-vscode/src/compare/compare-view.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
QueryCompareResult,
77
} from "../pure/interface-types";
88
import { extLogger, Logger } from "../common";
9-
import { showAndLogExceptionWithTelemetry } from "../common/vscode/logging";
9+
import { showAndLogExceptionWithTelemetry } from "../common/logging";
1010
import { CodeQLCliServer } from "../codeql-cli/cli";
1111
import { DatabaseManager } from "../databases/local-databases";
1212
import { jumpToLocation } from "../databases/local-databases/locations";
@@ -23,7 +23,7 @@ import {
2323
AbstractWebview,
2424
WebviewPanelConfig,
2525
} from "../common/vscode/abstract-webview";
26-
import { telemetryListener } from "../telemetry";
26+
import { telemetryListener } from "../common/vscode/telemetry";
2727
import { redactableError } from "../pure/errors";
2828

2929
interface ComparePair {
@@ -152,6 +152,7 @@ export class CompareView extends AbstractWebview<
152152
case "unhandledError":
153153
void showAndLogExceptionWithTelemetry(
154154
extLogger,
155+
telemetryListener,
155156
redactableError(
156157
msg.error,
157158
)`Unhandled error in result comparison view: ${msg.error.message}`,

extensions/ql-vscode/src/data-extensions-editor/data-extensions-editor-view.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@ import {
1717
} from "../pure/interface-types";
1818
import { ProgressUpdate } from "../common/vscode/progress";
1919
import { QueryRunner } from "../query-server";
20-
import { showAndLogExceptionWithTelemetry } from "../common/vscode/logging";
20+
import {
21+
showAndLogExceptionWithTelemetry,
22+
showAndLogErrorMessage,
23+
} from "../common/logging";
2124
import { outputFile, pathExists, readFile } from "fs-extra";
2225
import { load as loadYaml } from "js-yaml";
2326
import { DatabaseItem, DatabaseManager } from "../databases/local-databases";
@@ -42,7 +45,6 @@ import {
4245
} from "./auto-model";
4346
import { showLlmGeneration } from "../config";
4447
import { getAutoModelUsages } from "./auto-model-usages-query";
45-
import { showAndLogErrorMessage } from "../common/logging";
4648

4749
export class DataExtensionsEditorView extends AbstractWebview<
4850
ToDataExtensionsEditorMessage,
@@ -276,6 +278,7 @@ export class DataExtensionsEditorView extends AbstractWebview<
276278
} catch (err) {
277279
void showAndLogExceptionWithTelemetry(
278280
this.app.logger,
281+
this.app.telemetry,
279282
redactableError(
280283
asError(err),
281284
)`Failed to load external API usages: ${getErrorMessage(err)}`,
@@ -342,6 +345,7 @@ export class DataExtensionsEditorView extends AbstractWebview<
342345
} catch (e: unknown) {
343346
void showAndLogExceptionWithTelemetry(
344347
this.app.logger,
348+
this.app.telemetry,
345349
redactableError(
346350
asError(e),
347351
)`Failed to generate flow model: ${getErrorMessage(e)}`,
@@ -476,6 +480,7 @@ export class DataExtensionsEditorView extends AbstractWebview<
476480
if (e instanceof RequestError && e.status === 429) {
477481
void showAndLogExceptionWithTelemetry(
478482
this.app.logger,
483+
this.app.telemetry,
479484
redactableError(e)`Rate limit hit, please try again soon.`,
480485
);
481486
return null;

0 commit comments

Comments
 (0)