Breadcrumbs & context wrapping broken with native promises #265
Description
openedon Feb 3, 2017
*** Maintainer edit/resolution ***
This issue is resolved on Node.js 8.0.0, thanks to the following line item of the 8.0.0 changelog:
Native Promise instances are now Domain aware
Partial workarounds have been identified for version of Node prior to 8. Recommendation:
- If you can, use Node.js 8 and this problem will disappear
- If you can't use Node.js 8, use Bluebird promises instead of native promises
- If you can't do either of those, you'll have to avoid relying on
context()
orwrap()
; you can still use the global-scope context, which may be sufficient for certain use cases like Lambda
Original issue below:
I've been pulling my hair out trying to get breadcrumbs working in my Serverless Lambda services, but I can never seem to get it working. Like not even a little bit...
I've gone over the docs multiple times, and it seems like I'm doing everything as recommended. At this point I don't know what else to try.
Background
- Code written in ES6
- Built with Webpack v1 via serverless-webpack plugin
- Compiled using babel-loader
- Auto-breadcrumbs are disabled
- Using latest v1.1.1 of
raven-node
Example Code
Here's a simplified example of one of my functions.
// Lambda function handler
import Raven from 'raven'
Raven.config('my-dsn', { /* my config */ }).install()
import MyClass from './my-class'
export const handler = Raven.wrap((event, context, callback) => {
new MyClass(event, callback).startDoingStuff()
})
// my-class.js
import Raven from 'raven'
import AWS from 'aws-sdk'
const ddbClient = new AWS.DynamoDB.DocumentClient()
export default class MyClass {
constructor (event, callback) {
this.event = event
this.callback = callback
Raven.setContext({ extra: this.event })
}
startDoingStuff () {
Raven.captureBreadcrumb({ message: 'started doing stuff' })
return this.doTheStuff()
.then(this.itWorked.bind(this))
.catch(this.itBroke.bind(this))
}
doTheStuff () {
return new Promise((resolve, reject) => {
ddbClient.query({ /* query params */ }, (error, data) => {
if (error) {
Raven.captureBreadcrumb({ message: 'lol nope' })
return reject(error)
}
resolve(data)
})
})
}
itWorked (data) {
this.callback(null, data)
}
itBroke (error) {
Raven.captureException(error, (sentryError, eventId) => {
this.callback(error)
})
}
}
Results
If something goes wrong in doTheStuff()
, the events that get logged to Sentry don't have any of the extra
context I tried setting in the constructor, and don't have any breadcrumbs other than the final exception...
Also, my Lambda logs on AWS are full of entries like raven@1.1.1 alert: getContext called without context; this may indicate incorrect setup - refer to docs on contexts
.
So it seems like a problem with the Node.js domain API. Maybe because raven-node
seems to rely on implicit binding?
I'm not really familiar with the domain API, but if a different library also tried to use domains (say maybe the AWS SDK), would changing the active domain break raven-node
since it relies on domain.active.sentryContext
? IDK if that's even how it works...
Anyway, is there a way to disable node domains all together and just run everything in a global context? Domains aren't really useful in an AWS Lambda environment IMO (also, they're deprecated).