https://serverless-stack.com/ logo
#sst
Title
# sst
k

Kujtim Hoxha

08/13/2021, 10:44 AM
Hi, I tried to add a lambda layer to all of my functions using
app.setDefaultFunctionProps
but it looks like you can only do that within the context of a stack, I think there should be an easier way to add lambda layers for all functions in all my stacks (I ended up just adding it to one of the stacks that the others are dependent on)
f

Frank

08/13/2021, 3:39 PM
Hey @Kujtim Hoxha, were you using an existing layer?
Copy code
LayerVersion.fromLayerVersionArn(this, "Layer", layerArn);
Or were you creating a new layer?
Copy code
new lambda.LayerVersion(this, 'Layer', {
  code: lambda.Code.fromAsset(...),
});
k

Kujtim Hoxha

08/13/2021, 3:39 PM
An existing layer
Specifically the sentry one
f

Frank

08/13/2021, 3:40 PM
Oh, what was the error you were getting?
And can I see what your
app.setDefaultFunctionProps
looked like?
k

Kujtim Hoxha

08/13/2021, 3:41 PM
Copy code
const sentryLayer = lambda.LayerVersion.fromLayerVersionArn(
      this,
      'SentryLayer',
      'arn:aws:lambda:us-east-1:943013980633:layer:SentryNodeServerlessSDK:26'
    );

    app.setDefaultFunctionProps({
      layers: process.env.IS_LOCAL ? [] : [sentryLayer],
    });
f

Frank

08/13/2021, 3:43 PM
hmm, it won’t work b/c
this
isn’t a Stack right. Was that the error you were getting?
k

Kujtim Hoxha

08/13/2021, 3:43 PM
Wait sorry no instead of this I had
app
let me try again and give you the error
Copy code
Import at 'SentryLayer' should be created in the scope of a Stack, but no Stack found
f

Frank

08/13/2021, 3:44 PM
Yeah, try this:
Copy code
const sentryLayer = (stack) => lambda.LayerVersion.fromLayerVersionArn(
      stack,
      'SentryLayer',
      'arn:aws:lambda:us-east-1:943013980633:layer:SentryNodeServerlessSDK:26'
    );

  app.setDefaultFunctionProps((stack) => ({
    layers: process.env.IS_LOCAL ? [] : [sentryLayer(stack)],
  }));
k

Kujtim Hoxha

08/13/2021, 3:45 PM
That did it 😮
Thanks man
j

Janessa

09/02/2021, 4:52 PM
Hey @Kujtim Hoxha, curious how you got this working. Are you calling
Sentry.init({})
in each lambda handler?
k

Kujtim Hoxha

09/02/2021, 4:57 PM
Accidentally deleted my previous message 🤦‍♂️ So basically anything that has to do with sentry I only setup if I am not doing local development
Copy code
app.setDefaultFunctionProps((stack) => {
    stackLayer[stack.artifactId] =
      stackLayer[stack.artifactId] || sentryLayer(stack);
    return {
      layers: process.env.IS_LOCAL ? [] : [stackLayer[stack.artifactId]],
    };
  });
And for the handlers
Copy code
if (process.env.NODE_ENV !== 'development') {
  // x-ray tracing for all aws services
  // eslint-disable-next-line @typescript-eslint/no-var-requires
  captureAWS(require('aws-sdk'));
  const Sentry = require('@sentry/serverless');

  Sentry.AWSLambda.init({
    dsn: process.env.SENTRY_DNS,

    // We recommend adjusting this value in production, or using tracesSampler
    // for finer control
    tracesSampleRate: process.env.SENTRY_TRACES_SAMPLE_RATE,
  });
}
To get
NODE_ENV
inside your functions just add this
j

Janessa

09/02/2021, 4:58 PM
Thank you, I’ll try it out 😄
k

Kujtim Hoxha

