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

Fix logtail/logtail-js#19 Maximum call stack size exceeded #29

Closed
wants to merge 1 commit into from

Conversation

boly38
Copy link

@boly38 boly38 commented Dec 21, 2022

Fix #19 Maximum call stack size exceeded

Closes: 19
Thanks: Yago Tomé yagotome@gmail.com


this PR is my first typescript contribution so be indulgent. I take all comments. 🙏

Copy link
Contributor

@adikus adikus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot @boly38! This looks great and makes sense.

I left some comments but most of them are fairly small nitpicks.

I'll also try to check this out and run the test. (As you've noticed we don't have CI setup just yet)

packages/node/src/node.ts Show resolved Hide resolved
packages/node/src/node.ts Outdated Show resolved Hide resolved
packages/node/src/node.ts Outdated Show resolved Hide resolved
packages/node/src/node.ts Show resolved Hide resolved
@adikus
Copy link
Contributor

adikus commented Jan 2, 2023

Test looks great ✅

boly38 added a commit to boly38/logtail-js that referenced this pull request Jan 5, 2023
Reviewed-by: Andrej Hoos <andrej.hoos@gmail.com>
Reviewed-by: gyfis <gyfis@seznam.cz>
@boly38
Copy link
Author

boly38 commented Jan 5, 2023

contributing on this package: my small return of experience: in option.

  • first: in order to augment ILogtailOptions in core package, I do need to build type, then core package but I first encountered a lots of typescript issue when running npm run build in core and type packages. I understand that I need to increase typescript version, an regenerate package-lock via npm install to fix that.. I don't understand why I had to do that update if the build is green ?

  • while working on "snapshot" version on type, then core to update node package, I need a tip. This was not easy to proceed (at least for me). Here is a minimal tips (I omit npm install everywhere..)

cd packages/types && npm install && npm run build
cd ../core && npm install && npm link ../types && npm run build
cd ../node && npm link ../core && npm link ../types && npm run build
cd ../../ && npm run test

there is maybe a shortest way to contribute.
Is it possible to produce a minimal contribute.md with howTo + requirements to build (at least) type + core + node package ; like for example this 3 previous line as example ? the goal is to guide TS noob like me :p

  • in bonus, I would like to run test (jest) for only some packages and not all all time ; this without aving to update jest filter (jest.config.js). I don't know if there is a simple way to do that. Could be added to how to contribute tips.

@adikus
Copy link
Contributor

adikus commented Jan 7, 2023

@boly38 Thanks for the feedback!

We have Lerna to manage the multiple packages, however last time it I tried to get it working, I wasn't able to.

From what I understand Lerna is supposed to create symlinks in each package's node_modules to the other Logtail packages it needs - however this functionality was broken for me (something in our config might not be just right). I was able to create those symlinks manually though and get individual tests working that way (I think it was something like ln -s ../../packages/ node_modules/@logtail e.g. from inside packages/node; EDIT: Although using the npm link command as you did is much cleaner 😅 ).

To run individual jest tests, for checking the test you wrote I just run ./node_modules/.bin/jest packages/node/src/node.test.ts.

Hope that helps you get going on this PR, but I would completely understand if you don't want to bother - in that case I'm happy to take this PR over 🙂

We definitely should spend some more time to get the Lerna setup working, because as I noted, the current setup is not great.

Thanks!

@boly38
Copy link
Author

boly38 commented Jan 8, 2023

Thanks for your tips : I just fixed base test (I think).

I will squash those 3 commit and re-push, then give you the hand on this PR.
I'm not attached to the ownership of this contribution, so feel free to amend, refactor as you wish.
What is important for me is that the fix arrives in master :)
have a good day

Closes: 19
Thanks: Yago Tomé <yagotome@gmail.com>

code review from PR logtail#29

Reviewed-by: Andrej Hoos <andrej.hoos@gmail.com>
Reviewed-by: gyfis <gyfis@seznam.cz>

fix ./node_modules/.bin/jest packages/core/src/base.test.ts
Comment on lines +110 to +114
} else if (typeof value === "object" && maxDepth < 1) {
if (this._options.contextObjectMaxDepthWarn) {
console.warn(`[Logtail] Max depth of ${this._options.contextObjectMaxDepth} reached when serializing logs. Please do not use circular references and excessive object depth in your logs.`);
}
return value.toString();// result in "[object Object]" : we refused to serialize deeper
Copy link

@yagotome yagotome Jan 9, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi there! I'm the OP of this issue, thanks a lot for following up on it.

I'm keen to share some thoughts with you guys here. I appreciate there are other reasons you need to control max depth in the context object, as the underlying serialization scheme (msgpack) also has its own limitation, as pointed out by @boly38 in this comment. However, I wanted to call out that the original problem (#19) could be handled differently, in a more accurate way. That's how I believe the problem should be tackled:

  1. Keep track of all object references (including array) seen at the function call stack -- a WeakSet could be used for that.
  2. If you visit the same object again, then you refuse to serialize it.

Just a draft snippet to make it clearer:

private sanitizeForEncoding(value: any): any {
    const dfsPath = new WeakSet(); // note the weakset to track a dfs path (objects seen in the current call stack)

    if (value === null || typeof value === "boolean" || typeof value === "number" || typeof value === "string") {
      return value;
    } else if (value instanceof Date) {
      return value.toISOString();
    } else if (Array.isArray(value)) {
      return value.map((item) => this.sanitizeForEncoding(item));
    } else if (typeof value === "object") {
      const logClone: { [key: string]: any } = {};

      Object.entries(value).forEach((item: object) => {
        const key = item[0];
        const value = item[1];

        if (dfsPath.has(value)) { // here we detected a cycle
          logClone[key] = value.toString();// result in "[object Object]" : we refused to serialize deeper
        } else {
          const result = this.sanitizeForEncoding(value);
          if (result !== undefined){
            logClone[key] = result;
          }
        }
      });

      dfsPath.delete(value); // the  value` object is leaving the call stack and will not be part of a cycle if saw again in a different dfs path

      return logClone;
    } else {
      return undefined;
    }
  }

That way, circular references would be stopped as soon as detected, instead of serializing them until they reach maximum call depth. What do you guys think? cc @adikus

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

my quick feedback :

  • ℹ️ need to append dfsPath as arg for recursive call but understand the idea of weakSet & recursion ( with doc example in addition )
  • ➕ elegent design, and fix the circular ref
  • ➖ don't fix an object without circular ref but really deep (can be unrealistic I agree)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I like the idea! I'll try to incorporate this in.

@adikus
Copy link
Contributor

adikus commented Jan 9, 2023

@boly38 Thanks! I'm currently working on #32, after I'm done there, I'll rebase this on there and finish it off. I'll also incorporate @yagotome's suggestion.

@adikus
Copy link
Contributor

adikus commented Jan 9, 2023

Moving this to #34

@adikus adikus closed this Jan 9, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add cycle detection in Node sanitization for encoding
4 participants