Skip to content

Commit eefa7c1

Browse files
authored
Merge pull request #738 from rage/errorlog
Improved error messages & logging
2 parents 2a7a19f + dd0d334 commit eefa7c1

19 files changed

+185
-32
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
## [3.1.1] - 2025-02-21
4+
5+
- Improved error messages and logging.
6+
37
## [3.1.0] - 2024-12-17
48

59
- Fixed downloading old submissions for C# exercises

backend/tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22
"compilerOptions": {
33
"esModuleInterop": true,
44
"module": "commonjs",
5-
"target": "ES6"
5+
"target": "ES2022"
66
}
77
}

bin/validateRelease.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@ then
1818
fi
1919

2020
# Make sure that the package-lock.json version also matches
21-
packageLockVersion=$(grep -Eo '"version":.+$' package-lock.json)
21+
packageLockVersion=$(grep -Eom 1 '"version":.+$' package-lock.json)
2222
if [[ ! $packageLockVersion =~ '"version": "'$tagVersion'",' ]]
2323
then
24-
echo "Error: The version in package-lock.json '${packageVersion}' doesn't match with the tag '${tagVersion}'. Run 'npm i --package-lock-only'}"
24+
echo "Error: The version in package-lock.json '${packageLockVersion}' doesn't match with the tag '${tagVersion}'. Run \`npm i --package-lock-only\`."
2525
exitCode=1
2626
fi
2727

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "test-my-code",
33
"displayName": "TestMyCode",
4-
"version": "3.1.0",
4+
"version": "3.1.1",
55
"description": "TestMyCode extension for Visual Studio Code",
66
"categories": [
77
"Education",

playwright/tsconfig.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
"compilerOptions": {
33
"noEmit": true,
44
"module": "commonjs",
5-
"target": "es6",
6-
"lib": ["es6"],
5+
"target": "ES2022",
6+
"lib": ["ES2022"],
77
"sourceMap": true,
88
"rootDir": ".",
99
"strict": true

shared/lib.ts

Lines changed: 101 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
*/
44

55
import { Uri } from "vscode";
6+
import * as util from "node:util";
67

78
import { Course, Organization, RunResult, SubmissionFinished } from "./langsSchema";
9+
import { createIs } from "typia";
810

911
/**
1012
* Contains the state of the webview.
@@ -529,9 +531,105 @@ export function assertUnreachable(x: never): never {
529531
*/
530532
export class BaseError extends Error {
531533
public readonly name: string = "Base Error";
532-
public details: string;
533-
constructor(message?: string, details?: string) {
534+
public details?: string;
535+
public cause?: NodeJS.ErrnoException | string;
536+
public stack?: string;
537+
538+
// possible fields from ErrnoException
539+
public errno?: number;
540+
public code?: string;
541+
public path?: string;
542+
public syscall?: string;
543+
544+
constructor(err: unknown);
545+
constructor(err: Error, details?: string);
546+
constructor(message?: string, details?: string);
547+
548+
constructor(err: unknown, details?: string) {
549+
let message = "";
550+
let stack = "";
551+
let cause: NodeJS.ErrnoException | string = "";
552+
553+
let errno: number | undefined = undefined;
554+
let code: string | undefined = undefined;
555+
let path: string | undefined = undefined;
556+
let syscall: string | undefined = undefined;
557+
558+
// simple check first...
559+
if (typeof err === "string") {
560+
message = err;
561+
} else if (util.types.isNativeError(err)) {
562+
// deal with regular error stuff first
563+
message = err.message;
564+
if (err.stack) {
565+
stack = err.stack;
566+
}
567+
568+
// also check for special NodeJS error
569+
if (createIs<NodeJS.ErrnoException>()(err)) {
570+
// nodejs error with error code
571+
errno = err.errno;
572+
code = err.code;
573+
path = err.path;
574+
syscall = err.syscall;
575+
}
576+
577+
if (err.cause) {
578+
// same checks for cause
579+
if (
580+
util.types.isNativeError(err.cause) &&
581+
createIs<NodeJS.ErrnoException>()(err.cause)
582+
) {
583+
cause = err.cause;
584+
} else {
585+
cause = err.cause.toString();
586+
}
587+
}
588+
} else {
589+
// it's expected that this function is only called with
590+
// strings or error objects. but since errors are often "unknown"
591+
// in catch statements etc., this function accepts unknown types
592+
// and thus we'll handle them here just in case
593+
message = `Unexpected error ${err} (${typeof err})`;
594+
}
595+
534596
super(message);
535-
this.details = details ?? "";
597+
this.details = details;
598+
if (stack) {
599+
this.stack = stack;
600+
}
601+
if (cause) {
602+
this.cause = cause;
603+
}
604+
605+
// errno fields
606+
this.errno = errno;
607+
this.code = code;
608+
this.path = path;
609+
this.syscall = syscall;
610+
}
611+
612+
public toString(): string {
613+
let errorMessage = "";
614+
if (this.errno) {
615+
errorMessage += `[${this.errno}] `;
616+
}
617+
if (this.code) {
618+
errorMessage += `(${this.code}) `;
619+
}
620+
if (this.syscall) {
621+
errorMessage += `\`${this.syscall}\` `;
622+
}
623+
if (this.path) {
624+
errorMessage += `@${this.path} `;
625+
}
626+
errorMessage += `${this.name}: ${this.message}.`;
627+
if (this.details) {
628+
errorMessage += ` ${this.details}.`;
629+
}
630+
if (this.cause) {
631+
errorMessage += ` Caused by: ${this.cause}.`;
632+
}
633+
return errorMessage;
536634
}
537635
}

src/actions/checkForExerciseUpdates.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export async function checkForExerciseUpdates(
2525
): Promise<Result<OutdatedExercise[], Error>> {
2626
const { tmc, userData } = actionContext;
2727
const forceRefresh = options?.forceRefresh ?? false;
28-
Logger.info(`Checking for exercise updates, forced update: ${forceRefresh}`);
28+
Logger.info("Checking for exercise updates, forced update:", forceRefresh);
2929

3030
const checkUpdatesResult = await tmc.checkExerciseUpdates({ forceRefresh });
3131
if (checkUpdatesResult.err) {

src/actions/downloadNewExercisesForCourse.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export async function downloadNewExercisesForCourse(
1919
): Promise<Result<void, Error>> {
2020
const { userData } = actionContext;
2121
const course = userData.getCourse(courseId);
22-
Logger.info(`Downloading new exercises for course: ${course.title}`);
22+
Logger.info("Downloading new exercises for course:", course.title);
2323

2424
const postNewExercises = async (exerciseIds: number[]): Promise<void> =>
2525
await TmcPanel.postMessage({

src/actions/user.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ export async function submitExercise(
202202

203203
if (submissionResult.err) {
204204
if (submissionResult.val instanceof BottleneckError) {
205-
Logger.warn(`Submission was cancelled: ${submissionResult.val.message}.`);
205+
Logger.warn("Submission was cancelled:", submissionResult.val);
206206
return Ok.EMPTY;
207207
}
208208
TmcPanel.postMessage({

0 commit comments

Comments
 (0)