Skip to content

Commit ebb1fd7

Browse files
committed
feat(workers): add decorator to catch unhandled exceptions and report them to Hawk with worker type context
1 parent f4342de commit ebb1fd7

File tree

14 files changed

+62
-4
lines changed

14 files changed

+62
-4
lines changed

lib/utils/catchAndReport.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import HawkCatcher from "@hawk.so/nodejs";
2+
import { Worker } from "../worker";
3+
4+
/**
5+
* Catches unhandled exceptions from the decorated method, sends them to HawkCatcher
6+
* with the current worker’s type, and re‑throws.
7+
*/
8+
export function catchAndReport(): MethodDecorator {
9+
return function (_target, propertyKey, descriptor) {
10+
const original = descriptor.value;
11+
12+
if (typeof original !== "function") {
13+
throw new Error(
14+
`@catchAndReport can only be applied to methods (${String(propertyKey)})`
15+
)
16+
}
17+
18+
descriptor.value = async function (...args: any[]) {
19+
try {
20+
return await original.apply(this, args);
21+
} catch (error) {
22+
HawkCatcher.send(error, {
23+
workerType: (this as Worker).type
24+
});
25+
throw error;
26+
}
27+
} as typeof original;
28+
}
29+
};

runner.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { EventContext } from '@hawk.so/types';
12
import * as utils from './lib/utils';
23

