Sentry on AWS lambda in nodejs doesn't send exception - node.js

I'm trying to configure sentry on AWS Lambda (nodejs 8.10) but the exceptions are not sent to Sentry. I'm the feeling that is a issue of timing: the lambda is terminated before the data are sent to sentry.
Which is the right way to integrate sentry on AWS Lambda?
Thank you!

Update: Sentry automatically reports exceptions on Node/Lambda. see docs
You have to use the function flush which makes sure all events that are currently queued up will be sent:
Sentry.captureException(new Error('test'));
await Sentry.flush();
http://getsentry.github.io/sentry-javascript/modules/node.html#flush

Install the Sentry module
npm install #sentry/node
See the bellow example to configure AWS Lambda function with Sentry.
// Import the Sentry module.
const Sentry = require("#sentry/node");
// Configure the Sentry SDK.
Sentry.init({
dsn: "your dsn",
});
exports.handler = function (event, context) {
try {
textFunctionCall(); // Call undefined function.
} catch (e) {
Sentry.captureException(e); // using this function exception captured in Sentry dashboard.
Sentry.flush(2000);
}
const response = {
statusCode: 200,
body: "Hello, exception captured",
};
return response;
};
https://docs.sentry.io/platforms/node/serverless/#aws-lambda
Still not Capturing error in Sentry, then install the bellow node module these are the dependency.
npm install lru-cache
npm install lru_map
npm install tslib

Install sentry/node
npm i #sentry/node
Add this code in the top of your lambda
const Sentry = require("#sentry/node");
Sentry.init({
dsn: "https://0dc9558ffb934657b6c3b97182b4338d#sentry.io/182963" // your_dsn_key_here
});
Use sentry object
try {
//Logic here
} catch (error) {
Sentry.captureException("Error on getLogsById");
}

I try to work on this and got a solution using already existing function
Create file name sentry-lib.js and add below code
const Sentry = require("#sentry/node");
// Importing #sentry/tracing patches the global hub for tracing to work.
const SentryTracing = require("#sentry/tracing");
let transaction = null;
let SentryInstance = null;
export class SentryLib {
constructor(){
Sentry.init({
dsn: process.env.SENTRY_DSN,
tracesSampleRate: 1.0,
});
}
startLogging(){
transaction = Sentry.startTransaction();
}
logError(error){
Sentry.captureException(error);
}
async endLogging(){
await Sentry.flush(); // Flush all awaiting event in queue
transaction.finish();
}
}
Now create file name second.js
export const sum = (){
const sentryLibIns = new SentryLib();
try{
// Something will break here
} catch(error){
sentryLibIns.error(error);
}
}
Now create the handler file name handler.js
const handler = async (event, context, callback)=>{
const sentryLibIns = new SentryLib();
sentryLibIns.startLogging();
try{
fn1();
} catch(error){
sentryLibIns.error(error);
} finally{
await sentryLibIns.endLogging();
}
}

Related

Google Cloud Function failed with timeout

I have a Pub/Sub triggered cloud function that calls an API end-point and logs the message. But I am not seeing all log messages being logged in console except everything right before calling API.
Once the API is called I am logging the response, and exception messages in case of any error.
It is logging: Function execution took 120015 ms. Finished with status: timeout Earlier the default timeout was set to 60 sec, later I increased it to 120 sec. Still the problem persist.
I am not understanding the issue here since it is working locally without any issues.
Here I have custom module to log messages to Winston and GCP console (it doesn't have any issue and working fine).
Code calling the API module:
const console = require('./logging-utils');
const portal_api = require('./api-utils');
exports.triggerPortalNotifier = async (event, context) => {
try {
/*
.....
*/
console.metadata.cloudFunction = cf_name;
console.metadata.requestId = requestId;
console.metadata.organizationId = organizationId;
console.metadata.instanceId = instanceId;
console.logMessage(`Event received with payload: some message`);
var payload = {
//payload to API
}
var response = await portal_api.notifyPortal(payload);
console.logMessage(`Response received from portal API is: ${JSON.stringify(response.data)}`);
}
else {
throw new Error(`Invalid message received: ${_message}`);
}
}
catch (error) {
console.logMessage(`Portal API failed with exception: ${error}`);
throw new Error(`${error.message}`);
}
}
Code that make API request (using axios module)
require('dotenv').config();
const axios = require('./axios-instance');
const console = require('./logging-utils');
const nextgen_api = {
notifyPortal: async (payload) => {
try {
const config = {
headers: {
'Authorization': process.env.PORTAL_AUTHORIZATION_TOKEN,
'Content-Type': "application/json",
'Accept': "application/plain"
}
}
console.logMessage(`Input paylod for API end-point: ${process.env.PORTAL_API} => ${JSON.stringify(payload)}`)
const response = await axios.post(process.env.PORTAL_API, JSON.parse(JSON.stringify(payload)), config);
console.logMessage(`Response from API: ${JSON.stringify(response.data)}`);
return response;
}
catch (err) {
if (err.response && err.response.status !== 200) {
console.logMessage(`API call failed with status code: ${err.response.status} `);
throw new Error(`API call failed with status code: ${err.response.status} `);
}
else {
console.logMessage(`API call failed with ${err.stack}`);
throw new Error(`API call failed with status code: ${err.stack} `);
}
}
}
}
module.exports = my_api;
Message Response from API: ${JSON.stringify(response.data)} is not being logged.
Any help here is appreciated.

