New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[@sentry/node] AWS Lambda and other Serverless solutions support #1449
Comments
This may help I guess https://blog.sentry.io/2018/06/20/how-droplr-uses-sentry-to-debug-serverless (it's using old raven version, which had a callback, but I'm mostly pointing to a There's no official way yet, as we're still trying things out in beta, but it's doable with this code: import { init, getDefaultHub } from '@sentry/node';
init({
dsn: 'https://my-dsn.com/1337'
});
exports.myHandler = async function(event, context) {
// your code
await getDefaultHub().getClient().captureException(error, getDefaultHub().getScope());
context.fail();
} |
@kamilogorek Thank you for the pointer. I'll give it a try and play back the learnings. |
@kamilogorek You suggestion works. I'm looking forward to a more official way. |
@vietbui import { getCurrentHub } from '@sentry/node';
getCurrentHub().getClient().close(2000).then(result => {
if (!result) {
console.log('We reached the timeout for emptying the request buffer, still exiting now!');
}
global.process.exit(1);
})
|
@HazAT Nice one. Thanks for all the hard work. |
In 4.0.3 I call it like this in my lambda function:
getDefaultHub() is no longer available. |
@vietbui it's called |
@kamilogorek Thanks for the clarification. There is a problem with In the end I took a different approach as suggested by @HazAT to capture exception in my lambda functions:
And it works perfectly. |
Is this the recommended way to wait/force sentry to send events? |
This solution does not work for me for some reason. It only works the first time in production when there is a cold start time and does not work after that. here is example code 'use strict'
const Sentry = require('@sentry/node')
Sentry.init({
dsn: 'xxx',
environment: process.env.STAGE
});
module.exports.createPlaylist = async (event, context, callback) => {
context.callbackWaitsForEmptyEventLoop = false
if(!event.body) {
Sentry.captureException(error)
await new Promise(resolve => Sentry.getCurrentHub().getClient().close(2000).then(resolve))
return {
statusCode: 500,
headers: { 'Content-Type': 'text/plain' },
body: 'Missing body parameters'
}
}
return {
statusCode: 200,
}
}; |
@Andriy-Kulak Thats also stated in the docs:
So I don't know how we can handle this in lambda where we don't know when the application will be killed. Best would be to drain sentry per request like we could with the old API? |
@HazAT could we reopen this, please? I think it's important to have a way to work with this on Lambda, which is becoming an increasingly common target to deploy to. This is currently blocking me from upgrading to the latest version... Personally, I would prefer being able to get a Promise/callback when reporting an error. Having a way to drain the queue without actually closing it afterward would be the next best thing... What was the rationale of removing the callback from |
@albinekb it does not work at all if I remove the following line await new Promise(resolve => Sentry.getCurrentHub().getClient().close(2000).then(resolve)) @LinusU what is the solution and sentry or raven solution are you using? For me basically the following works with 'use strict'
const Sentry = require('@sentry/node')
Sentry.init({
dsn: 'xxx',
environment: process.env.STAGE
});
module.exports.createPlaylist = async (event, context, callback) => {
context.callbackWaitsForEmptyEventLoop = false
if(!event.body) {
const error = new Error('Missing body parameters in createPlaylist')
await Sentry.captureException(error)
await new Promise(resolve => {setTimeout(resolve, 2000)})
return {
statusCode: 500,
headers: { 'Content-Type': 'text/plain' },
body: 'Missing body parameters'
}
}
return {
statusCode: 200,
}
}; |
We're also getting burned on this on Lambda. We started with the new libs and are totally boxed out, considering to go back to Raven. We're writing tests right now to attempt to close the hub and then reinitialize, which would be a workable workaround if it holds water. But still hacky / likely to cause problems under load. Personally I'd prefer some sort of |
I'm using the following express error handler: app.use((err: any, req: express.Request, res: express.Response, next: express.NextFunction) => {
let status = (err.status || err.statusCode || 500) as number
if (process.env.NODE_ENV === 'test') {
return next(err)
}
if (status < 400 || status >= 500) {
Raven.captureException(err, () => next(err))
} else {
next(err)
}
}) I'm then using scandium to deploy the Express app to Lambda edit: this is with Raven |
The dream API would be something like this 😍 Sentry.captureException(err: Error): Promise<void> |
@LinusU https://github.com/getsentry/sentry-javascript/blob/master/packages/core/src/baseclient.ts#L145-L152 🙂 You have to use client instance directly to get it though. The reason for this is that decided that the main scenario is a "fire and forget" type of behavior, thus it's not an async method. Internally however, we do have async API which we use ourselves. |
Seems that what I actually want is something more like: const backend = client.getBackend()
const event = await backend.eventFromException(error)
await client.processEvent(event, finalEvent => backend.sendEvent(finalEvent)) In order to skip all the queueing and buffering... I get that the design is tailored to "fire and forgot", and for running in a long-running server, and it's probably quite good at that since it does a lot of buffering, etc. The problem is that this is the exact opposite that you want for Lambda, App Engine, and other "serverless" architectures, which are becoming more and more common. Would it be possible to have a special method that sends the event as fast as possible, and returns a class Sentry {
// ...
async unbufferedCaptureException(err: Error): Promise<void> {
const backend = this.client.getBackend()
const event = await backend.eventFromException(error)
await this.client.processEvent(event, finalEvent => backend.sendEvent(finalEvent))
}
// ...
} |
@LinusU we'll most likely create a specific serverless package for this scenario. We just need to find some time, as it's the end of the year and things are getting crowdy now. Will keep you posted! |
That would be amazing! 😍 |
(talking about draining) It's meant to be used as the last thing before closing down the server process. Timeout in There's no way to know this per se, but there's a way to tell the lambda when it should be shut down using handler's callback argument. Also @LinusU, I re-read your previous comment, specifically this part:
This is how we implemented our buffer. Every sentry-javascript/packages/core/src/requestbuffer.ts Lines 12 to 18 in 0f0dc37
This means that if you do something like this in AWS Lambda (assuming you want to use default client, which is the simplest case): import * as Sentry from '@sentry/browser';
Sentry.init({ dsn: '__YOUR_DSN__' });
exports.handler = (event, context, callback) => {
try {
// do something
catch (err) {
Sentry.getCurrentHub()
.getClient()
.captureException(err)
.then((status) => {
// request status
callback(null, 'Hello from Lambda');
})
}
}; You can be sure that it was sent right away and there was no timing/processing overhead. |
@kamilogorek import * as Sentry from '@sentry/node';
Sentry.init({ dsn: '__YOUR_DSN__' });
exports.handler = async (event, context) => {
try {
// do something
return 'Hello from Lambda';
catch (err) {
await Sentry.getCurrentHub().getClient().captureException(err);
return 'Hello from Lambda with error';
}
}; |
@kamilogorek I'll give it a go and report back. Also, thanks for the tip on |
await Sentry.flush(2000); is also |
@tanduong can you provide repro case? Just stating that it doesn't work isn't too helpful 😅 |
@kamilogorek actually, I just found out that await Sentry.getCurrentHub().getClient().close(2000) doesn't work for me either because my lambda function is attached to VPC. I confirm that await Sentry.flush(2000); is actually working. BTW, so how would you deal with lambda in VPC? Attach to a NAT gateway? I just want Sentry but not the public internet. |
@tanduong Sentry is on the public internet, so yes you need to have NAT gateway if your lambda is running within your VPC. Otherwise you would have to explore hosted Sentry option. |
What's the |
Flushing the internal queue of messages over the wire |
Ok, that makes total sense. I think my issue then it's that this promise never returns when there's nothing else to flush? Whenever I run my wrapped |
|
I have what I believe is a similar issue as @enapupe. If you call I am using code like this: let client = Sentry.getCurrentHub().getClient();
if (client) {
// flush the sentry client if it has any events to send
log('begin flushing sentry client');
try {
await client.flush(2000);
} catch (err) {
console.error('sentry client flush error:', err);
}
log('end flushing sentry client');
} But when I make two calls to my lambda function in rapid succession, I get:
You can see that the second promise is never resolved. |
@esetnik I've filed an issue on that: #2131
|
In case anybody is having any issues. |
@SarasArya @HazAT |
@cibergarri I don't think so, looks synchronous to me, in case you have an async method in there, then there would be race conditions. |
Yeah, it's totally fine to do that |
Update: Sentry now supports automated error capture for Node/Lambda environments: https://docs.sentry.io/platforms/node/guides/aws-lambda/ |
I'm using @sentry/serverless like this:
It does not work on lambda. Any advice? |
Please tell how did your measure that it loses the exception events? Do you have a code sample of how such lost exception was thrown? Need more context. |
What is happening? I assume that the problem is when the invocation is over a reused container. I have tried also |
@marshall-lee what do you recomend? Should I create an issue, I'm stuck here. |
@armando25723 Looks like the server is responding with 429 (too many exceptions) while sending these events. We throw that in case of over quota/rate limiting scenarios. Do you know if you are sequentially sending errors or over quota? We can debug further if you think these are real error events getting dropped and you are under our 5k limit for the free tier. |
@ajjindal all other projects are working fine with sentry. The organization slug is "alegra", project name is mail-dispatch-serverless under #mail-micros. We have been using sentry for a long time, but first time with serverless. This is not free tier, I can't tell you exactly which plan we are using but it is a paid one. PD: is Team Plan |
I'm having the same issue here, doing |
What is the current behavior?
I'm using @sentry/node to capture exception on AWS lambda function.
However, it kills the process when
context.fail()
is called and the exception does not end up in Sentry.I could do a workaround like:
What is the expected behavior?
It would be nice if I can do something like:
Or something globally handle callback.
The text was updated successfully, but these errors were encountered: