DataDog / datadog-lambda-js

The Datadog AWS Lambda Library for Node
Apache License 2.0
109 stars 35 forks source link

TypeError: handler is not a function #431

Closed tedyu closed 10 months ago

tedyu commented 10 months ago

Expected Behavior

datadog-lambda-js functions correctly with node.js 18.

Actual Behavior

TypeError: handler is not a function
anonymous at /opt/nodejs/node_modules/datadog-lambda-js/utils/handler.js, 150
Object.<anonymous> at /opt/nodejs/node_modules/datadog-lambda-js/index.js, 167
step at /opt/nodejs/node_modules/datadog-lambda-js/index.js, 44
Object.next at /opt/nodejs/node_modules/datadog-lambda-js/index.js, 25
anonymous at /opt/nodejs/node_modules/datadog-lambda-js/index.js, 19
new Promise at <anonymous>
__awaiter at /opt/nodejs/node_modules/datadog-lambda-js/index.js, 15
anonymous at /opt/nodejs/node_modules/datadog-lambda-js/index.js, 161
anonymous at /opt/nodejs/node_modules/dd-trace/packages/dd-trace/src/tracer.js, 94
anonymous at /opt/nodejs/node_modules/dd-trace/packages/dd-trace/src/tracer.js, 45

Steps to Reproduce the Problem

  1. "datadog-lambda-js": ">=6.89.0"
  2. node.js 18

Specifications

Stacktrace

See above.

astuyve commented 10 months ago

Hey @tedyu! Thanks for reaching out.

My guess is that you're seeing this error thrown from here which is loading your handler code.

We have to take your handler function and call it, if we can't load it, this error can result.

Can you check the value of DD_LAMBDA_HANDLER and verify that it correctly points to your function handler? If not, can you open a support ticket with us so that we can see your specific packaging logic and handler code, and identify any possible issues?

Thanks!

tedyu commented 10 months ago

Can you confirm this is correct for node 18 ? layers = ["arn:aws:lambda:${var.region}:464622532012:layer:Datadog-Node18-x:98"] thanks

astuyve commented 10 months ago

Hi, yes this is the right layer but you also need to configure your handler deployment correctly. I'm not sure which deployment tool you're using but you can find the instructions here.

At a glance, I think that DD_LAMBDA_HANDLER is not being set correctly, so our layer can't load your code.

