Skip to content

Commit

Permalink
fix: exit process on unhandledRejection (#950)
Browse files Browse the repository at this point in the history
  • Loading branch information
subzero10 authored Nov 26, 2022
1 parent 2a4dd4e commit ffff051
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 19 deletions.
3 changes: 2 additions & 1 deletion packages/js/rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ export default [
external: ['http', 'https', 'url', 'os', 'fs', 'util', 'domain', 'async_hooks'],
output: {
file: pkg.main,
format: 'cjs'
format: 'cjs',
sourcemap: true
},
plugins: sharedPlugins
}
Expand Down
59 changes: 45 additions & 14 deletions packages/js/src/server/integrations/uncaught_exception.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import { fatallyLogAndExit } from '../util'
import Client from '../../server'
import { removeAwsDefaultUncaughtExceptionListener } from '../aws_lambda'

let count = 0
let isReporting = false
let handlerAlreadyCalled = false

function removeAwsLambdaListener() {
const isLambda = !!process.env.LAMBDA_TASK_ROOT
Expand All @@ -14,28 +15,58 @@ function removeAwsLambdaListener() {
removeAwsDefaultUncaughtExceptionListener()
}

/**
* If there are no other uncaughtException listeners,
* we want to report the exception to Honeybadger and
* mimic the default behavior of NodeJs,
* which is to exit the process with code 1
*/
function hasOtherUncaughtExceptionListeners() {
return process.listeners('uncaughtException').length > 1
}

export default function (): Types.Plugin {
return {
load: (client: typeof Client) => {
if (!client.config.enableUncaught) { return }
if (!client.config.enableUncaught) {
return
}

removeAwsLambdaListener()


process.on('uncaughtException', function honeybadgerUncaughtExceptionListener(uncaughtError) {
// Prevent recursive errors
if (count > 1) { fatallyLogAndExit(uncaughtError) }

if (client.config.enableUncaught) {
client.notify(uncaughtError, {
afterNotify: (_err, _notice) => {
count += 1
client.config.afterUncaught(uncaughtError)
}
})
} else {
count += 1
if (!client.config.enableUncaught) {
client.config.afterUncaught(uncaughtError)
if (!hasOtherUncaughtExceptionListeners()) {
fatallyLogAndExit(uncaughtError)
}
return
}

// report only the first error - prevent reporting recursive errors
if (handlerAlreadyCalled) {
if (!hasOtherUncaughtExceptionListeners()) {
fatallyLogAndExit(uncaughtError)
}
return
}

if (isReporting) {
return
}

isReporting = true
client.notify(uncaughtError, {
afterNotify: (_err, _notice) => {
isReporting = false
handlerAlreadyCalled = true
client.config.afterUncaught(uncaughtError)
if (!hasOtherUncaughtExceptionListeners()) {
fatallyLogAndExit(uncaughtError)
}
}
})
})
}
}
Expand Down
37 changes: 33 additions & 4 deletions packages/js/src/server/integrations/unhandled_rejection.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,43 @@
import { Types } from '@honeybadger-io/core'
import Client from '../../server'
import { fatallyLogAndExit } from '../util'

let isReporting = false

/**
* If there are no other unhandledRejection listeners,
* we want to report the exception to Honeybadger and
* mimic the default behavior of NodeJs,
* which is to exit the process with code 1
*/
function hasOtherUnhandledRejectionListeners() {
return process.listeners('unhandledRejection').length > 1
}

export default function (): Types.Plugin {
return {
load: (client: typeof Client) => {
if (!client.config.enableUnhandledRejection) { return }
if (!client.config.enableUnhandledRejection) {
return
}

process.on('unhandledRejection', function honeybadgerUnhandledRejectionListener(reason, _promise) {
if (!client.config.enableUnhandledRejection) {
if (!hasOtherUnhandledRejectionListeners() && !isReporting) {
fatallyLogAndExit(reason as Error)
}
return
}

process.on('unhandledRejection', function (reason, _promise) {
if (!client.config.enableUnhandledRejection) { return }
client.notify(reason as Types.Noticeable, { component: 'unhandledRejection' })
isReporting = true;
client.notify(reason as Types.Noticeable, { component: 'unhandledRejection' }, {
afterNotify: () => {
isReporting = false;
if (!hasOtherUnhandledRejectionListeners()) {
fatallyLogAndExit(reason as Error)
}
}
})
})
}
}
Expand Down
1 change: 1 addition & 0 deletions tsconfig.base.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"noImplicitAny": false,
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"stripInternal": true,
"skipLibCheck": true
}
Expand Down

0 comments on commit ffff051

Please sign in to comment.