Node.js - How to Call External Function from AWS Lambda Index.js

I am very new to AWS Lambda and Node.js, so apologies for the elementary question. I have two JS files in my Lambda project's root directory:
index.js
engageMessage(); //For testing purposes - throws error
exports.handler = async (event) => {
// TODO implement
const response = {
statusCode: 200,
body: JSON.stringify('This is index'),
};
return response;
};
messenger.js
function engageMessage() {
console.log("Messenger, checking in!");
};
How do I call engageMessage() from my index.js file? Each time I try, I get a "TypeError: engageMessage is not a function" message, and am unsure of how to properly reference/import/require messenger.js.
In messenger.js you need to export your function
module.exports = { engageMessage }
Then require it in your index.js
const { engageMessage } = require("./messenger.js")

Able to connect to redis but set/get times out

I'm trying to do a get() from my AWS Lambda (NodeJS) on ElastiCache Redis using node_redis client. I believe that I'm able to connect to redis but I'm getting Time out (Lambda 60 sec time out) when I'm trying to perform a get() operation.
I have also granted my AWS lambda Administrator access just to be certain that it's not a permissions issue. I'm hitting lambda by going to AWS console and clicking the Test button.
Here is my redisClient.js:
const util = require('util');
const redis = require('redis');
console.info('Start to connect to Redis Server');
const client = redis.createClient({
host: process.env.ElastiCacheEndpoint,
port: process.env.ElastiCachePort
});
client.get = util.promisify(client.get);
client.set = util.promisify(client.set);
client.on('ready',function() {
console.log(" subs Redis is ready"); //Can see this output in logs
});
client.on('connect',function(){
console.log('subs connected to redis'); //Can see this output in logs
})
exports.set = async function(key, value) {
console.log("called set!");
return await client.set(key, value);
}
exports.get = async function(key) {
console.log("called get!"); //Can see this output in logs
return await client.get(key);
}
Here's my index.js which calls the redisClient.js:
const redisclient = require("./redisClient");
exports.handler = async (event) => {
const params = event.params
const operation = event.operation;
try {
console.log("Checking RedisCache by calling client get") // Can see this output in logs
const cachedVal = await redisclient.get('mykey');
console.log("Checked RedisCache by calling client get") // This doesn't show up in logs.
console.log(cachedVal);
if (cachedVal) {
return {
statusCode: 200,
body: JSON.stringify(cachedVal)
}
} else {
const setCache = await redisclient.set('myKey','myVal');
console.log(setCache);
console.log("*******")
let response = await makeCERequest(operation, params, event.account);
console.log("CE Request returned");
return response;
}
}
catch (err) {
return {
statusCode: 500,
body: err,
};
}
}
This is the output (time out error message) that I get:
{
"errorMessage": "2020-07-05T19:04:28.695Z 9951942c-f54a-4b18-9cc2-119eed65e9f1 Task timed out after 60.06 seconds"
}
I have tried using Bluebird (changing get to getAsync()) per this: https://github.com/UtkarshYeolekar/promisify-redis-client/blob/master/redis.js but still got the same behavior.
I also changed the port to use a random value (like 8088) that I'm using to create client (to see the behavior of connect event for a failed connection) - in this case I still see a Timed Out error response but I don't see the subs Redis is ready and subs connected to redis in my logs.
Can anyone please point me in the right direction? I don't seem to understand why I'm able to connect to redis but the get() request times out.
I figured out the issue and posting here in case it helps anyone in future as the behavior wasn't very intuitive for me.
I had enabled AuthToken param while setting up my redis. I was passing the param to lambda with the environment variables but wasn't using it while sending the get()/set() requests. When I disabled the AuthToken requirement from redis configuration - Lambda was able to hit redis with get/set requests. More details on AuthToken can be found here: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-elasticache-replicationgroup.html#cfn-elasticache-replicationgroup-authtoken

How to connect to Azure SQL database from multiple Azure Functions TypeScript API endpoints

