diff --git a/extensions/ql-vscode/CHANGELOG.md b/extensions/ql-vscode/CHANGELOG.md index 50b4ff560..e26a116c0 100644 --- a/extensions/ql-vscode/CHANGELOG.md +++ b/extensions/ql-vscode/CHANGELOG.md @@ -6,6 +6,7 @@ - Avoid synchronizing the `codeQL.cli.executablePath` setting. [#1252](https://github.com/github/vscode-codeql/pull/1252) - Open the directory in the finder/explorer (instead of just highlighting it) when running the "Open query directory" command from the query history view. [#1235](https://github.com/github/vscode-codeql/pull/1235) - Ensure query label in the query history view changes are persisted across restarts. [#1235](https://github.com/github/vscode-codeql/pull/1235) +- Prints end-of-query evaluator log summaries to the Query Server Console. [#1264](https://github.com/github/vscode-codeql/pull/1264) ## 1.6.1 - 17 March 2022 diff --git a/extensions/ql-vscode/src/cli.ts b/extensions/ql-vscode/src/cli.ts index 7ee7d5e87..9a9d1295f 100644 --- a/extensions/ql-vscode/src/cli.ts +++ b/extensions/ql-vscode/src/cli.ts @@ -667,15 +667,18 @@ export class CodeQLCliServer implements Disposable { /** * Generate a summary of an evaluation log. + * @param endSummaryPath The path to write only the end of query part of the human-readable summary to. * @param inputPath The path of an evaluation event log. * @param outputPath The path to write a human-readable summary of it to. */ async generateLogSummary( inputPath: string, outputPath: string, + endSummaryPath: string, ): Promise { const subcommandArgs = [ '--format=text', + `--end-summary=${endSummaryPath}`, inputPath, outputPath ]; @@ -1279,8 +1282,14 @@ export class CliVersionConstraint { /** * CLI version that supports rotating structured logs to produce one per query. + * + * Note that 2.8.4 supports generating the evaluation logs and summaries, + * but 2.9.0 includes a new option to produce the end-of-query summary logs to + * the query server console. For simplicity we gate all features behind 2.9.0, + * but if a user is tied to the 2.8 release, we can enable evaluator logs + * and summaries for them. */ - public static CLI_VERSION_WITH_PER_QUERY_EVAL_LOG = new SemVer('2.8.4'); + public static CLI_VERSION_WITH_PER_QUERY_EVAL_LOG = new SemVer('2.9.0'); constructor(private readonly cli: CodeQLCliServer) { /**/ diff --git a/extensions/ql-vscode/src/pure/messages.ts b/extensions/ql-vscode/src/pure/messages.ts index d6f95a6f1..ac3cac01d 100644 --- a/extensions/ql-vscode/src/pure/messages.ts +++ b/extensions/ql-vscode/src/pure/messages.ts @@ -722,7 +722,7 @@ export interface StartLogResult { } /** - * The result of terminating a structured. + * The result of terminating a structured log. */ export interface EndLogResult { /** diff --git a/extensions/ql-vscode/src/query-history.ts b/extensions/ql-vscode/src/query-history.ts index 368761a07..822f13b4d 100644 --- a/extensions/ql-vscode/src/query-history.ts +++ b/extensions/ql-vscode/src/query-history.ts @@ -781,6 +781,11 @@ export class QueryHistoryManager extends DisposableObject { void showAndLogWarningMessage('No evaluator log is available for this run. Perhaps it failed before evaluation, or you are running with a version of CodeQL before ' + CliVersionConstraint.CLI_VERSION_WITH_PER_QUERY_EVAL_LOG + '?'); } + private warnNoEvalLogSummary() { + void showAndLogWarningMessage(`No evaluator log summary is available for this run. Perhaps it failed before evaluation, or you are running with a version of CodeQL before ${CliVersionConstraint.CLI_VERSION_WITH_PER_QUERY_EVAL_LOG}?`); + } + + async handleShowEvalLog( singleItem: QueryHistoryInfo, multiSelect: QueryHistoryInfo[] @@ -810,13 +815,10 @@ export class QueryHistoryManager extends DisposableObject { return; } - if (finalSingleItem.evalLogLocation) { - if (!fs.existsSync(finalSingleItem.evalLogSummaryLocation)) { - await this.qs.cliServer.generateLogSummary(finalSingleItem.evalLogLocation, finalSingleItem.evalLogSummaryLocation); - } - await this.tryOpenExternalFile(finalSingleItem.evalLogSummaryLocation); + if (finalSingleItem.evalLogSummaryLocation) { + await this.tryOpenExternalFile(finalSingleItem.evalLogSummaryLocation); } else { - this.warnNoEvalLog(); + this.warnNoEvalLogSummary(); } } diff --git a/extensions/ql-vscode/src/query-results.ts b/extensions/ql-vscode/src/query-results.ts index f6d10e375..67ed99339 100644 --- a/extensions/ql-vscode/src/query-results.ts +++ b/extensions/ql-vscode/src/query-results.ts @@ -217,6 +217,7 @@ export class LocalQueryInfo { public failureReason: string | undefined; public completedQuery: CompletedQueryInfo | undefined; public evalLogLocation: string | undefined; + public evalLogSummaryLocation: string | undefined; private config: QueryHistoryConfig | undefined; /** @@ -312,14 +313,6 @@ export class LocalQueryInfo { } } - /** - * Return the location of a query's evaluator log summary. This file may not exist yet, - * in which case it can be created by invoking `codeql generate log-summary`. - */ - get evalLogSummaryLocation(): string { - return this.evalLogLocation + '.summary'; - } - get completed(): boolean { return !!this.completedQuery; } diff --git a/extensions/ql-vscode/src/queryserver-client.ts b/extensions/ql-vscode/src/queryserver-client.ts index 23549068d..af51ef519 100644 --- a/extensions/ql-vscode/src/queryserver-client.ts +++ b/extensions/ql-vscode/src/queryserver-client.ts @@ -259,6 +259,14 @@ export function findQueryLogFile(resultPath: string): string { return path.join(resultPath, 'query.log'); } -export function findQueryStructLogFile(resultPath: string): string { +export function findQueryEvalLogFile(resultPath: string): string { return path.join(resultPath, 'evaluator-log.jsonl'); } + +export function findQueryEvalLogSummaryFile(resultPath: string): string { + return path.join(resultPath, 'evaluator-log.summary'); +} + +export function findQueryEvalLogEndSummaryFile(resultPath: string): string { + return path.join(resultPath, 'evaluator-log-end.summary'); +} \ No newline at end of file diff --git a/extensions/ql-vscode/src/run-queries.ts b/extensions/ql-vscode/src/run-queries.ts index d41eaad69..67f974755 100644 --- a/extensions/ql-vscode/src/run-queries.ts +++ b/extensions/ql-vscode/src/run-queries.ts @@ -95,8 +95,16 @@ export class QueryEvaluationInfo { return qsClient.findQueryLogFile(this.querySaveDir); } - get structLogPath() { - return qsClient.findQueryStructLogFile(this.querySaveDir); + get evalLogPath() { + return qsClient.findQueryEvalLogFile(this.querySaveDir); + } + + get evalLogSummaryPath() { + return qsClient.findQueryEvalLogSummaryFile(this.querySaveDir); + } + + get evalLogEndSummaryPath() { + return qsClient.findQueryEvalLogEndSummaryFile(this.querySaveDir); } get resultsPaths() { @@ -164,8 +172,9 @@ export class QueryEvaluationInfo { if (queryInfo && await qs.cliServer.cliConstraints.supportsPerQueryEvalLog()) { await qs.sendRequest(messages.startLog, { db: dataset, - logPath: this.structLogPath, + logPath: this.evalLogPath, }); + } const params: messages.EvaluateQueriesParams = { db: dataset, @@ -186,9 +195,22 @@ export class QueryEvaluationInfo { if (queryInfo && await qs.cliServer.cliConstraints.supportsPerQueryEvalLog()) { await qs.sendRequest(messages.endLog, { db: dataset, - logPath: this.structLogPath, + logPath: this.evalLogPath, }); - queryInfo.evalLogLocation = this.structLogPath; + if (await this.hasEvalLog()) { + queryInfo.evalLogLocation = this.evalLogPath; + await qs.cliServer.generateLogSummary(this.evalLogPath, this.evalLogSummaryPath, this.evalLogEndSummaryPath); + queryInfo.evalLogSummaryLocation = this.evalLogSummaryPath; + fs.readFile(this.evalLogEndSummaryPath, (err, buffer) => { + if (err) { + throw new Error(`Could not read structured evaluator log end of summary file at ${this.evalLogEndSummaryPath}.`); + } + void qs.logger.log(' --- Evaluator Log Summary --- '); + void qs.logger.log(buffer.toString()); + }); + } else { + void showAndLogWarningMessage(`Failed to write structured evaluator log to ${this.evalLogPath}.`); + } } } return result || { @@ -303,6 +325,13 @@ export class QueryEvaluationInfo { return this.dilPath; } + /** + * Holds if this query already has a completed structured evaluator log + */ + async hasEvalLog(): Promise { + return fs.pathExists(this.evalLogPath); + } + /** * Creates the CSV file containing the results of this query. This will only be called if the query * does not have interpreted results and the CSV file does not already exist.