Skip to content

Commit add9a43

Browse files
authored
Merge pull request #9441 from keymanapp/chore/developer/report-fatal-compiler-errors-to-sentry
chore(developer): improve kmc sentry reporting on fatal build errors
2 parents 22f7bdc + 0692b82 commit add9a43

File tree

23 files changed

+129
-80
lines changed

23 files changed

+129
-80
lines changed

common/web/types/src/util/compiler-interfaces.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ export interface CompilerEvent {
66
line?: number;
77
code: number;
88
message: string;
9+
/**
10+
* an internal error occurred that should be captured with a stack trace
11+
* e.g. to the Keyman sentry instance by kmc
12+
*/
13+
exceptionVar?: any;
914
};
1015

1116
export enum CompilerErrorSeverity {
@@ -399,7 +404,13 @@ export const defaultCompilerOptions: CompilerOptions = {
399404
* @param message
400405
* @returns
401406
*/
402-
export const CompilerMessageSpec = (code: number, message: string) : CompilerEvent => { return { code, message } };
407+
export const CompilerMessageSpec = (code: number, message: string, exceptionVar?: any) : CompilerEvent => ({
408+
code,
409+
message: exceptionVar
410+
? (message ?? `Unexpected exception`) + `: ${exceptionVar.toString()}\n\nCall stack:\n${(exceptionVar instanceof Error ? exceptionVar.stack : (new Error()).stack)}` :
411+
message,
412+
exceptionVar
413+
});
403414

404415
/**
405416
* @deprecated use `CompilerError.exceptionToString` instead

developer/src/kmc-analyze/src/messages.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { CompilerErrorNamespace, CompilerErrorSeverity, CompilerMessageSpec as m, compilerExceptionToString as exc } from "@keymanapp/common-types";
1+
import { CompilerErrorNamespace, CompilerErrorSeverity, CompilerMessageSpec as m } from "@keymanapp/common-types";
22

33
const Namespace = CompilerErrorNamespace.Analyzer;
44
const SevInfo = CompilerErrorSeverity.Info | Namespace;
@@ -8,7 +8,7 @@ const SevInfo = CompilerErrorSeverity.Info | Namespace;
88
const SevFatal = CompilerErrorSeverity.Fatal | Namespace;
99

1010
export class AnalyzerMessages {
11-
static Fatal_UnexpectedException = (o:{e: any}) => m(this.FATAL_UnexpectedException, `Unexpected exception: ${exc(o.e)}`);
11+
static Fatal_UnexpectedException = (o:{e: any}) => m(this.FATAL_UnexpectedException, null, o.e ?? 'unknown error');
1212
static FATAL_UnexpectedException = SevFatal | 0x0001;
1313

1414
static Info_ScanningFile = (o:{type: string, name: string}) => m(this.INFO_ScanningFile,

developer/src/kmc-keyboard-info/src/messages.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@ const SevError = CompilerErrorSeverity.Error | Namespace;
88
const SevFatal = CompilerErrorSeverity.Fatal | Namespace;
99

1010
export class KeyboardInfoCompilerMessages {
11-
static Fatal_UnexpectedException = (o:{e: any}) => m(this.FATAL_UnexpectedException,
12-
`Unexpected exception: ${(o.e ?? 'unknown error').toString()}\n\nCall stack:\n${(o.e instanceof Error ? o.e.stack : (new Error()).stack)}`);
11+
static Fatal_UnexpectedException = (o:{e: any}) => m(this.FATAL_UnexpectedException, null, o.e ?? 'unknown error');
1312
static FATAL_UnexpectedException = SevFatal | 0x0001;
1413

1514
static Error_FileDoesNotExist = (o:{filename: string}) => m(this.ERROR_FileDoesNotExist, `File ${o.filename} does not exist.`);

developer/src/kmc-kmn/src/compiler/messages.ts

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { CompilerErrorNamespace, CompilerErrorSeverity, CompilerEvent, CompilerMessageSpec as m, compilerExceptionToString as exc } from "@keymanapp/common-types";
1+
import { CompilerErrorNamespace, CompilerErrorSeverity, CompilerEvent, CompilerMessageSpec as m } from "@keymanapp/common-types";
22

33
const Namespace = CompilerErrorNamespace.KmnCompiler;
44
const SevInfo = CompilerErrorSeverity.Info | Namespace;
@@ -46,20 +46,21 @@ export const enum KmnCompilerMessageRanges {
4646
are reserved for kmcmplib messages.
4747
*/
4848
export class CompilerMessages {
49-
static Fatal_UnexpectedException = (o:{e: any}) => m(this.FATAL_UnexpectedException, `Unexpected exception: ${exc(o.e)}`);
49+
static Fatal_UnexpectedException = (o:{e: any}) => m(this.FATAL_UnexpectedException, null, o.e ?? 'unknown error');
5050
static FATAL_UnexpectedException = SevFatal | 0x900;
5151

52-
static Fatal_MissingWasmModule = (o:{e?: any}) => m(this.FATAL_MissingWasmModule, `Could not instantiate WASM compiler module or initialization failed: ${exc(o.e)}`);
52+
static Fatal_MissingWasmModule = (o:{e?: any}) => m(this.FATAL_MissingWasmModule,
53+
`Could not instantiate WASM compiler module or initialization failed`, o.e ?? 'unknown error');
5354
static FATAL_MissingWasmModule = SevFatal | 0x901;
5455

5556
// TODO: Is this now deprecated?
56-
static Fatal_UnableToSetCompilerOptions = () => m(this.FATAL_UnableToSetCompilerOptions, `Unable to set compiler options`);
57+
static Fatal_UnableToSetCompilerOptions = () => m(this.FATAL_UnableToSetCompilerOptions, null, `Unable to set compiler options`);
5758
static FATAL_UnableToSetCompilerOptions = SevFatal | 0x902;
5859

59-
static Fatal_CallbacksNotSet = () => m(this.FATAL_CallbacksNotSet, `Callbacks were not set with init`);
60+
static Fatal_CallbacksNotSet = () => m(this.FATAL_CallbacksNotSet, null, `Callbacks were not set with init`);
6061
static FATAL_CallbacksNotSet = SevFatal | 0x903;
6162

62-
static Fatal_UnicodeSetOutOfRange = () => m(this.FATAL_UnicodeSetOutOfRange, `UnicodeSet buffer was too small`);
63+
static Fatal_UnicodeSetOutOfRange = () => m(this.FATAL_UnicodeSetOutOfRange, null, `UnicodeSet buffer was too small`);
6364
static FATAL_UnicodeSetOutOfRange = SevFatal | 0x904;
6465

6566
static Error_UnicodeSetHasStrings = () => m(this.ERROR_UnicodeSetHasStrings, `UnicodeSet contains strings, not allowed`);
@@ -72,19 +73,19 @@ export class CompilerMessages {
7273
static ERROR_UnicodeSetSyntaxError = SevError | 0x907;
7374

7475
static Error_InvalidKvksFile = (o:{filename: string, e: any}) => m(this.ERROR_InvalidKvksFile,
75-
`Error encountered parsing ${o.filename}: ${o.e}`);
76+
`Error encountered parsing ${o.filename}: ${o.e ?? 'unknown error'}`); // Note, not fatal, not reporting to Sentry
7677
static ERROR_InvalidKvksFile = SevError | 0x908;
7778

7879
static Warn_InvalidVkeyInKvksFile = (o:{filename: string, invalidVkey: string}) => m(this.WARN_InvalidVkeyInKvksFile,
7980
`Invalid virtual key ${o.invalidVkey} found in ${o.filename}`);
8081
static WARN_InvalidVkeyInKvksFile = SevWarn | 0x909;
8182

8283
static Error_InvalidDisplayMapFile = (o:{filename: string, e: any}) => m(this.ERROR_InvalidDisplayMapFile,
83-
`Error encountered parsing display map ${o.filename}: ${o.e}`);
84+
`Error encountered parsing display map ${o.filename}: ${o.e ?? 'unknown error'}`); // Note, not fatal, not reporting to Sentry
8485
static ERROR_InvalidDisplayMapFile = SevError | 0x90A;
8586

8687
static Error_InvalidKvkFile = (o:{filename: string, e: any}) => m(this.ERROR_InvalidKvkFile,
87-
`Error encountered loading ${o.filename}: ${o.e}`);
88+
`Error encountered loading ${o.filename}: ${o.e ?? 'unknown error'}`); // Note, not fatal, not reporting to Sentry
8889
static ERROR_InvalidKvkFile = SevError | 0x90B;
8990
};
9091

