Skip to content

Commit

Permalink
Merge pull request #1924 from bugsnag/contextualize
Browse files Browse the repository at this point in the history
rewrite plugin-contextualize without domain
  • Loading branch information
djskinner authored Mar 8, 2023
2 parents 034c736 + a18b793 commit 630d9ca
Show file tree
Hide file tree
Showing 7 changed files with 64 additions and 138 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
### Changed

- (plugin-navigation-breadcrumbs) calling `pushState` or `replaceState` no longer triggers a new session when `autoTrackSessions` is enabled [#1820](https://github.com/bugsnag/bugsnag-js/pull/1820)
- (plugin-contextualize) reimplement without relying on the deprecated node Domain API. From Node 16+ unhandled promise rejections are also supported [#1924](https://github.com/bugsnag/bugsnag-js/pull/1924)

## TBD

Expand Down
30 changes: 12 additions & 18 deletions packages/plugin-contextualize/contextualize.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
/* eslint node/no-deprecated-api: [error, {ignoreModuleItems: ["domain"]}] */
const domain = require('domain')
const { getStack, maybeUseFallbackStack } = require('@bugsnag/core/lib/node-fallback-stack')
const { getStack } = require('@bugsnag/core/lib/node-fallback-stack')
const clone = require('@bugsnag/core/lib/clone-client')

module.exports = {
name: 'contextualize',
Expand All @@ -9,21 +8,16 @@ module.exports = {
// capture a stacktrace in case a resulting error has nothing
const fallbackStack = getStack()

const dom = domain.create()
dom.on('error', err => {
// check if the stacktrace has no context, if so, if so append the frames we created earlier
if (err.stack) maybeUseFallbackStack(err, fallbackStack)
const event = client.Event.create(err, true, {
severity: 'error',
unhandled: true,
severityReason: { type: 'unhandledException' }
}, 'contextualize()', 1)
client._notify(event, onError, (e, event) => {
if (e) client._logger.error('Failed to send event to Bugsnag')
client._config.onUncaughtException(err, event, client._logger)
})
})
process.nextTick(() => dom.run(fn))
const clonedClient = clone(client)

// add the stacktrace to the cloned client so it can be used later
// by the uncaught exception handler. Note the unhandled rejection
// handler does not need this because it gets a stacktrace
clonedClient.fallbackStack = fallbackStack

clonedClient.addOnError(onError)

client._clientContext.run(clonedClient, fn)
}

return contextualize
Expand Down
119 changes: 0 additions & 119 deletions packages/plugin-contextualize/test/contextualize.test.ts

This file was deleted.

6 changes: 6 additions & 0 deletions packages/plugin-node-uncaught-exception/uncaught-exception.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
const { maybeUseFallbackStack } = require('@bugsnag/core/lib/node-fallback-stack')

let _handler
module.exports = {
load: client => {
Expand All @@ -7,6 +9,10 @@ module.exports = {
// if we are in an async context, use the client from that context
const c = (client._clientContext && client._clientContext.getStore()) ? client._clientContext.getStore() : client

// check if the stacktrace has no context, if so, if so append the frames we created earlier
// see plugin-contextualize for where this is created
if (err.stack && c.fallbackStack) maybeUseFallbackStack(err, c.fallbackStack)

const event = c.Event.create(err, false, {
severity: 'error',
unhandled: true,
Expand Down
2 changes: 1 addition & 1 deletion test/aws-lambda/features/promise-rejection.feature
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ Scenario Outline: unhandled promise rejections are not reported when autoDetectE
When I invoke the "<lambda>" lambda in "features/fixtures/simple-app" with the "events/<type>/promise-rejection.json" event
Then the lambda response "errorMessage" equals "Error: yikes"
And the lambda response "errorType" equals "Runtime.UnhandledPromiseRejection"
And the lambda response "trace" is an array with 6 elements
And the lambda response "trace" is an array with 5 elements
And the lambda response "trace.0" equals "Runtime.UnhandledPromiseRejection: Error: yikes"
And the lambda response "body" is null
And the lambda response "statusCode" is null
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
var Bugsnag = require('@bugsnag/node')
Bugsnag.start({
apiKey: process.env.BUGSNAG_API_KEY,
endpoints: {
notify: process.env.BUGSNAG_NOTIFY_ENDPOINT,
sessions: process.env.BUGSNAG_SESSIONS_ENDPOINT
}
})

var contextualize = Bugsnag.getPlugin('contextualize')
contextualize(function () {
Promise.reject(new Error('unhandled rejection'))
}, function (event) {
event.addMetadata('subsystem', { name: 'fs reader', widgetsAdded: 'cat,dog,mouse' })
})
29 changes: 29 additions & 0 deletions test/node/features/unhandled_errors.feature
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,35 @@ Scenario: using contextualize to add context to an error
And the event "metaData.subsystem.name" equals "fs reader"
And the event "metaData.subsystem.widgetsAdded" equals "cat,dog,mouse"

@skip_before_node_16
Scenario: using contextualize with an unhandled rejection (with context added)
And I run the service "unhandled" with the command "node scenarios/contextualize-unhandled-rejection"
And I wait to receive an error
Then the error is valid for the error reporting API version "4" for the "Bugsnag Node" notifier
And the event "unhandled" is true
And the event "severity" equals "error"
And the event "severityReason.type" equals "unhandledPromiseRejection"
And the exception "errorClass" equals "Error"
And the exception "message" equals "unhandled rejection"
And the exception "type" equals "nodejs"
And the "file" of stack frame 0 equals "scenarios/contextualize-unhandled-rejection.js"
And the "lineNumber" of stack frame 0 equals 12
And the event "metaData.subsystem.name" equals "fs reader"
And the event "metaData.subsystem.widgetsAdded" equals "cat,dog,mouse"

Scenario: using contextualize with an unhandled rejection (no context added)
And I run the service "unhandled" with the command "node scenarios/contextualize-unhandled-rejection"
And I wait to receive an error
Then the error is valid for the error reporting API version "4" for the "Bugsnag Node" notifier
And the event "unhandled" is true
And the event "severity" equals "error"
And the event "severityReason.type" equals "unhandledPromiseRejection"
And the exception "errorClass" equals "Error"
And the exception "message" equals "unhandled rejection"
And the exception "type" equals "nodejs"
And the "file" of stack frame 0 equals "scenarios/contextualize-unhandled-rejection.js"
And the "lineNumber" of stack frame 0 equals 12

Scenario: overridden handled state in a callback
And I run the service "unhandled" with the command "node scenarios/modify-unhandled-callback"
And I wait to receive an error
Expand Down

0 comments on commit 630d9ca

Please sign in to comment.