Hoist uncaughtException handler to the top of workers to better report error messages

This commit is contained in:
Eric Allam
2024-04-29 14:22:55 +01:00
parent 4fdb7f8288
commit 43bc7ed94e
6 changed files with 52 additions and 51 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"trigger.dev": patch
---
Hoist uncaughtException handler to the top of workers to better report error messages
+10
View File
@@ -202,6 +202,13 @@ async function _deployCommand(dir: string, options: DeployCommandOptions) {
projectRef: options.projectRef,
});
if (resolvedConfig.status === "error") {
logger.error("Failed to read config:", resolvedConfig.error);
span && recordSpanException(span, resolvedConfig.error);
throw new SkipLoggingError("Failed to read config");
}
logger.debug("Resolved config", { resolvedConfig });
span?.setAttributes({
@@ -1126,6 +1133,9 @@ async function compileProject(
format: "cjs", // This is needed to support opentelemetry instrumentation that uses module patching
target: ["node18", "es2020"],
outdir: "out",
banner: {
js: `process.on("uncaughtException", function(error, origin) { if (error instanceof Error) { process.send && process.send({ type: "EVENT", message: { type: "UNCAUGHT_EXCEPTION", payload: { error: { name: error.name, message: error.message, stack: error.stack }, origin }, version: "v1" } }); } else { process.send && process.send({ type: "EVENT", message: { type: "UNCAUGHT_EXCEPTION", payload: { error: { name: "Error", message: typeof error === "string" ? error : JSON.stringify(error) }, origin }, version: "v1" } }); } });`,
},
define: {
TRIGGER_API_URL: `"${config.triggerUrl}"`,
__PROJECT_CONFIG__: JSON.stringify(config),
+13 -5
View File
@@ -153,6 +153,11 @@ async function startDev(
logger.debug("Initial config", { config });
if (config.status === "error") {
logger.error("Failed to read config", config.error);
process.exit(1);
}
async function getDevReactElement(
configParam: ResolvedConfig,
authorization: { apiUrl: string; accessToken: string },
@@ -164,18 +169,18 @@ async function startDev(
apiClient = new CliApiClient(apiUrl, accessToken);
const devEnv = await apiClient.getProjectEnv({
projectRef: config.config.project,
projectRef: configParam.project,
env: "dev",
});
if (!devEnv.success) {
if (devEnv.error === "Project not found") {
logger.error(
`Project not found: ${config.config.project}. Ensure you are using the correct project ref and CLI profile (use --profile). Currently using the "${options.profile}" profile, which points to ${authorization.apiUrl}`
`Project not found: ${configParam.project}. Ensure you are using the correct project ref and CLI profile (use --profile). Currently using the "${options.profile}" profile, which points to ${authorization.apiUrl}`
);
} else {
logger.error(
`Failed to initialize dev environment: ${devEnv.error}. Using project ref ${config.config.project}`
`Failed to initialize dev environment: ${devEnv.error}. Using project ref ${configParam.project}`
);
}
@@ -388,6 +393,9 @@ function useDev({
resolveDir: process.cwd(),
sourcefile: "__entryPoint.ts",
},
banner: {
js: `process.on("uncaughtException", function(error, origin) { if (error instanceof Error) { process.send && process.send({ type: "UNCAUGHT_EXCEPTION", payload: { error: { name: error.name, message: error.message, stack: error.stack }, origin }, version: "v1" }); } else { process.send && process.send({ type: "UNCAUGHT_EXCEPTION", payload: { error: { name: "Error", message: typeof error === "string" ? error : JSON.stringify(error) }, origin }, version: "v1" }); } });`,
},
bundle: true,
metafile: true,
write: false,
@@ -602,10 +610,10 @@ function useDev({
} else {
}
if (e.originalError.stack) {
if (e.originalError.message || e.originalError.stack) {
logger.log(
`${chalkError("X Error:")} Worker failed to start`,
e.originalError.stack
e.originalError.stack ?? e.originalError.message
);
}
+24 -13
View File
@@ -126,6 +126,10 @@ export type ReadConfigResult =
| {
status: "in-memory";
config: ResolvedConfig;
}
| {
status: "error";
error: unknown;
};
export async function readConfig(
@@ -182,22 +186,29 @@ export async function readConfig(
],
});
// import the config file
const userConfigModule = await import(builtConfigFileHref);
try {
// import the config file
const userConfigModule = await import(builtConfigFileHref);
// The --project-ref CLI arg will always override the project specified in the config file
const rawConfig = await normalizeConfig(
userConfigModule?.config,
options?.projectRef ? { project: options?.projectRef } : undefined
);
// The --project-ref CLI arg will always override the project specified in the config file
const rawConfig = await normalizeConfig(
userConfigModule?.config,
options?.projectRef ? { project: options?.projectRef } : undefined
);
const config = Config.parse(rawConfig);
const config = Config.parse(rawConfig);
return {
status: "file",
config: await resolveConfig(absoluteDir, config),
path: configPath,
};
return {
status: "file",
config: await resolveConfig(absoluteDir, config),
path: configPath,
};
} catch (error) {
return {
status: "error",
error,
};
}
}
export async function resolveConfig(path: string, config: Config): Promise<ResolvedConfig> {
@@ -33,19 +33,4 @@ export const sender = new ZodMessageSender({
},
});
process.on("uncaughtException", (error, origin) => {
sender
.send("UNCAUGHT_EXCEPTION", {
error: {
name: error.name,
message: error.message,
stack: error.stack,
},
origin,
})
.catch((err) => {
console.error("Failed to send UNCAUGHT_EXCEPTION message", err);
});
});
taskCatalog.setGlobalTaskCatalog(new StandardTaskCatalog());
@@ -19,22 +19,4 @@ export const tracingSDK = new TracingSDK({
diagLogLevel: (process.env.OTEL_LOG_LEVEL as TracingDiagnosticLogLevel) ?? "none",
});
process.on("uncaughtException", (error, origin) => {
process.send?.({
type: "EVENT",
message: {
type: "UNCAUGHT_EXCEPTION",
payload: {
error: {
name: error.name,
message: error.message,
stack: error.stack,
},
origin,
},
version: "v1",
},
});
});
taskCatalog.setGlobalTaskCatalog(new StandardTaskCatalog());