developer/src/kmc-ldml/src/compiler/messages.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ export class CompilerMessages {
6060
static ERROR_MustBeAtLeastOneLayerElement = SevError | 0x000E;
6161

6262
static Fatal_SectionCompilerFailed = (o:{sect: string}) =>
63-
m(this.FATAL_SectionCompilerFailed, `The compiler for '${o.sect}' failed unexpectedly.`);
63+
m(this.FATAL_SectionCompilerFailed, null, `The compiler for '${o.sect}' failed unexpectedly.`);
6464
static FATAL_SectionCompilerFailed = SevFatal | 0x000F;
6565

6666
static Error_DisplayIsRepeated = (o:{to: string}) =>

developer/src/kmc-model-info/src/messages.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@ const SevError = CompilerErrorSeverity.Error | Namespace;
88
const SevFatal = CompilerErrorSeverity.Fatal | Namespace;
99

1010
export class ModelInfoCompilerMessages {
11-
static Fatal_UnexpectedException = (o:{e: any}) => m(this.FATAL_UnexpectedException,
12-
`Unexpected exception: ${(o.e ?? 'unknown error').toString()}\n\nCall stack:\n${(o.e instanceof Error ? o.e.stack : (new Error()).stack)}`);
11+
static Fatal_UnexpectedException = (o:{e: any}) => m(this.FATAL_UnexpectedException, null, o.e ?? 'unknown error');
1312
static FATAL_UnexpectedException = SevFatal | 0x0001;
1413

1514
static Error_FileDoesNotExist = (o:{filename: string}) => m(this.ERROR_FileDoesNotExist, `File ${o.filename} does not exist.`);

developer/src/kmc-model/src/model-compiler-errors.ts

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { CompilerErrorNamespace, CompilerErrorSeverity, CompilerEvent } from "@keymanapp/common-types";
1+
import { CompilerErrorNamespace, CompilerErrorSeverity, CompilerEvent, CompilerMessageSpec } from "@keymanapp/common-types";
22

33
const Namespace = CompilerErrorNamespace.ModelCompiler;
44
// const SevInfo = CompilerErrorSeverity.Info | Namespace;
@@ -7,12 +7,11 @@ const SevWarn = CompilerErrorSeverity.Warn | Namespace;
77
const SevError = CompilerErrorSeverity.Error | Namespace;
88
const SevFatal = CompilerErrorSeverity.Fatal | Namespace;
99

10-
const m = (code: number, message: string) : CompilerEvent => { return {
10+
const m = (code: number, message: string, exceptionVar?: any) : CompilerEvent => ({
11+
...CompilerMessageSpec(code, message, exceptionVar),
1112
line: ModelCompilerMessageContext.line,
1213
filename: ModelCompilerMessageContext.filename,
13-
code,
14-
message
15-
} };
14+
});
1615

1716
export class ModelCompilerMessageContext {
1817
// Context added to all messages
@@ -22,8 +21,7 @@ export class ModelCompilerMessageContext {
2221

2322
export class ModelCompilerMessages {
2423

25-
static Fatal_UnexpectedException = (o:{e: any}) => m(this.FATAL_UnexpectedException,
26-
`Unexpected exception: ${(o.e ?? 'unknown error').toString()}\n\nCall stack:\n${(o.e instanceof Error ? o.e.stack : (new Error()).stack)}`);
24+
static Fatal_UnexpectedException = (o:{e: any}) => m(this.FATAL_UnexpectedException, null, o.e ?? 'unknown error');
2725
static FATAL_UnexpectedException = SevFatal | 0x0001;
2826

2927
static Warn_MixedNormalizationForms = (o:{wordform: string}) => m(this.WARN_MixedNormalizationForms,

developer/src/kmc-package/src/compiler/messages.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@ const SevError = CompilerErrorSeverity.Error | Namespace;
88
const SevFatal = CompilerErrorSeverity.Fatal | Namespace;
99

1010
export class CompilerMessages {
11-
static Fatal_UnexpectedException = (o:{e: any}) => m(this.FATAL_UnexpectedException,
12-
`Unexpected exception: ${(o.e ?? 'unknown error').toString()}\n\nCall stack:\n${(o.e instanceof Error ? o.e.stack : (new Error()).stack)}`);
11+
static Fatal_UnexpectedException = (o:{e: any}) => m(this.FATAL_UnexpectedException, null, o.e ?? 'unknown error');
1312
static FATAL_UnexpectedException = SevFatal | 0x0001;
1413

1514
static Warn_AbsolutePath = (o:{filename: string}) => m(this.WARN_AbsolutePath, `File ${o.filename} has an absolute path, which is not portable.`);

developer/src/kmc/src/commands/analyze.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import * as fs from 'fs';
22
import * as path from 'path';
33
import { Command, Option } from 'commander';
44
import { NodeCompilerCallbacks } from '../util/NodeCompilerCallbacks.js';
5-
import { InfrastructureMessages } from '../messages/messages.js';
5+
import { InfrastructureMessages } from '../messages/infrastructureMessages.js';
66
import { CompilerCallbacks, CompilerLogLevel } from '@keymanapp/common-types';
77
import { AnalyzeOskCharacterUse, AnalyzeOskRewritePua } from '@keymanapp/kmc-analyze';
88
import { BaseOptions } from '../util/baseOptions.js';

developer/src/kmc/src/commands/build.ts

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { Command } from 'commander';
44
import { buildActivities } from './buildClasses/buildActivities.js';
55
import { BuildProject } from './buildClasses/BuildProject.js';
66
import { NodeCompilerCallbacks } from '../util/NodeCompilerCallbacks.js';
7-
import { InfrastructureMessages } from '../messages/messages.js';
7+
import { InfrastructureMessages } from '../messages/infrastructureMessages.js';
88
import { CompilerFileCallbacks, CompilerOptions, KeymanFileTypes } from '@keymanapp/common-types';
99
import { BaseOptions } from '../util/baseOptions.js';
1010
import { expandFileLists } from '../util/fileLists.js';
@@ -80,13 +80,18 @@ If no input file is supplied, kmc will build the current folder.`)
8080
}
8181

8282
async function build(filename: string, parentCallbacks: NodeCompilerCallbacks, options: CompilerOptions): Promise<boolean> {
83+
try {
84+
// TEST: allow command-line simulation of infrastructure fatal errors, and
85+
// also for unit tests
86+
if(process.env.SENTRY_CLIENT_TEST_BUILD_EXCEPTION == '1') {
87+
throw new Error('Test exception from SENTRY_CLIENT_TEST_BUILD_EXCEPTION');
88+
}
8389

84-
if(!fs.existsSync(filename)) {
85-
parentCallbacks.reportMessage(InfrastructureMessages.Error_FileDoesNotExist({filename}));
86-
return false;
87-
}
90+
if(!fs.existsSync(filename)) {
91+
parentCallbacks.reportMessage(InfrastructureMessages.Error_FileDoesNotExist({filename}));
92+
return false;
93+
}
8894

89-
try {
9095
let builder = null;
9196

9297
// If infile is a directory, then we treat that as a project and build it
@@ -136,3 +141,10 @@ async function build(filename: string, parentCallbacks: NodeCompilerCallbacks, o
136141
return false;
137142
}
138143
}
144+
145+
/**
146+
* these are exported only for unit tests, do not use
147+
*/
148+
export const unitTestEndpoints = {
149+
build
150+
};

0 commit comments

Comments
 (0)