Skip to content
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

Feature Request: support BigInt serialization in segment metadata #1713

Closed
misterjoshua opened this issue Sep 26, 2023 · 9 comments · Fixed by #1716 or #1769
Closed

Feature Request: support BigInt serialization in segment metadata #1713

misterjoshua opened this issue Sep 26, 2023 · 9 comments · Fixed by #1716 or #1769
Assignees
Labels
completed This item is complete and has been merged/shipped feature-request This item refers to a feature request for an existing or new utility tracer This item relates to the Tracer Utility

Comments

@misterjoshua
Copy link
Contributor

misterjoshua commented Sep 26, 2023

Expected Behaviour

When I add metadata to my trace, I expect the tracer to handle any data I send to it without causing the lambda to crash.

Current Behaviour

It crashes the lambda when I use putMetadata to add a data structure that contains a BigInt or any other format that JSON.stringify cannot encode.

Code snippet

import middy from '@middy/core';
import { Tracer, captureLambdaHandler } from '@aws-lambda-powertools/tracer';

const tracer = new Tracer({ serviceName: 'stream-handler-test' });

export const handler = middy(async (_event) => {
  console.log('1. Call someLogic.doSomething()');
  const response = await someLogic.doSomething();

  console.log('2. Add the response to the trace metadata');

  // Add the response to the trace, so we can see it in the observability
  // platform in case anything goes wrong.
  //
  // This is a very common pattern for me. I don't usually think about
  // what's in the response unless the data is likely to be giant or
  // contains sensitive information in it. It just so happens that
  // this response contains a BigInt, and that's enough to cause the
  // lambda to crash.
  tracer.putMetadata('response', response);

  console.log('3. Return a response from the handler');

  return `Here is a number: ${response.number}`;
}).use(captureLambdaHandler(tracer));

class SomeLogic {
  async doSomething() {
    return {
      number: 1234n,
    };
  }
}

const someLogic = new SomeLogic();

Steps to Reproduce

  1. Bundle and install the script above in an AWS Lambda function with tracing enabled.
  2. Run the function from the AWS Lambda console.

Possible Solution

I think there are two factors to consider.

  1. We should make an effort to encode the types putMetadata receives when JSON.stringify cannot handle them - perhaps we can use a replacer function to encode exotic types.
  2. Even if the tracer fails to encode, I don't think it should ever crash the lambda. Perhaps the tracer should be swallowing all exceptions so it doesn't affect the user's code when something goes wrong.

Powertools for AWS Lambda (TypeScript) version

latest

AWS Lambda function runtime

16.x

Packaging format used

npm

Execution logs

START RequestId: c879e6a8-5a2f-4e1b-9240-7dfce8c41719 Version: $LATEST
2023-09-26T17:29:22.684Z	c879e6a8-5a2f-4e1b-9240-7dfce8c41719	INFO	1. Call someLogic.doSomething()
2023-09-26T17:29:22.704Z	c879e6a8-5a2f-4e1b-9240-7dfce8c41719	INFO	2. Add the response to the trace metadata
2023-09-26T17:29:22.704Z	c879e6a8-5a2f-4e1b-9240-7dfce8c41719	INFO	3. Return a response from the handler
2023-09-26T17:29:22.765Z	c879e6a8-5a2f-4e1b-9240-7dfce8c41719	ERROR	Invoke Error 	{"errorType":"TypeError","errorMessage":"Do not know how to serialize a BigInt","originalError":{"errorType":"TypeError","errorMessage":"Do not know how to serialize a BigInt","stack":["TypeError: Do not know how to serialize a BigInt","    at JSON.stringify (<anonymous>)","    at Subsegment.format (/var/task/index.js:2103:19)","    at Object.send (/var/task/index.js:1434:33)","    at Subsegment.flush (/var/task/index.js:2073:26)","    at Subsegment.streamSubsegments (/var/task/index.js:2083:14)","    at Subsegment.close (/var/task/index.js:2046:18)","    at close (/var/task/index.js:9565:24)","    at captureLambdaHandlerAfter (/var/task/index.js:9581:11)","    at runMiddlewares (/var/task/index.js:9799:23)","    at runRequest (/var/task/index.js:9775:13)"]},"stack":["TypeError: Do not know how to serialize a BigInt","    at JSON.stringify (<anonymous>)","    at Subsegment.format (/var/task/index.js:2103:19)","    at Object.send (/var/task/index.js:1434:33)","    at Subsegment.flush (/var/task/index.js:2073:26)","    at Subsegment.streamSubsegments (/var/task/index.js:2083:14)","    at Subsegment.close (/var/task/index.js:2046:18)","    at close (/var/task/index.js:9565:24)","    at captureLambdaHandlerError (/var/task/index.js:9587:11)","    at runMiddlewares (/var/task/index.js:9799:23)","    at runRequest (/var/task/index.js:9782:13)"]}
END RequestId: c879e6a8-5a2f-4e1b-9240-7dfce8c41719
REPORT RequestId: c879e6a8-5a2f-4e1b-9240-7dfce8c41719	Duration: 139.59 ms	Billed Duration: 140 ms	Memory Size: 128 MB	Max Memory Used: 63 MB	Init Duration: 199.81 ms	
XRAY TraceId: 1-651314f2-6e1cb4753036f8091d8a133e	SegmentId: 1937588641412fd8	Sampled: true	

This issue came from Twitter. https://twitter.com/heitor_lessa/status/1706563622694981916

@misterjoshua misterjoshua added triage This item has not been triaged by a maintainer, please wait bug Something isn't working labels Sep 26, 2023
@misterjoshua misterjoshua changed the title Bug: tracer crashes lambda when bigint added to segment at the moment the segment is closed Bug: tracer crashes lambda when bigint added to segment Sep 26, 2023
@dreamorosi dreamorosi added tracer This item relates to the Tracer Utility confirmed The scope is clear, ready for implementation and removed triage This item has not been triaged by a maintainer, please wait labels Sep 27, 2023
@dreamorosi dreamorosi self-assigned this Sep 27, 2023
@dreamorosi
Copy link
Contributor

Hi @misterjoshua thanks for taking the time to open the issue.

As you have correctly pointed out , the JSON.stringify() function doesn't know how to serialize the BigInt and so it crashes. Based on this I'd be more inclined in considering this a feature request given that the behavior observed is idiomatic to the language/runtime.

With that said, I do agree that we should do something about this, so I propose a two steps approach:

Step 1

Add an internal try/catch logic to the putMetadata() method so that in case the SDK throws an error, we catch it and log a warning.

This is a change that we can do right away and that we should do regardless of the serialization discussion.

Step 2

Add support for BigInt objects. The change is relatively simple if made on the X-Ray SDK side, however if made on the Powertools side it requires a more roundabout way given that the SDK implementation doesn't extract the serialization in its own method. For this reason we would have to essentially provide a new implementation for two methods of the Segment object.

I'd like to avoid this option if possible since it'll mean we'll have to make sure our implementation doesn't go out of sync with theirs, so for this reason I have opened an issue on the X-Ray SDK repo and offered to contribute a fix: aws/aws-xray-sdk-node#616.

To PoC the fix, I was able write this passing test:

it('handles BigInt types when adding metadata to a segment', () => {
  const segment = new Subsegment('## foo.bar');

  segment.addMetadata('foo', {
    bar: BigInt(123),
  });

  (
    segment as Subsegment & {
      type: string;
      parent_id: string;
      trace_id: string;
    }
  ).format = function format() {
    this.type = 'subsegment';
    if (this.parent) {
      this.parent_id = this.parent.id;
    }
    if (this.segment) {
      this.trace_id = this.segment.trace_id;
    }

    return JSON.stringify(this, (_, value) => {
      if (typeof value === 'bigint') {
        return value.toString();
      }

      return value;
    });
  };
  segment.toString = function toString() {
    return JSON.stringify(this, (_, value) => {
      if (typeof value === 'bigint') {
        return value.toString();
      }

      return value;
    });
  };

  expect(segment).toEqual(
    expect.objectContaining({
      metadata: {
        default: {
          foo: {
            bar: 123n,
          },
        },
      },
    })
  );
});

As mentioned I'll open a PR for the first step and try to include it in the next release that should go out today/tomorrow at the latest. In the meanwhile, we'll give some time to the X-Ray SDK folks to review my issue and reply.

We'll keep monitoring the progress of that issue and hopefully contribute a PR; however if things take too long we'll reconsider implementing the change on our side either temporarily or permanently.

@dreamorosi dreamorosi added feature-request This item refers to a feature request for an existing or new utility and removed bug Something isn't working labels Sep 27, 2023
@dreamorosi dreamorosi changed the title Bug: tracer crashes lambda when bigint added to segment Feature Request: support BigInt serialization in segment metadata Sep 27, 2023
@dreamorosi dreamorosi moved this from Backlog to Working on it in Powertools for AWS Lambda (TypeScript) Sep 27, 2023
@dreamorosi dreamorosi moved this from Working on it to Pending review in Powertools for AWS Lambda (TypeScript) Sep 28, 2023
@dreamorosi dreamorosi moved this from Pending review to On hold in Powertools for AWS Lambda (TypeScript) Sep 28, 2023
@dreamorosi dreamorosi added blocked This item's progress is blocked by external dependency or reason and removed confirmed The scope is clear, ready for implementation labels Sep 28, 2023
@dreamorosi dreamorosi linked a pull request Sep 28, 2023 that will close this issue
9 tasks
@github-actions github-actions bot added pending-release This item has been merged and will be released soon and removed blocked This item's progress is blocked by external dependency or reason labels Sep 28, 2023
@dreamorosi
Copy link
Contributor

The PR (#1716) that introduces the try/catch logic has been merged. Ultimately we ended up going in a slightly different direction than the one anticipated. Specifically we didn't add the try/catch to the putMetadata() method but instead we added it to our decorators and middleware.

The reason why we went this way is that after debugging the error and following the stack trace, we noticed that the error described in your issue is thrown only when a segment is emitted, specifically when its serialized, and not when the data is added to the segment. Adding the try/catch logic to the putMetadata() method wouldn't have helped mitigating this.

So we have added try/catch in those places where we call segment.close() on segments that we opened, namely the @captureMethod() and @captureLambdaHandler() decorators and the captureLambdaHandler() Middy middleware.

Unfortunately, until X-Ray merges the fix we are proposing, calling segment.close() on a segment directly will still throw an error.

At the time of writing, we are still waiting on the X-Ray team for the issue we've opened so I'm leaving the issue open and changing its state to status/blocked. I'll report back here once there's movement.

@dreamorosi
Copy link
Contributor

I just wanted to report that I have managed to get through the X-Ray team and we have agreed on a fix. I'll be opening a PR in their repo in the next few days.

@dreamorosi dreamorosi added blocked This item's progress is blocked by external dependency or reason and removed pending-release This item has been merged and will be released soon labels Oct 18, 2023
@dreamorosi
Copy link
Contributor

The PR is up: aws/aws-xray-sdk-node#619

@misterjoshua
Copy link
Contributor Author

The PR is up: aws/aws-xray-sdk-node#619

Thanks, Andrea. I pulled down a local copy, tested it, and left a small review.

@misterjoshua
Copy link
Contributor Author

@dreamorosi Looks like The X-Ray SDK for Node released the BigInt serialization change in v3.5.3.

I updated my personal reproduction to: @aws-lambda-powertools/[email protected], [email protected]

After re-running the lambda, my trace now contains the bigint.

image

So, I think we're done here unless you can think of anything else?

@dreamorosi
Copy link
Contributor

Hi @misterjoshua thanks for reporting back on the topic.

I'll bump the version of the X-Ray SDK in the Tracer dependencies to the latest and close the issue.

@dreamorosi dreamorosi added confirmed The scope is clear, ready for implementation and removed blocked This item's progress is blocked by external dependency or reason labels Oct 30, 2023
@dreamorosi dreamorosi moved this from On hold to Working on it in Powertools for AWS Lambda (TypeScript) Oct 30, 2023
@github-project-automation github-project-automation bot moved this from Working on it to Coming soon in Powertools for AWS Lambda (TypeScript) Oct 30, 2023
@github-actions
Copy link
Contributor

⚠️ COMMENT VISIBILITY WARNING ⚠️

Comments on closed issues are hard for our team to see.
If you need more assistance, please either tag a team member or open a new issue that references this one.
If you wish to keep having a conversation with other community members under this issue feel free to do so.

@github-actions github-actions bot added pending-release This item has been merged and will be released soon and removed confirmed The scope is clear, ready for implementation labels Oct 30, 2023
Copy link
Contributor

github-actions bot commented Nov 1, 2023

This is now released under v1.14.1 version!

@github-actions github-actions bot added completed This item is complete and has been merged/shipped and removed pending-release This item has been merged and will be released soon labels Nov 1, 2023
@dreamorosi dreamorosi moved this from Coming soon to Shipped in Powertools for AWS Lambda (TypeScript) Nov 2, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
completed This item is complete and has been merged/shipped feature-request This item refers to a feature request for an existing or new utility tracer This item relates to the Tracer Utility
Projects
2 participants