09/02/2021, 5:00 PM
@Janessa just one change from the example @Frank shared, it looks like the method is called multiple times for the same stack (at least for me it was) so I had to check if I already added the layer
Copy code
const sentryLayer = (stack: Stack) => {
  return lambda.LayerVersion.fromLayerVersionArn(
    stack,
    'SentryLayer',
    'arn:aws:lambda:us-east-1:943013980633:layer:SentryNodeServerlessSDK:26'
  );
};

const stackLayer: { [key: string]: ILayerVersion } = {};
app.setDefaultFunctionProps((stack) => {
  stackLayer[stack.artifactId] =
    stackLayer[stack.artifactId] || sentryLayer(stack);
  return {
    layers: process.env.IS_LOCAL ? [] : [stackLayer[stack.artifactId]],
  };
});
^ here is the full code
j

Janessa

09/02/2021, 5:33 PM
I see the layer on my lambda, but I’m not getting anything in Sentry 🤔 Do you see anything wrong about this?
Copy code
export const lambdaHandler: Handler = (async (event: Event, context: Context) => {
  // eslint-disable-next-line @typescript-eslint/no-var-requires
  const Sentry = require('@sentry/serverless');

  Sentry.AWSLambda.init({
    dsn: process.env.SENTRY_DNS,

    // We recommend adjusting this value in production, or using tracesSampler
    // for finer control
    tracesSampleRate: 1.0 //process.env.SENTRY_TRACES_SAMPLE_RATE,
  });
  try {
    randomFunction()
  } catch (error) {
    console.log("Error!")
    Sentry.captureException(error)
  }
})

const randomFunction = () => {
  throw new Error("Function not implemented.");
}
k

Kujtim Hoxha

09/02/2021, 5:34 PM
do you have the
SENTRY_DNS
setup ?
j

Janessa

09/02/2021, 5:35 PM
yeah, I also printed out the sentry client in my lambda and it has the expected value for the dsn
k

Kujtim Hoxha

09/02/2021, 5:36 PM
Instead of using
Sentry.captureException(error)
try doing
Sentry.AWSLambda.init
outside of your handler function and wrap the handler using
Copy code
if (process.env.NODE_ENV !== 'development') {
  handler = Sentry.AWSLambda.wrapHandler(handler);
}
j

Janessa

09/02/2021, 5:37 PM
I’ve gotten it to work using the Sentry wrapHandler method, but I was hoping I could avoid it by using the Sentry Layer 😅 Maybe not though?
k

Kujtim Hoxha

09/02/2021, 5:38 PM
I think you will have to use both 🤔 but maybe I am wrong
Why are you trying to avoid the wrapper ?
j

Janessa

09/02/2021, 5:39 PM
Ooh maybe that is the case, is the layer just another way of injecting the sentry module then? I was trying to avoid the wrapper because I didn’t want to have to go back and wrap all existing lambda handlers with it
k

Kujtim Hoxha

09/02/2021, 5:40 PM
Ah yeah the layer just ads the module (i think)
j

Janessa

09/02/2021, 5:41 PM
I see. Thank you for your help!! 🙏
k

Kujtim Hoxha

09/02/2021, 5:41 PM
If you already need to go back and wrap all the functions maybe create a middleware that adds the sentry wrapper and use that middleware, and from now on if you have to wrap all the functions again you could just add it to the middleware
I personally use
middy
Copy code
export const wrapper = (handler: LambdaHandler, logger?: Logger): any => {
  if (process.env.NODE_ENV !== 'development') {
    handler = Sentry.AWSLambda.wrapHandler(handler);
  }
  return middy(handler)
    .use(warmerMiddleware())
    .use(httpEventNormalizer())
    .use(httpHeaderNormalizer())
    .use(httpJsonBodyParser())
    .use(httpUrlEncodeBodyParser())
    .use(authMiddleware())
    .use(httpCors())
    .use(errorMiddleware(logger))
    .use(httpResponseSerializer());
};
And now all my functions look like
Copy code
export const handler = wrapper(async (event: APIGatewayProxyEvent) => {
....
})
2 Views