Introduce showAndLogExceptionWithTelemetry

This commit is contained in:
Robert
2023-01-26 15:35:11 +00:00
parent 07f5d2b20d
commit db0d41a305
22 changed files with 338 additions and 111 deletions

View File

@@ -25,7 +25,8 @@ import {
} from "./pure/bqrs-utils";
import { commandRunner } from "./commandRunner";
import { DisposableObject } from "./pure/disposable-object";
import { showAndLogErrorMessage } from "./helpers";
import { showAndLogExceptionWithTelemetry } from "./helpers";
import { asError } from "./pure/helpers-pure";
export interface AstItem {
id: BqrsId;
@@ -146,7 +147,8 @@ export class AstViewer extends DisposableObject {
() => {
/**/
},
(err) => showAndLogErrorMessage(err),
(error: unknown) =>
showAndLogExceptionWithTelemetry(asError(error), "AST_viewer_reveal"),
);
}
@@ -204,7 +206,11 @@ export class AstViewer extends DisposableObject {
() => {
/**/
},
(err) => showAndLogErrorMessage(err),
(error: unknown) =>
showAndLogExceptionWithTelemetry(
asError(error),
"AST_viewer_reveal",
),
);
}
}

View File

@@ -6,7 +6,10 @@ import {
Disposable,
ProgressLocation,
} from "vscode";
import { showAndLogErrorMessage, showAndLogWarningMessage } from "./helpers";
import {
showAndLogExceptionWithTelemetry,
showAndLogWarningMessage,
} from "./helpers";
import { extLogger } from "./common";
import { asError, getErrorMessage, getErrorStack } from "./pure/helpers-pure";
import { telemetryListener } from "./telemetry";
@@ -125,23 +128,27 @@ export function commandRunner(
try {
return await task(...args);
} catch (e) {
const errorMessage = `${getErrorMessage(e) || e} (${commandId})`;
const notificationMessage = `${getErrorMessage(e) || e} (${commandId})`;
error = asError(e);
const errorStack = getErrorStack(e);
if (e instanceof UserCancellationException) {
// User has cancelled this action manually
if (e.silent) {
void extLogger.log(errorMessage);
void extLogger.log(notificationMessage);
} else {
void showAndLogWarningMessage(errorMessage);
void showAndLogWarningMessage(notificationMessage);
}
} else {
// Include the full stack in the error log only.
const fullMessage = errorStack
? `${errorMessage}\n${errorStack}`
: errorMessage;
void showAndLogErrorMessage(errorMessage, {
? `${notificationMessage}\n${errorStack}`
: notificationMessage;
void showAndLogExceptionWithTelemetry(error, "command_failed", {
notificationMessage,
fullMessage,
extraTelemetryProperties: {
command: commandId,
},
});
}
return undefined;
@@ -178,24 +185,28 @@ export function commandRunnerWithProgress<R>(
try {
return await withProgress(progressOptionsWithDefaults, task, ...args);
} catch (e) {
const errorMessage = `${getErrorMessage(e) || e} (${commandId})`;
const notificationMessage = `${getErrorMessage(e) || e} (${commandId})`;
error = asError(e);
const errorStack = getErrorStack(e);
if (e instanceof UserCancellationException) {
// User has cancelled this action manually
if (e.silent) {
void outputLogger.log(errorMessage);
void outputLogger.log(notificationMessage);
} else {
void showAndLogWarningMessage(errorMessage, { outputLogger });
void showAndLogWarningMessage(notificationMessage, { outputLogger });
}
} else {
// Include the full stack in the error log only.
const fullMessage = errorStack
? `${errorMessage}\n${errorStack}`
: errorMessage;
void showAndLogErrorMessage(errorMessage, {
? `${notificationMessage}\n${errorStack}`
: notificationMessage;
void showAndLogExceptionWithTelemetry(error, "command_failed", {
outputLogger,
notificationMessage,
fullMessage,
extraTelemetryProperties: {
command: commandId,
},
});
}
return undefined;

View File

@@ -7,8 +7,8 @@ import {
getPrimaryDbscheme,
getQlPackForDbscheme,
getOnDiskWorkspaceFolders,
showAndLogErrorMessage,
QlPacksForLanguage,
showAndLogExceptionWithTelemetry,
} from "../helpers";
import { KeyType, kindOfKeyType, nameOfKeyType, tagOfKeyType } from "./keyType";
import { CodeQLCliServer } from "../cli";
@@ -88,22 +88,17 @@ export async function resolveQueries(
}
// No queries found. Determine the correct error message for the various scenarios.
const errorMessage = `No ${nameOfKeyType(
keyType,
)} queries (tagged "${tagOfKeyType(
keyType,
)}") could be found in the current library path. \
Try upgrading the CodeQL libraries. If that doesn't work, then ${nameOfKeyType(
keyType,
)} queries are not yet available \
const keyTypeName = nameOfKeyType(keyType);
const keyTypeTag = tagOfKeyType(keyType);
const joinedPacksToSearch = packsToSearch.join(", ");
const errorMessage = `No ${keyTypeName} queries (tagged "${keyTypeTag}") could be found in the \
current library path (tried searching the following packs: ${joinedPacksToSearch}). \
Try upgrading the CodeQL libraries. If that doesn't work, then ${keyTypeName} queries are not yet available \
for this language.`;
void showAndLogErrorMessage(errorMessage);
throw new Error(
`Couldn't find any queries tagged ${tagOfKeyType(
keyType,
)} in any of the following packs: ${packsToSearch.join(", ")}.`,
);
const e = new Error(errorMessage);
void showAndLogExceptionWithTelemetry(e, "resolve_queries");
throw e;
}
async function resolveContextualQuery(

View File

@@ -29,6 +29,7 @@ import {
isLikelyDatabaseRoot,
isLikelyDbLanguageFolder,
showAndLogErrorMessage,
showAndLogExceptionWithTelemetry,
} from "./helpers";
import { extLogger } from "./common";
import {
@@ -36,7 +37,7 @@ import {
promptImportGithubDatabase,
promptImportInternetDatabase,
} from "./databaseFetcher";
import { asyncFilter, getErrorMessage } from "./pure/helpers-pure";
import { asError, asyncFilter, getErrorMessage } from "./pure/helpers-pure";
import { QueryRunner } from "./queryRunner";
import { isCanary } from "./config";
import { App } from "./common/app";
@@ -344,7 +345,10 @@ export class DatabaseUI extends DisposableObject {
try {
await this.chooseAndSetDatabase(true, progress, token);
} catch (e) {
void showAndLogErrorMessage(getErrorMessage(e));
void showAndLogExceptionWithTelemetry(
asError(e),
"databases_ui_choose_and_set_database",
);
}
};
@@ -393,6 +397,10 @@ export class DatabaseUI extends DisposableObject {
void extLogger.log(`Deleting orphaned database '${dbDir}'.`);
await remove(dbDir);
} catch (e) {
void showAndLogExceptionWithTelemetry(
asError(e),
"databases_ui_remove_orphaned_database",
);
failures.push(`${basename(dbDir)}`);
}
}),
@@ -414,8 +422,11 @@ export class DatabaseUI extends DisposableObject {
): Promise<void> => {
try {
await this.chooseAndSetDatabase(false, progress, token);
} catch (e) {
void showAndLogErrorMessage(getErrorMessage(e));
} catch (e: unknown) {
void showAndLogExceptionWithTelemetry(
asError(e),
"databases_ui_choose_and_set_database",
);
}
};

View File

@@ -5,10 +5,10 @@ import * as vscode from "vscode";
import * as cli from "./cli";
import { ExtensionContext } from "vscode";
import {
showAndLogErrorMessage,
showAndLogWarningMessage,
showAndLogInformationMessage,
isLikelyDatabaseRoot,
showAndLogExceptionWithTelemetry,
} from "./helpers";
import { ProgressCallback, withProgress } from "./commandRunner";
import {
@@ -794,8 +794,14 @@ export class DatabaseManager extends DisposableObject {
await this.updatePersistedDatabaseList();
} catch (e) {
// database list had an unexpected type - nothing to be done?
void showAndLogErrorMessage(
`Database list loading failed: ${getErrorMessage(e)}`,
void showAndLogExceptionWithTelemetry(
asError(e),
"databases_load_persisted_state",
{
notificationMessage: `Database list loading failed: ${getErrorMessage(
e,
)}`,
},
);
}

View File

@@ -10,7 +10,8 @@ import {
} from "vscode";
import { commandRunner } from "./commandRunner";
import { DisposableObject } from "./pure/disposable-object";
import { showAndLogErrorMessage } from "./helpers";
import { showAndLogExceptionWithTelemetry } from "./helpers";
import { asError } from "./pure/helpers-pure";
export interface EvalLogTreeItem {
label?: string;
@@ -104,7 +105,11 @@ export class EvalLogViewer extends DisposableObject {
() => {
/**/
},
(err) => showAndLogErrorMessage(err),
(err: unknown) =>
showAndLogExceptionWithTelemetry(
asError(err),
"eval_log_viewer_reveal",
),
);
}
}

View File

@@ -72,6 +72,7 @@ import {
showAndLogInformationMessage,
showInformationMessageWithAction,
tmpDir,
showAndLogExceptionWithTelemetry,
} from "./helpers";
import { asError, assertNever, getErrorMessage } from "./pure/helpers-pure";
import { spawnIdeServer } from "./ide-server";
@@ -714,7 +715,10 @@ async function activateWithInstalledDistribution(
try {
await compareView.showResults(from, to);
} catch (e) {
void showAndLogErrorMessage(getErrorMessage(e));
void showAndLogExceptionWithTelemetry(
asError(e),
"compare_view_show_results",
);
}
}
@@ -812,9 +816,14 @@ async function activateWithInstalledDistribution(
)
? `Could not generate markdown from ${pathToQhelp}: Bad formatting in .qhelp file.`
: `Could not open a preview of the generated file (${absolutePathToMd}).`;
void showAndLogErrorMessage(errorMessage, {
fullMessage: `${errorMessage}\n${e}`,
});
void showAndLogExceptionWithTelemetry(
asError(e),
"preview_query_help",
{
notificationMessage: errorMessage,
fullMessage: `${errorMessage}\n${getErrorMessage(e)}`,
},
);
}
}
}

View File

@@ -21,6 +21,7 @@ import { CodeQLCliServer, QlpacksInfo } from "./cli";
import { UserCancellationException } from "./commandRunner";
import { extLogger, OutputChannelLogger } from "./common";
import { QueryMetadata } from "./pure/interface-types";
import { ErrorType, telemetryListener } from "./telemetry";
// Shared temporary folder for the extension.
export const tmpDir = dirSync({
@@ -43,6 +44,13 @@ export const tmpDirDisposal = {
},
};
interface ShowAndLogExceptionOptions extends ShowAndLogOptions {
/** Custom properties to include in the telemetry report. */
extraTelemetryProperties?: { [key: string]: string };
/** An alternate message to use for the notification instead of the message from the `Error`. */
notificationMessage?: string;
}
interface ShowAndLogOptions {
/** The output logger that will receive the message. */
outputLogger?: OutputChannelLogger;
@@ -55,6 +63,33 @@ interface ShowAndLogOptions {
fullMessage?: string;
}
/**
* Show an error message and log it to the console
*
* @param error The error message to show, either as a Error or string. Will not be included in the
* telemetry event, and therefore may safely include sensitive information.
* @param telemetryErrorType A safe string that identifies the error, to be included in the
* telemetry event. If not provided, then no telemetry event will be sent.
* @param options See indivual fields on `ShowAndLogExceptionOptions` type.
*
* @return A promise that resolves to the selected item or undefined when being dismissed.
*/
export async function showAndLogExceptionWithTelemetry(
error: Error,
telemetryErrorType: ErrorType,
options: ShowAndLogExceptionOptions = {},
): Promise<string | undefined> {
telemetryListener?.sendError(
telemetryErrorType,
error.stack,
options.extraTelemetryProperties,
);
return showAndLogErrorMessage(
options.notificationMessage ?? error.message,
options,
);
}
/**
* Show an error message and log it to the console
*

View File

@@ -13,8 +13,9 @@ import {
import * as cli from "./cli";
import { CodeQLCliServer } from "./cli";
import { DatabaseEventKind, DatabaseItem, DatabaseManager } from "./databases";
import { showAndLogErrorMessage } from "./helpers";
import { showAndLogExceptionWithTelemetry } from "./helpers";
import {
asError,
assertNever,
getErrorMessage,
getErrorStack,
@@ -291,9 +292,13 @@ export class ResultsView extends AbstractWebview<
assertNever(msg);
}
} catch (e) {
void showAndLogErrorMessage(getErrorMessage(e), {
fullMessage: getErrorStack(e),
});
void showAndLogExceptionWithTelemetry(
asError(e),
"results_view_on_message",
{
fullMessage: getErrorStack(e),
},
);
}
}
@@ -335,8 +340,9 @@ export class ResultsView extends AbstractWebview<
sortState: InterpretedResultsSortState | undefined,
): Promise<void> {
if (this._displayedQuery === undefined) {
void showAndLogErrorMessage(
"Failed to sort results since evaluation info was unknown.",
void showAndLogExceptionWithTelemetry(
asError("Failed to sort results since evaluation info was unknown."),
"results_view_displayed_query_undefined",
);
return;
}
@@ -353,8 +359,9 @@ export class ResultsView extends AbstractWebview<
sortState: RawResultsSortState | undefined,
): Promise<void> {
if (this._displayedQuery === undefined) {
void showAndLogErrorMessage(
"Failed to sort results since evaluation info was unknown.",
void showAndLogExceptionWithTelemetry(
asError("Failed to sort results since evaluation info was unknown."),
"results_view_displayed_query_undefined",
);
return;
}
@@ -762,10 +769,14 @@ export class ResultsView extends AbstractWebview<
} catch (e) {
// If interpretation fails, accept the error and continue
// trying to render uninterpreted results anyway.
void showAndLogErrorMessage(
`Showing raw results instead of interpreted ones due to an error. ${getErrorMessage(
e,
)}`,
void showAndLogExceptionWithTelemetry(
asError(e),
"results_view_interpret_results_info",
{
notificationMessage: `Showing raw results instead of interpreted ones due to an error. ${getErrorMessage(
e,
)}`,
},
);
}
}

View File

@@ -8,6 +8,7 @@ import { DatabaseItem } from "../databases";
import {
getOnDiskWorkspaceFolders,
showAndLogErrorMessage,
showAndLogExceptionWithTelemetry,
showAndLogWarningMessage,
tryGetQueryMetadata,
upgradesTmpDir,
@@ -18,7 +19,7 @@ import { extLogger } from "../common";
import * as messages from "../pure/legacy-messages";
import { InitialQueryInfo, LocalQueryInfo } from "../query-results";
import * as qsClient from "./queryserver-client";
import { getErrorMessage } from "../pure/helpers-pure";
import { asError, getErrorMessage } from "../pure/helpers-pure";
import { compileDatabaseUpgradeSequence } from "./upgrades";
import { QueryEvaluationInfo, QueryWithResults } from "../run-queries-shared";
@@ -367,10 +368,16 @@ export async function compileAndRunQueryAgainstDatabase(
void extLogger.log("Did not find any available ML models.");
}
} catch (e) {
const message =
const notificationMessage =
`Couldn't resolve available ML models for ${qlProgram.queryPath}. Running the ` +
`query without any ML models: ${e}.`;
void showAndLogErrorMessage(message);
void showAndLogExceptionWithTelemetry(
asError(e),
"legacy_query_server_ml_models_not_found",
{
notificationMessage,
},
);
}
const hasMetadataFile = await dbItem.hasMetadataFile();
@@ -424,7 +431,10 @@ export async function compileAndRunQueryAgainstDatabase(
if (result.resultType !== messages.QueryResultType.SUCCESS) {
const message = result.message || "Failed to run query";
void extLogger.log(message);
void showAndLogErrorMessage(message);
void showAndLogExceptionWithTelemetry(
asError(message),
"legacy_query_server_run_queries",
);
}
const message = formatLegacyMessage(result);

View File

@@ -1,7 +1,7 @@
import * as vscode from "vscode";
import {
getOnDiskWorkspaceFolders,
showAndLogErrorMessage,
showAndLogExceptionWithTelemetry,
tmpDir,
} from "../helpers";
import { ProgressCallback, UserCancellationException } from "../commandRunner";
@@ -11,6 +11,7 @@ import * as qsClient from "./queryserver-client";
import * as tmp from "tmp-promise";
import { dirname } from "path";
import { DatabaseItem } from "../databases";
import { asError } from "../pure/helpers-pure";
/**
* Maximum number of lines to include from database upgrade message,
@@ -209,8 +210,12 @@ export async function upgradeDatabaseExplicit(
token,
);
} catch (e) {
void showAndLogErrorMessage(
`Compilation of database upgrades failed: ${e}`,
void showAndLogExceptionWithTelemetry(
asError(e),
"database_upgrade_compilation",
{
notificationMessage: `Compilation of database upgrades failed: ${e}`,
},
);
return;
} finally {
@@ -220,8 +225,9 @@ export async function upgradeDatabaseExplicit(
if (!compileUpgradeResult.compiledUpgrades) {
const error =
compileUpgradeResult.error || "[no error message available]";
void showAndLogErrorMessage(
`Compilation of database upgrades failed: ${error}`,
void showAndLogExceptionWithTelemetry(
asError(`Compilation of database upgrades failed: ${error}`),
"database_upgrade_compilation",
);
return;
}
@@ -253,7 +259,9 @@ export async function upgradeDatabaseExplicit(
await qs.restartQueryServer(progress, token);
return result;
} catch (e) {
void showAndLogErrorMessage(`Database upgrade failed: ${e}`);
void showAndLogExceptionWithTelemetry(asError(e), "database_upgrade", {
notificationMessage: `Database upgrade failed: ${e}`,
});
return;
} finally {
void qs.logger.log("Done running database upgrade.");

View File

@@ -1,12 +1,13 @@
import { CodeQLCliServer } from "./cli";
import {
getOnDiskWorkspaceFolders,
showAndLogErrorMessage,
showAndLogExceptionWithTelemetry,
showAndLogInformationMessage,
} from "./helpers";
import { QuickPickItem, window } from "vscode";
import { ProgressCallback, UserCancellationException } from "./commandRunner";
import { extLogger } from "./common";
import { asError, getErrorStack } from "./pure/helpers-pure";
const QUERY_PACKS = [
"codeql/cpp-queries",
@@ -66,8 +67,14 @@ export async function handleDownloadPacks(
await cliServer.packDownload(packsToDownload);
void showAndLogInformationMessage("Finished downloading packs.");
} catch (error) {
void showAndLogErrorMessage(
"Unable to download all packs. See log for more details.",
void showAndLogExceptionWithTelemetry(
asError(error),
"packaging_download_packs",
{
notificationMessage:
"Unable to download all packs. See log for more details.",
fullMessage: getErrorStack(error),
},
);
}
}

View File

@@ -46,11 +46,11 @@ export const REPO_REGEX = /^[a-zA-Z0-9-_\.]+\/[a-zA-Z0-9-_\.]+$/;
*/
export const OWNER_REGEX = /^[a-zA-Z0-9-_\.]+$/;
export function getErrorMessage(e: unknown) {
export function getErrorMessage(e: unknown): string {
return e instanceof Error ? e.message : String(e);
}
export function getErrorStack(e: unknown) {
export function getErrorStack(e: unknown): string {
return e instanceof Error ? e.stack ?? "" : "";
}

View File

@@ -16,6 +16,7 @@ import {
import { QueryHistoryConfig } from "../config";
import {
showAndLogErrorMessage,
showAndLogExceptionWithTelemetry,
showAndLogInformationMessage,
showAndLogWarningMessage,
showBinaryChoiceDialog,
@@ -27,6 +28,7 @@ import { DisposableObject } from "../pure/disposable-object";
import { commandRunner } from "../commandRunner";
import { ONE_HOUR_IN_MS, TWO_HOURS_IN_MS } from "../pure/time";
import {
asError,
assertNever,
getErrorMessage,
getErrorStack,
@@ -809,7 +811,10 @@ export class QueryHistoryManager extends DisposableObject {
);
}
} catch (e) {
void showAndLogErrorMessage(getErrorMessage(e));
void showAndLogExceptionWithTelemetry(
asError(e),
"query_history_manager_compare_with",
);
}
}
@@ -1374,13 +1379,21 @@ the file in the file explorer and dragging it into the workspace.`,
try {
await commands.executeCommand("revealFileInOS", uri);
} catch (e) {
void showAndLogErrorMessage(getErrorMessage(e));
void showAndLogExceptionWithTelemetry(
asError(e),
"query_history_manager_reveal_file_in_os",
);
}
}
} else {
void showAndLogErrorMessage(`Could not open file ${fileLocation}`);
void extLogger.log(getErrorMessage(e));
void extLogger.log(getErrorStack(e));
void showAndLogExceptionWithTelemetry(
asError(e),
"query_history_manager_show_text_document",
{
notificationMessage: `Could not open file ${fileLocation}`,
fullMessage: `${getErrorMessage(e)}\n${getErrorStack(e)}`,
},
);
}
}
}

View File

@@ -1,8 +1,9 @@
import { pathExists, readFile, remove, mkdir, writeFile } from "fs-extra";
import { dirname } from "path";
import { showAndLogErrorMessage } from "./helpers";
import { showAndLogExceptionWithTelemetry } from "./helpers";
import {
asError,
asyncFilter,
getErrorMessage,
getErrorStack,
@@ -24,8 +25,11 @@ export async function deserializeQueryHistory(
const data = await readFile(fsPath, "utf8");
const obj = JSON.parse(data);
if (![1, 2].includes(obj.version)) {
void showAndLogErrorMessage(
`Can't parse query history. Unsupported query history format: v${obj.version}. `,
void showAndLogExceptionWithTelemetry(
asError(
`Can't parse query history. Unsupported query history format: v${obj.version}.`,
),
"query_serialization_unsupported_format",
);
return [];
}
@@ -92,11 +96,14 @@ export async function deserializeQueryHistory(
return !!resultsPath && (await pathExists(resultsPath));
});
} catch (e) {
void showAndLogErrorMessage("Error loading query history.", {
fullMessage: ["Error loading query history.", getErrorStack(e)].join(
"\n",
),
});
void showAndLogExceptionWithTelemetry(
asError(e),
"query_history_deserialization",
{
notificationMessage: "Error loading query history.",
fullMessage: `Error loading query history.\n${getErrorStack(e)}`,
},
);
// since the query history is invalid, it should be deleted so this error does not happen on next startup.
await remove(fsPath);
return [];

View File

@@ -5,7 +5,7 @@ import { ProgressCallback } from "../commandRunner";
import { DatabaseItem } from "../databases";
import {
getOnDiskWorkspaceFolders,
showAndLogErrorMessage,
showAndLogExceptionWithTelemetry,
showAndLogWarningMessage,
tryGetQueryMetadata,
} from "../helpers";
@@ -15,6 +15,7 @@ import { QueryResultType } from "../pure/legacy-messages";
import { InitialQueryInfo, LocalQueryInfo } from "../query-results";
import { QueryEvaluationInfo, QueryWithResults } from "../run-queries-shared";
import * as qsClient from "./queryserver-client";
import { asError } from "../pure/helpers-pure";
/**
* run-queries.ts
@@ -111,7 +112,10 @@ export async function compileAndRunQueryAgainstDatabase(
if (result.resultType !== messages.QueryResultType.SUCCESS) {
const message = result.message || "Failed to run query";
void extLogger.log(message);
void showAndLogErrorMessage(message);
void showAndLogExceptionWithTelemetry(
asError(message),
"query_server_run_queries",
);
}
let message;
switch (result.resultType) {

View File

@@ -1,7 +1,7 @@
import { join } from "path";
import { pathExists, readFile, writeFile } from "fs-extra";
import {
showAndLogErrorMessage,
showAndLogExceptionWithTelemetry,
showAndLogWarningMessage,
tmpDir,
} from "../../helpers";
@@ -15,7 +15,7 @@ import {
RemoteQueryResultIndex,
RemoteQuerySuccessIndexItem,
} from "../remote-query-result-index";
import { getErrorMessage } from "../../pure/helpers-pure";
import { asError, getErrorMessage } from "../../pure/helpers-pure";
import { unzipFile } from "../../pure/zip";
import { VariantAnalysis } from "../shared/variant-analysis";
@@ -492,10 +492,14 @@ export async function getRepositoriesMetadata(
}
} while (cursor);
} catch (e) {
void showAndLogErrorMessage(
`Error retrieving repository metadata for variant analysis: ${getErrorMessage(
e,
)}`,
void showAndLogExceptionWithTelemetry(
asError(e),
"gh_actions_api_client_get_repositories_metadata",
{
notificationMessage: `Error retrieving repository metadata for variant analysis: ${getErrorMessage(
e,
)}`,
},
);
}

View File

@@ -6,9 +6,10 @@ import { RemoteQueriesResponse } from "./gh-api/remote-queries";
import { submitRemoteQueries } from "./gh-api/gh-api-client";
import {
showAndLogErrorMessage,
showAndLogExceptionWithTelemetry,
showAndLogInformationMessage,
} from "../helpers";
import { getErrorMessage } from "../pure/helpers-pure";
import { asError } from "../pure/helpers-pure";
import { pluralize } from "../pure/word";
export async function runRemoteQueriesApiRequest(
@@ -43,7 +44,10 @@ export async function runRemoteQueriesApiRequest(
`Controller repository was not found. Please make sure it's a valid repo name.${eol}`,
);
} else {
void showAndLogErrorMessage(getErrorMessage(error));
void showAndLogExceptionWithTelemetry(
asError(error),
"remote_queries_submit",
);
}
}
}

View File

@@ -16,6 +16,7 @@ import { ProgressCallback } from "../commandRunner";
import {
createTimestampFile,
showAndLogErrorMessage,
showAndLogExceptionWithTelemetry,
showAndLogInformationMessage,
showInformationMessageWithAction,
} from "../helpers";
@@ -36,7 +37,7 @@ import {
} from "./remote-query-result";
import { DownloadLink } from "./download-link";
import { AnalysesResultsManager } from "./analyses-results-manager";
import { assertNever, getErrorMessage } from "../pure/helpers-pure";
import { asError, assertNever } from "../pure/helpers-pure";
import { QueryStatus } from "../query-status";
import { DisposableObject } from "../pure/disposable-object";
import { AnalysisResults } from "./shared/analysis-result";
@@ -149,10 +150,23 @@ export class RemoteQueriesManager extends DisposableObject {
// Open results in the background
void this.openResults(remoteQuery, remoteQueryResult).then(
noop,
(err: unknown) => void showAndLogErrorMessage(getErrorMessage(err)),
(e: unknown) =>
void showAndLogExceptionWithTelemetry(
asError(e),
"remote_queries_manager_open_results",
{
notificationMessage: `Could not open query results. ${e}`,
},
),
);
} catch (e) {
void showAndLogErrorMessage(`Could not open query results. ${e}`);
void showAndLogExceptionWithTelemetry(
asError(e),
"remote_queries_manager_open_results",
{
notificationMessage: `Could not open query results. ${e}`,
},
);
}
}
@@ -269,8 +283,9 @@ export class RemoteQueriesManager extends DisposableObject {
void showAndLogInformationMessage("Variant analysis was cancelled");
} else if (queryWorkflowResult.status === "InProgress") {
// Should not get here. Only including this to ensure `assertNever` uses proper type checking.
void showAndLogErrorMessage(
`Unexpected status: ${queryWorkflowResult.status}`,
void showAndLogExceptionWithTelemetry(
asError(`Unexpected status: ${queryWorkflowResult.status}`),
"remote_queries_manager_monitor_unexpectd_status",
);
} else {
// Ensure all cases are covered
@@ -475,15 +490,23 @@ export class RemoteQueriesManager extends DisposableObject {
// Ask if the user wants to open the results in the background.
void this.askToOpenResults(remoteQuery, queryResult).then(
noop,
(err: unknown) => {
void showAndLogErrorMessage(getErrorMessage(err));
},
(e: unknown) =>
void showAndLogExceptionWithTelemetry(
asError(e),
"remote_queries_manager_open_results",
{
notificationMessage: `Could not open query results. ${e}`,
},
),
);
} else {
const controllerRepo = `${remoteQuery.controllerRepository.owner}/${remoteQuery.controllerRepository.name}`;
const workflowRunUrl = `https://github.com/${controllerRepo}/actions/runs/${remoteQuery.actionsWorkflowRunId}`;
void showAndLogErrorMessage(
`There was an issue retrieving the result for the query [${remoteQuery.queryName}](${workflowRunUrl}).`,
void showAndLogExceptionWithTelemetry(
asError(
`There was an issue retrieving the result for the query [${remoteQuery.queryName}](${workflowRunUrl}).`,
),
"remote_queries_manager_download_missing_index",
);
this.remoteQueryStatusUpdateEventEmitter.fire({
queryId,

View File

@@ -29,7 +29,7 @@ import {
VariantAnalysisScannedRepositoryState,
VariantAnalysisSubmission,
} from "./shared/variant-analysis";
import { getErrorMessage } from "../pure/helpers-pure";
import { asError, getErrorMessage } from "../pure/helpers-pure";
import { VariantAnalysisView } from "./variant-analysis-view";
import { VariantAnalysisViewManager } from "./variant-analysis-view-manager";
import {
@@ -44,7 +44,7 @@ import {
import PQueue from "p-queue";
import {
createTimestampFile,
showAndLogErrorMessage,
showAndLogExceptionWithTelemetry,
showAndLogInformationMessage,
showAndLogWarningMessage,
} from "../helpers";
@@ -261,8 +261,9 @@ export class VariantAnalysisManager
public async showView(variantAnalysisId: number): Promise<void> {
if (!this.variantAnalyses.get(variantAnalysisId)) {
void showAndLogErrorMessage(
`No variant analysis found with id: ${variantAnalysisId}.`,
void showAndLogExceptionWithTelemetry(
asError(`No variant analysis found with id: ${variantAnalysisId}.`),
"variant_analysis_manager_id_not_found",
);
}
if (!this.views.has(variantAnalysisId)) {

View File

@@ -28,6 +28,38 @@ export enum CommandCompletion {
Cancelled = "Cancelled",
}
export type ErrorType =
| "AST_viewer_reveal"
| "command_failed"
| "compare_view_show_results"
| "databases_load_persisted_state"
| "databases_ui_choose_and_set_database"
| "databases_ui_remove_orphaned_database"
| "database_upgrade"
| "database_upgrade_compilation"
| "eval_log_viewer_reveal"
| "gh_actions_api_client_get_repositories_metadata"
| "legacy_query_server_ml_models_not_found"
| "legacy_query_server_run_queries"
| "packaging_download_packs"
| "preview_query_help"
| "query_history_deserialization"
| "query_history_manager_compare_with"
| "query_history_manager_reveal_file_in_os"
| "query_history_manager_show_text_document"
| "query_serialization_unsupported_format"
| "query_server_run_queries"
| "remote_queries_submit"
| "remote_queries_manager_open_results"
| "remote_queries_manager_monitor_unexpectd_status"
| "remote_queries_manager_download_missing_index"
| "resolve_queries"
| "results_view_on_message"
| "results_view_interpret_results_info"
| "results_view_displayed_query_undefined"
| "test_adapter_remove_databases_before_tests"
| "variant_analysis_manager_id_not_found";
// Avoid sending the following data to App insights since we don't need it.
const tagsToRemove = [
"ai.application.ver",
@@ -182,6 +214,26 @@ export class TelemetryListener extends ConfigListener {
);
}
sendError(
errorType: ErrorType,
stack?: string,
extraProperties?: { [key: string]: string },
) {
if (!this.reporter) {
return;
}
const properties: { [key: string]: string } = {
type: errorType,
...extraProperties,
};
if (stack && stack !== "") {
properties.stack = stack;
}
this.reporter.sendTelemetryEvent("error", properties, {});
}
/**
* Displays a popup asking the user if they want to enable telemetry
* for this extension.

View File

@@ -30,11 +30,12 @@ import { DisposableObject } from "./pure/disposable-object";
import { CodeQLCliServer } from "./cli";
import {
getOnDiskWorkspaceFolders,
showAndLogErrorMessage,
showAndLogExceptionWithTelemetry,
showAndLogWarningMessage,
} from "./helpers";
import { testLogger } from "./common";
import { DatabaseItem, DatabaseManager } from "./databases";
import { asError } from "./pure/helpers-pure";
/**
* Get the full path of the `.expected` file for the specified QL test.
@@ -278,8 +279,12 @@ export class QLTestAdapter extends DisposableObject implements TestAdapter {
// This method is invoked from Test Explorer UI, and testing indicates that Test
// Explorer UI swallows any thrown exception without reporting it to the user.
// So we need to display the error message ourselves and then rethrow.
void showAndLogErrorMessage(
`Cannot remove database ${database.name}: ${e}`,
void showAndLogExceptionWithTelemetry(
asError(e),
"test_adapter_remove_databases_before_tests",
{
notificationMessage: `Cannot remove database ${database.name}: ${e}`,
},
);
throw e;
}