Skip to content
This repository was archived by the owner on Nov 5, 2025. It is now read-only.

Commit 812a6ed

Browse files
committed
make Deno capture unhandled exceptions and rejections and report them to the server
RocketChat/Rocket.Chat#33997
1 parent 09e1141 commit 812a6ed

File tree

4 files changed

+56
-0
lines changed

4 files changed

+56
-0
lines changed

deno-runtime/error-handlers.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import * as Messenger from './lib/messenger.ts';
2+
3+
export function unhandledRejectionListener(event: PromiseRejectionEvent) {
4+
event.preventDefault();
5+
6+
const { type, reason } = event;
7+
8+
Messenger.sendNotification({
9+
method: 'unhandledRejection',
10+
params: [
11+
{
12+
type,
13+
reason: reason instanceof Error ? reason.message : reason,
14+
timestamp: new Date(),
15+
},
16+
],
17+
});
18+
}
19+
20+
export function unhandledExceptionListener(event: ErrorEvent) {
21+
event.preventDefault();
22+
23+
const { type, message, filename, lineno, colno } = event;
24+
Messenger.sendNotification({
25+
method: 'uncaughtException',
26+
params: [{ type, message, filename, lineno, colno }],
27+
});
28+
}
29+
30+
export default function registerErrorListeners() {
31+
addEventListener('unhandledrejection', unhandledRejectionListener);
32+
addEventListener('error', unhandledExceptionListener);
33+
}

deno-runtime/main.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import videoConferenceHandler from './handlers/videoconference-handler.ts';
2121
import apiHandler from './handlers/api-handler.ts';
2222
import handleApp from './handlers/app/handler.ts';
2323
import handleScheduler from './handlers/scheduler-handler.ts';
24+
import registerErrorListeners from './error-handlers.ts';
2425

2526
type Handlers = {
2627
app: typeof handleApp;
@@ -126,4 +127,6 @@ async function main() {
126127
}
127128
}
128129

130+
registerErrorListeners();
131+
129132
main();

src/definition/metadata/AppMethod.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,4 +101,6 @@ export enum AppMethod {
101101
EXECUTE_POST_USER_STATUS_CHANGED = 'executePostUserStatusChanged',
102102
// Runtime specific methods
103103
RUNTIME_RESTART = 'runtime:restart',
104+
RUNTIME_UNCAUGHT_EXCEPTION = 'runtime:uncaughtException',
105+
RUNTIME_UNHANDLED_REJECTION = 'runtime:unhandledRejection',
104106
}

src/server/runtime/deno/AppsEngineDenoRuntime.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import type { IParseAppPackageResult } from '../../compiler';
1313
import { AppConsole, type ILoggerStorageEntry } from '../../logging';
1414
import type { AppAccessorManager, AppApiManager } from '../../managers';
1515
import { AppStatus } from '../../../definition/AppStatus';
16+
import type { AppMethod } from '../../../definition/metadata';
1617
import { bundleLegacyApp } from './bundler';
1718
import { ProcessMessenger } from './ProcessMessenger';
1819
import { LivenessManager } from './LivenessManager';
@@ -535,12 +536,29 @@ export class DenoRuntimeSubprocessController extends EventEmitter {
535536
case 'log':
536537
console.log('SUBPROCESS LOG', message);
537538
break;
539+
case 'unhandledRejection':
540+
case 'uncaughtException':
541+
await this.logUnhandledError(`runtime:${method}`, message);
542+
break;
543+
538544
default:
539545
console.warn('Unrecognized method from sub process');
540546
break;
541547
}
542548
}
543549

550+
private async logUnhandledError(
551+
method: `${AppMethod.RUNTIME_UNCAUGHT_EXCEPTION | AppMethod.RUNTIME_UNHANDLED_REJECTION}`,
552+
message: jsonrpc.IParsedObjectRequest | jsonrpc.IParsedObjectNotification,
553+
) {
554+
this.debug('Unhandled error of type "%s" caught in subprocess', method);
555+
556+
const logger = new AppConsole(method);
557+
logger.error(message.payload);
558+
559+
await this.logStorage.storeEntries(AppConsole.toStorageEntry(this.getAppId(), logger));
560+
}
561+
544562
private async handleResultMessage(message: jsonrpc.IParsedObjectError | jsonrpc.IParsedObjectSuccess): Promise<void> {
545563
const { id } = message.payload;
546564

0 commit comments

Comments
 (0)