tedyu commented 10 months ago
const { Datadog, datadog } = require('datadog-lambda-js');
...
async function handler(event) {
...
module.exports.handler = datadog(handler);

After wrapping the handler, I get the following:

     TypeError: Cannot set property 'done' of undefined
      at /opt/app/node_modules/datadog-lambda-js/dist/utils/handler.js:163:22
      at /opt/app/node_modules/datadog-lambda-js/dist/index.js:223:70
      at step (node_modules/datadog-lambda-js/dist/index.js:44:23)
      at Object.next (node_modules/datadog-lambda-js/dist/index.js:25:53)
      at /opt/app/node_modules/datadog-lambda-js/dist/index.js:19:71
      at new Promise (<anonymous>)

Looking at utils/handler.js,

    return function (event, context) {

It seems context is undefined. Please let me know the proper way of wrapping.

tedyu commented 10 months ago

@astuyve the link you mentioned, https://docs.datadoghq.com/serverless/aws_lambda/installation/nodejs/?tab=datadogcli, doesn't mention using wrapper. Can you confirm whether wrapper is needed ? If so, what should be corrected in the formation in my previous comment ?

thanks

astuyve commented 10 months ago

Hi! Sorry! Those are just the entrypoint for our installation instructions. Please select the installation method for your IAC provider using the tabs. We support most tools such as terraform, serverless framework, Sam, CDK, etc. It's okay if you're not using the datadogcli method.

If you want to proceed with this via the custom instrumentation path, which is what it seems, you'll need to click the custom tab: https://docs.datadoghq.com/serverless/aws_lambda/installation/nodejs/?tab=custom

If you want to use the custom path but don't want to use the built-in handler redirection supported by this library, that's fine, you can wrap your function manually (as it seems you are attempting to do here).

That is linked further on the custom page: https://docs.datadoghq.com/serverless/guide/handler_wrapper/

You may need to pass the implicit context argument manually. It looks like that may be missing from the code snippet you've added (and perhaps the docs, although it should be passed automatically by the lambda runtime):

async function handler(event, context) {

I'd probably suggest trying the default handler redirection though, which means you don't need to touch this library in your own codebase but that's up to you.

Is this error present in Lambda, or coming from a local emulator?

Thanks!

tedyu commented 10 months ago

I am upgrading code from node.js 14 to 18. I want to transform current code first to see if the upgrade can go through.

When I specify the context parameter, I get:

/opt/app/lambda/index.js
  79:31  error  'context' is defined but never used  no-unused-vars

Can you tell me how context should be passed down to datadog ?

Thanks

astuyve commented 10 months ago

Hi, the context object is passed by the runtime and documented by Lambda here.

If your linter is complaining about it, maybe you can use _context as the variable name, or ignore the rule?

Thanks!

tedyu commented 10 months ago

After suppressing the lint error, I still get:

     TypeError: Cannot set property 'done' of undefined
      at /opt/app/node_modules/datadog-lambda-js/dist/utils/handler.js:163:22
      at /opt/app/node_modules/datadog-lambda-js/dist/index.js:223:70
      at step (node_modules/datadog-lambda-js/dist/index.js:44:23)
      at Object.next (node_modules/datadog-lambda-js/dist/index.js:25:53)
      at /opt/app/node_modules/datadog-lambda-js/dist/index.js:19:71
      at new Promise (<anonymous>)
      at __awaiter (node_modules/datadog-lambda-js/dist/index.js:15:12)
      at traceListenerOnWrap (node_modules/datadog-lambda-js/dist/index.js:200:36)
      at /opt/app/node_modules/datadog-lambda-js/dist/index.js:246:91

It seems I need to find a way to pass down the context.

astuyve commented 10 months ago

That path looks interesting, you're getting this error in Lambda, or coming from a local emulator? Can you provide a minimal project which reproduces this?

Thanks!

tedyu commented 10 months ago

The stack trace was copied from our circleci job which runs unit tests. I will see if the reproduction can be isolated. Meanwhile, it would be nice if someone can advise on passing context parameter. Thanks

astuyve commented 10 months ago

Ahhhh I see! Thanks for that!

I had asked earlier if this error was being raised inside Lambda, but it's not. This is occurring within a unit test, but you're exercising all of the features of this library outside of Lambda, which won't work.

You'll need to stub this library when testing it outside of Lambda (much like you would stub any normal external dependency). This library isn't meant to be executed without Lambda runtime features being present (nor will it run effectively without being able to reach the datadog agent or forwarder).

This is also why we support wrapping handlers via non-code paths, so that your unit tests can still run without this library being present.

I'm closing this issue as it's not an issue with this codebase.

Thank you!

tedyu commented 10 months ago

I did some search w.r.t. stubbing out datadog handler for unit testing but haven't found answer.

If you can provide some pointer, that would be nice.

tedyu commented 10 months ago

@astuyve Another possibility is for datadog-lambda-js to regard the absence of context as indication that the lambda is not operating in normal cloud environment and return early. This would make datadog-lambda-js more developer friendly.

What do you think ?

astuyve commented 10 months ago

Hi, you'll want to read the documentation for whatever test running library you're using. Jest or Mocha are both popular and offer this ability. You can even see how we do this with jest in this library.

This library isn't intended to be a development dependency, so we wouldn't even expect it to be installed outside of Lambda.

We would have to carefully consider how and where we'd return early here because we spy on the function entrance and exit, and it may be challenging to fully support a "disabled" mode.

Instead most users find they can stub this dependency easily and do so in their unit tests. Thanks! AJ

tedyu commented 10 months ago

From https://github.com/DataDog/datadog-lambda-js, I see node 16 mentioned. Can you confirm that node 18 is supported by 6.89 release ?

Thanks

astuyve commented 10 months ago

Hi yes, we support node 18 as per the readme:

image

and all recent releases: https://github.com/DataDog/datadog-lambda-js/releases/tag/v7.99.0