34
/* Prometheus client for pushing metrics to the pushgateway */
@@ -192,7 +193,9 @@ class WorkerRunner {
192193

193194
utils.sendReport(worker.constructor.name + ' started');
194195
} catch (startingError) {
195-
this.exceptionHandler(startingError);
196+
this.exceptionHandler(startingError, {
197+
workerType: worker.type
198+
});
196199

197200
utils.sendReport(worker.constructor.name + ' failed to start');
198201

@@ -209,8 +212,8 @@ class WorkerRunner {
209212
*
210213
* @param error - error to handle
211214
*/
212-
private exceptionHandler(error: Error): void {
213-
HawkCatcher.send(error);
215+
private exceptionHandler(error: Error, context?: EventContext): void {
216+
HawkCatcher.send(error, context);
214217

215218
console.log(
216219
'\x1b[41m%s\x1b[0m',
@@ -285,7 +288,9 @@ class WorkerRunner {
285288
`\n\n Worker ${worker.constructor.name} stopped \n`
286289
);
287290
} catch (finishingError) {
288-
HawkCatcher.send(finishingError);
291+
HawkCatcher.send(finishingError, {
292+
workerType: worker.type
293+
});
289294
console.error('Error while finishing Worker: ', finishingError);
290295
}
291296
}

workers/accountant/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { Worker } from '../../../lib/worker';
55
import * as pkg from '../package.json';
66
import { AccountantEvent, EventType, IncomeTransactionPayload, TransactionEvent, TransactionType } from '../types/accountant-worker-events';
77
import { WorkspaceDBScheme } from '@hawk.so/types';
8+
import { catchAndReport } from '../../../lib/utils/catchAndReport';
89

910
/**
1011
* Worker for managing workspaces balance
@@ -63,6 +64,7 @@ export default class AccountantWorker extends Worker {
6364
*
6465
* @param event
6566
*/
67+
@catchAndReport()
6668
public async handle(event: AccountantEvent): Promise<void> {
6769
switch (event.type) {
6870
case EventType.Transaction:

workers/archiver/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import * as dotenv from 'dotenv';
99
import prettysize from 'prettysize';
1010
import { ProjectDBScheme } from '@hawk.so/types';
1111
import { HOURS_IN_DAY, MINUTES_IN_HOUR, MS_IN_SEC, SECONDS_IN_MINUTE } from '../../../lib/utils/consts';
12+
import { catchAndReport } from '../../../lib/utils/catchAndReport';
1213

1314
dotenv.config({ path: path.resolve(__dirname, '../.env') });
1415

@@ -77,6 +78,7 @@ export default class ArchiverWorker extends Worker {
7778
/**
7879
* Task handling function
7980
*/
81+
@catchAndReport()
8082
public async handle(): Promise<void> {
8183
const dbSizeOnStart = (await this.eventsDbConnection.stats()).dataSize;
8284

workers/default/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { ErrorsCatcherType } from '@hawk.so/types';
22
import { EventWorker } from '../../../lib/event-worker';
33
import * as pkg from '../package.json';
44
import { DefaultEventWorkerTask } from '../types/default-event-worker-task';
5+
import { catchAndReport } from '../../../lib/utils/catchAndReport';
56

67
/**
78
* Worker for handling Default events
@@ -17,6 +18,7 @@ export default class DefaultEventWorker extends EventWorker {
1718
*
1819
* @param event - event to handle
1920
*/
21+
@catchAndReport()
2022
public async handle(event: DefaultEventWorkerTask): Promise<void> {
2123
/**
2224
* Define event type

workers/grouper/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import { hasValue } from '../../../lib/utils/hasValue';
3131
*/
3232
/* eslint-disable-next-line no-unused-vars */
3333
import { memoize } from '../../../lib/memoize';
34+
import { catchAndReport } from '../../../lib/utils/catchAndReport';
3435

3536
/**
3637
* eslint does not count decorators as a variable usage
@@ -136,6 +137,7 @@ export default class GrouperWorker extends Worker {
136137
*
137138
* @param task - event to handle
138139
*/
140+
@catchAndReport()
139141
public async handle(task: GroupWorkerTask<ErrorsCatcherType>): Promise<void> {
140142
let uniqueEventHash = await this.getUniqueEventHash(task);
141143

workers/javascript/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { beautifyUserAgent, getFunctionContext } from './utils';
1414
import { Collection } from 'mongodb';
1515
/* eslint-disable-next-line no-unused-vars */
1616
import { memoize } from '../../../lib/memoize';
17+
import { catchAndReport } from '../../../lib/utils/catchAndReport';
1718

1819
/**
1920
* eslint does not count decorators as a variable usage
@@ -70,6 +71,7 @@ export default class JavascriptEventWorker extends EventWorker {
7071
*
7172
* @param event - event to handle
7273
*/
74+
@catchAndReport()
7375
public async handle(event: JavaScriptEventWorkerTask): Promise<void> {
7476
if (event.payload.release && event.payload.backtrace) {
7577
this.logger.info('beautifyBacktrace called');

workers/limiter/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { WorkspaceWithTariffPlan } from '../types';
1313
import * as WorkerNames from '../../../lib/workerNames';
1414
import { DbHelper } from './dbHelper';
1515
import * as telegram from '../../../lib/utils/telegram';
16+
import { catchAndReport } from '../../../lib/utils/catchAndReport';
1617

1718
dotenv.config({ path: path.resolve(__dirname, '../.env') });
1819

@@ -91,6 +92,7 @@ export default class LimiterWorker extends Worker {
9192
*
9293
* @param event - worker event to handle
9394
*/
95+
@catchAndReport()
9496
public async handle(event: LimiterEvent): Promise<void> {
9597
switch (event.type) {
9698
case 'regular-workspaces-check':

workers/notifier/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { SenderWorkerTask } from 'hawk-worker-sender/types/sender-task';
1111
import RuleValidator from './validator';
1212
import TimeMs from '../../../lib/utils/time';
1313
import RedisHelper from './redisHelper';
14+
import { catchAndReport } from '../../../lib/utils/catchAndReport';
1415

1516
/**
1617
* Worker to buffer events before sending notifications about them
@@ -56,6 +57,7 @@ export default class NotifierWorker extends Worker {
5657
*
5758
* @param task — notifier task to handle
5859
*/
60+
@catchAndReport()
5961
public async handle(task: NotifierWorkerTask): Promise<void> {
6062
try {
6163
const { projectId, event } = task;

workers/paymaster/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import axios from 'axios';
1010
import * as WorkerNames from '../../../lib/workerNames';
1111
import HawkCatcher from '@hawk.so/nodejs';
1212
import { countDaysBeforePayday, countDaysAfterPayday, countDaysAfterBlock } from '../../../lib/utils/payday';
13+
import { catchAndReport } from '../../../lib/utils/catchAndReport';
1314

1415
dotenv.config({
1516
path: path.resolve(__dirname, '../.env'),
@@ -136,6 +137,7 @@ export default class PaymasterWorker extends Worker {
136137
*
137138
* @param event - event to handle
138139
*/
140+
@catchAndReport()
139141
public async handle(event: PaymasterEvent): Promise<void> {
140142
switch (event.type) {
141143
case EventType.WorkspaceSubscriptionCheck:

0 commit comments

Comments
 (0)