Skip to content

Commit e9bbf11

Browse files
Merge pull request #2125 from github/robertbrignull/error_listener
Report unhandled errors, but only those that are from our extension
2 parents 68ce7c3 + 9386817 commit e9bbf11

File tree

2 files changed

+52
-1
lines changed

2 files changed

+52
-1
lines changed

extensions/ql-vscode/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
## [UNRELEASED]
44

5+
- Send telemetry about unhandled errors happening within the extension. [#2125](https://github.com/github/vscode-codeql/pull/2125)
6+
57
## 1.7.11 - 1 March 2023
68

79
- Enable collection of telemetry concerning interactions with UI elements, including buttons, links, and other inputs. [#2114](https://github.com/github/vscode-codeql/pull/2114)

extensions/ql-vscode/src/extension.ts

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,12 @@ import {
7272
tmpDir,
7373
tmpDirDisposal,
7474
} from "./helpers";
75-
import { asError, assertNever, getErrorMessage } from "./pure/helpers-pure";
75+
import {
76+
asError,
77+
assertNever,
78+
getErrorMessage,
79+
getErrorStack,
80+
} from "./pure/helpers-pure";
7681
import { spawnIdeServer } from "./ide-server";
7782
import { ResultsView } from "./interface";
7883
import { WebviewReveal } from "./interface-utils";
@@ -235,6 +240,7 @@ export async function activate(
235240
const distributionConfigListener = new DistributionConfigListener();
236241
await initializeLogging(ctx);
237242
await initializeTelemetry(extension, ctx);
243+
addUnhandledRejectionListener();
238244
install();
239245

240246
const codelensProvider = new QuickEvalCodeLensProvider();
@@ -1569,6 +1575,49 @@ async function activateWithInstalledDistribution(
15691575
};
15701576
}
15711577

1578+
function addUnhandledRejectionListener() {
1579+
const handler = (error: unknown) => {
1580+
// This listener will be triggered for errors from other extensions as
1581+
// well as errors from this extension. We don't want to flood the user
1582+
// with popups about errors from other extensions, and we don't want to
1583+
// report them in our telemetry.
1584+
//
1585+
// The stack trace gets redacted before being sent as telemetry, but at
1586+
// this point in the code we have the full unredacted information.
1587+
const isFromThisExtension =
1588+
extension && getErrorStack(error).includes(extension.extensionPath);
1589+
1590+
if (isFromThisExtension) {
1591+
const message = redactableError(
1592+
asError(error),
1593+
)`Unhandled error: ${getErrorMessage(error)}`;
1594+
// Add a catch so that showAndLogExceptionWithTelemetry fails, we avoid
1595+
// triggering "unhandledRejection" and avoid an infinite loop
1596+
showAndLogExceptionWithTelemetry(message).catch(
1597+
(telemetryError: unknown) => {
1598+
void extLogger.log(
1599+
`Failed to send error telemetry: ${getErrorMessage(
1600+
telemetryError,
1601+
)}`,
1602+
);
1603+
void extLogger.log(message.fullMessage);
1604+
},
1605+
);
1606+
}
1607+
};
1608+
1609+
// "uncaughtException" will trigger whenever an exception reaches the top level.
1610+
// This covers extension initialization and any code within a `setTimeout`.
1611+
// Notably this does not include exceptions thrown when executing commands,
1612+
// because `commandRunner` wraps the command body and handles errors.
1613+
process.addListener("uncaughtException", handler);
1614+
1615+
// "unhandledRejection" will trigger whenever any promise is rejected and it is
1616+
// not handled by a "catch" somewhere in the promise chain. This includes when
1617+
// a promise is used with the "void" operator.
1618+
process.addListener("unhandledRejection", handler);
1619+
}
1620+
15721621
async function createQueryServer(
15731622
qlConfigurationListener: QueryServerConfigListener,
15741623
cliServer: CodeQLCliServer,

0 commit comments

Comments
 (0)