⚡️ Easy Sentry SDK Integration For AWS Lambda
About
This library simplifies the integration of Sentry's @sentry/node library with AWS Lambda. The only supported platforms of this library are the Lambda Runtimes for Node.js 10 and 12. Python and Java support will require dedicated libraries. Pull requests are welcome!
The serverless-sentry-plugin
and serverless-sentry-lib
libraries are not affiliated with either Functional Software Inc., Sentry, Serverless or Amazon Web Services but developed independently and in my spare time.
Benefits
- Easy to use. Promised 🤞
- Integrates with Serverless Framework as well as the AWS Serverless Application Model for AWS Lambda (though no use of any framework is required).
- Wraps your Node.js code with Sentry error capturing.
- Forwards any errors returned by your AWS Lambda function to Sentry.
- Warn if your code is about to hit the execution timeout limit.
- Warn if your Lambda function is low on memory.
- Reports unhandled promise rejections.
- Catches and reports uncaught exceptions.
- Serverless, Sentry and as well as this library are all Open Source. Yay! 🎉
- TypeScript support
Installation
Although this library is written in TypeScript, the resulting library uses exclusively Node 10 features to ensure this code can run on AWS Lambda without any additional transpiling or further processing. We also do not use any 3rd party node module other than @sentry/node
itself.
This library can be used standalone or as part of the Serverless Sentry Plugin.
Use as a Standalone Library
If you don't want to add another plugin to Serverless (or if you're not using the Serverless Framework), you can use this library standalone without additional dependencies (besides @sentry/node
itself).
If you're using the Serverless Framework, extend your serverless.yml
to include additional environment variables. The only required environment variable is SENTRY_DSN
to set the DSN URL for your reporting. A full list of all available environment variables is available below.
service: my-serverless-project
provider:
environment:
SENTRY_ENVIRONMENT: ${opt:stage, self:provider.stage}
SENTRY_DSN: https://xxxx:yyyy@sentry.io/zzzz
If you are using the AWS Serverless Application Model, set the environment variables in your template.yml
:
AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Resources:
SomeFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: some-function/
Handler: index.handler
Runtime: nodejs12.x
Environment:
Variables:
SENTRY_DSN: https://xxxx:yyyy@sentry.io/zzzz
Environment Variables
Capturing can be controlled through the following environment variables. You can set them manually in your serverless.yml
(Serverless Framework) or template.yml
(AWS SAM) or let them be configured automatically using the Serverless Sentry Plugin during deployment.
In addition, the library checks for the following optional variables and adds them as custom Sentry tags automatically:
Environment Variable | Sentry Tag | Description |
---|
SERVERLESS_SERVICE | service_name | Serveless service name |
SERVERLESS_STAGE | stage | Serverless stage |
SERVERLESS_ALIAS | alias | Serverless alias, see Serverless AWS Alias Plugin |
SERVERLESS_REGION | region | Serverless region name |
Use Together With the Serverless Sentry Plugin
The Serverless Sentry Plugin allows simpler configuration of the library through the serverless.yml
and will upload your source-maps automatically during deployment. This is the recommended way of using the serverless-sentry-lib
library.
Instead of manually setting environment variables, the plugin determines and sets them automatically. In the serverless.yml
simply load the plugin and set the dsn
configuration option as follows:
service: my-serverless-project
provider:
plugins: serverless-sentry
custom:
sentry:
dsn: https://xxxx:yyyy@sentry.io/zzzz
You can still manually set environment variables on a per-function level to overwrite the default ones. Please refer to the Serverless Sentry Plugin for full documentation of all available options.
Updating Your Code
For maximum flexibility, this library is implemented as a wrapper around your original AWS Lambda handler code (your handler.js
or similar function). The withSentry
higher-order function adds error and exception handling and takes care of configuring the Sentry client automatically.
withSentry
is pre-configured to reasonable defaults and doesn't need any configuration. It will automatically load and configure @sentry/node
which needs to be installed as a peer dependency.
Original Lambda Handler Code:
exports.handler = async function (event, context) {
console.log("EVENT: \n" + JSON.stringify(event, null, 2));
return context.logStreamName;
};
New Lambda Handler Code Using withSentry
For Sentry Reporting
const withSentry = require("serverless-sentry-lib");
exports.handler = withSentry(async function (event, context) {
console.log("EVENT: \n" + JSON.stringify(event, null, 2));
return context.logStreamName;
});
ES6 Module: Original Lambda Handler Code:
export async function handler(event, context) {
console.log("EVENT: \n" + JSON.stringify(event, null, 2));
return context.logStreamName;
}
ES6 Module: New Lambda Handler Code Using withSentry
For Sentry Reporting
import withSentry from "serverless-sentry-lib";
export const handler = withSentry(async (event, context) => {
console.log("EVENT: \n" + JSON.stringify(event, null, 2));
return context.logStreamName;
});
Once your Lambda handler code is wrapped in withSentry
, it will be extended it with automatic error reporting. Whenever your Lambda handler sets an error response, the error is forwarded to Sentry with additional context information.
Setting Custom Configuration Options
As shown above you can use environment variables to control the Sentry integration. In some scenarios in which environment variables are not desired or in which custom logic needs to be executed, you can also pass in configuration options to withSentry
directly:
You can control how Sentry should be initialized by passing the following options:
sentryOptions
- Additional options to set for the Sentry client, e.g. proxy settings.scope
- Custom scope settings.filterLocal
- don't report errors from local environments (defaults to true
).sourceMaps
- Enable source maps (defaults to false
) by loading the RewriteFrames
Sentry integration.
Or, alternatively, you can pass in a custom, already preconfigured Sentry object. Note that Sentry needs to be properly initialized in this case:
sentry
- Use the given Sentry instance instead of importing it automatically.
In addition, you can set any of the following options to control what events should be captured:
flushTimeout
- How long we should wait for Sentry data to be written when shutting down the Lambda or between invocations (defaults to 2000
milliseconds).autoBreadcrumbs
- Automatically create breadcrumbs (see Sentry SDK docs, defaults to true
).captureErrors
- capture Lambda errors (defaults to true
).captureUnhandledRejections
- capture unhandled Promise rejections (defaults to true
).captureUncaughtException
- capture uncaught exceptions (defaults to true
).captureMemory
- monitor memory usage (defaults to true
).captureTimeouts
- monitor execution timeouts (defaults to true
).
Example
import withSentry from "serverless-sentry-lib";
const withSentryOptions = {
sentryOptions: {
httpProxy: "...",
httpsProxy: "...",
},
scope: {
tags: {
Foo: "bar",
},
},
captureErrors: false,
captureUnhandledRejections: true,
captureUncaughtException: true,
captureMemory: true,
captureTimeouts: true,
};
export const handler = withSentry(withSentryOptions, async (event, context) => {
console.log("EVENT: \n" + JSON.stringify(event, null, 2));
return context.logStreamName;
});
Accessing the Sentry Client for Capturing Custom Messages and Exceptions
If you want to capture a message or exception from anywhere in your code, simply use the Sentry client as usual. It is a singleton instance and doesn't need to be configured again:
CommonJS
const Sentry = require("@sentry/node");
Sentry.captureMessage("Hello from Lambda!", { level: "info" });
ES6 Modules
import * as Sentry from "@sentry/node";
Sentry.captureMessage("Hello from Lambda!", { level: "info" });
For further documentation on how to use it to capture your own messages refer to docs.getsentry.com.
Capturing Unhandled Promise Rejections
When enabled all Promise rejections that aren't handled by yourself will be reported to Sentry.
Capturing Uncaught Exceptions
Typically, if your Lambda code throws an unhandled exception somewhere in the code, the invocation is immediately aborted and the function exits with a "Process exited before completing request
". The plugin captures these unhandled exceptions, forwards them to Sentry and then exits the Lambda with an error code.
Local Development
By default the library will only forward errors to Sentry when deployed on AWS Lambda, not during local development. If you want to change this behavior set the filterLocal
option to false
.
Detecting Slow Running Code
It's a good practice to specify the function timeout in serverless.yml
to be at last twice as large as the expected maximum execution time. If you specify a timeout of 6 seconds (the default), this plugin will warn you if the function runs for 3 or more seconds. That means it's time to either review your code for possible performance improvements or increase the timeout value slightly.
Low Memory Warnings
The plugin will automatically generate a warning if the memory consumption of your Lambda function crosses 75% of the allocated memory limit. The plugin samples the amount of memory used by Node.js every 500 milliseconds (using process.memoryUsage()
), independently of any garbage collection. As with all Node.js code, it is important to remember that JavaScript code runs single-threaded and the monitoring function will only be able to sample memory usage if your code is in a wait state, e.g. during database queries or when calling asynchronous functions with a callback.
Only one low memory warning will be generated per function invocation. You might want to increase the memory limit step by step until your code runs without warnings.
Turn Sentry Reporting On/Off
Sentry reporting is only enabled if you wrap your code using withSentry
as shown in the examples above. In addition, error
reporting is only active if the SENTRY_DSN
environment variable is set or if you explicitly pass { sentryOptions: { dsn } }
as configuration options. This is an easy way to enable or disable reporting as a whole or for specific functions.
In some cases, it might be desirable to disable only error reporting itself but keep the advanced features such as timeout and low memory warnings in place. This can be achieved via setting the respective options in the environment variables or withSentry
during initialization:
const withSentry = require("serverless-sentry-lib");
module.exports.handler = withSentry({ captureErrors: false }, (event, context, callback) => {
});
Version History
2.5.2
- Fixed invalid
@sentry/integrations
peer dependency definition. Sorry :(
2.5.1
@sentry/integrations
is a peer dependency now. Make sure to install it in your project!
2.5.0
- Added support for
@sentry/node
v7. This is the recommended version now. - Updated dependencies
2.4.0
2.3.0
- Override existing
unhandledRejection
and uncaughtException
listeners when captureUnhandledRejections
or captureUncaughtExceptions
are enabled and invoke them after we handled them. At the same time we disable Sentry's default integrations for both to avoid duplicate reporting. This works around a custom listener registered by AWS Lambda internally that prevents proper automatic handling with Sentry. Thanks to ffxsam for reporting the original issue. By updating the execution order of the listeners we keep side effects to a minimum. Please report back if you encounter any weird or unexpected behavior! - Upgraded all dependencies, use Typescript 4.0
2.2.0
- Reset the scope on every Lambda start (if no custom Sentry client instance is used). This should avoid breadcrumbs and extras from previous runs "bleeding" into subsequent Lambda invocations. Thanks to demsey2 for reporting the original issue.
- Added a new
flushTimeout
option to control how long we want to wait for data to be written to Sentry before the Lambda shuts down or between invocations. - Deprecated
captureMemoryWarnings
and captureTimeoutWarnings
in favor of new options captureMemory
and captureTimeouts
which allow more customization. Thanks to keanolane for suggesting custom timeouts. This only affects users invoking withSentry
with custom options. If you're using serverless-sentry-plugin
to set all options you won't have to change anything. - Fixed an issue with custom tags, extras and user traits not being set when passed as options to
withSentry
. Thanks to blelump for reporting and providing a pull request.
2.1.0
- Explicitly check environment variables
IS_OFFLINE
and IS_LOCAL
for truthy strings (yes
, on
, true
, t
, or 1
). Thanks to danilofuchs for suggesting it. - Flush to Sentry when the Lambda function finishes but doesn't close Sentry yet as we might get called again. #23
2.0.1
- Fixed some type resolution issues in the generated TypeScript definition file
2.0.0
- Rewrite using TypeScript. The use of TypeScript in your project is fully optional, but if you do, we got you covered!
- Added new default uncaught exception handler.
- Dropped support for Node.js 6 and 8. The only supported versions are Node.js 10 and 12.
- Upgrade from Sentry SDK
raven
to the Unified Node.js SDK @sentry/node
. - Simplified integration using
withSentry
higher-order function. Passing the Sentry instance is now optional. - Thank you @aheissenberger and @Vadorequest for their contributions to this release! 🤗
Support
That you for supporting me and my projects.