I'm creating an API in Azure Functions using TypeScript, with multiple endpoints connecting to the same Azure SQL Server. Each endpoint was set up using the Azure Functions extension for VS Code, with the HttpTrigger TypeScript template. Each endpoint will eventually make different calls to the database, collecting from, processing and storing data to different tables.
There don't seem to be any default bindings for Azure SQL (only Storage or Cosmos), and while tedious is used in some Microsoft documentation, it tends not to cover Azure Functions, which appears to be running asynchronously. What's more, other similar StackOverflow questions tend to be for standard JavaScript, and use module.exports = async function (context) syntax, rather than the const httpTrigger: AzureFunction = async function (context: Context, req: HttpRequest): Promise<void> syntax used by the TypeScript HttpTrigger templates.
Here's what I've got so far in one of these endpoints, with sample code from the tedious documentation in the default Azure Functions HttpTrigger:
var Connection = require('tedious').Connection;
var config = {
server: process.env.AZURE_DB_SERVER,
options: {},
authentication: {
type: "default",
options: {
userName: process.env.AZURE_DB_USER,
password: process.env.AZURE_DB_PASSWORD,
}
}
};
import { AzureFunction, Context, HttpRequest } from "#azure/functions"
const httpTrigger: AzureFunction = async function (context: Context, req: HttpRequest): Promise<void> {
context.log('HTTP trigger function processed a request.');
const name = (req.query.name || (req.body && req.body.name));
if (name) {
var connection = new Connection(config);
connection.on('connect', function(err) {
if(err) {
console.log('Error: ', err)
}
context.log('Connected to database');
});
context.res = {
// status: 200, /* Defaults to 200 */
body: "Hello " + (req.query.name || req.body.name)
};
}
else {
context.res = {
status: 400,
body: "Please pass a name on the query string or in the request body"
};
}
};
export default httpTrigger;
This ends up with the following message:
Warning: Unexpected call to 'log' on the context object after function
execution has completed. Please check for asynchronous calls that are
not awaited or calls to 'done' made before function execution
completes. Function name: HttpTrigger1. Invocation Id:
xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx. Learn more:
https://go.microsoft.com/fwlink/?linkid=2097909
Again, the async documentation linked to covers just the standard JavaScript module.exports = async function (context) syntax, rather than the syntax used by these TypeScript httpTriggers.
I've also been reading that best practice might be to have a single connection, rather than connecting anew each time these endpoints are called - but again unsure if this should be done in a separate function that all of the endpoints call. Any help would be much appreciated!
I'm glad that using the 'mssql' node package works for you:
const sql = require('mssql');
require('dotenv').config();
module.exports = async function (context, req) {
try {
await sql.connect(process.env.AZURE_SQL_CONNECTIONSTRING);
const result = await sql.query`select Id, Username from Users`;
context.res.status(200).send(result);
} catch (error) {
context.log('error occurred ', error);
context.res.status(500).send(error);
}
};
Ref:https://stackoverflow.com/questions/62233383/understanding-js-callbacks-w-azure-functions-tedious-sql
Hope this helps.

Testcafe: How to error on 4xx or 5xx response?

I'm working on a legacy app that makes a lot of calls to external sources. I'm trying to refactor this app, and I've written testcafe tests to help inform me when I've made a mistake. I'm NOT running my tests with --skip-js-errors, but when I get 404 errors and the console prints this out:
the test doesn't stop. I'd like errors like these to be something I am informed about. How do I make 4xx and 5xx network responses fail testcafe? I'm using angular 1.2 if that matters. If I could, I would change all the remote calls to throw an exception on 4xx or 5xx, but this is legacy code I don't understand, and I'm sure doing that would break a feature.
I suggest you extend RequestLogger to check a request. You can throw an error based on the request status. For example:
import EventEmitter from 'events';
import { RequestHook } from 'testcafe';
fixture `test`
.page('https://testcafe.devexpress.com/Details2/')
class FailedRequestsLogger extends RequestHook {
constructor (...args) {
super(...args);
this.events = new EventEmitter();
this.failedRequestPromise = new Promise(resolve => this.events.once('failed-request', resolve));
}
onRequest (request) {
}
onResponse (response) {
if (response.statusCode >= 400)
this.events.emit('failed-request', response.statusCode);
}
waitForFailedRequest (action) {
return Promise.race([
action(),
this.failedRequestPromise.then(statusCode => Promise.reject(new Error(`Request failed with the ${statusCode} status code`)))
])
}
}
const logger = new FailedRequestsLogger();
test.requestHooks(logger)('test', async t => {
await logger.waitForFailedRequest(async () => {
await t.click('body');
await t.wait(10000);
});
});

Resources