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

Propagate Sentry scope #46

Merged
merged 1 commit into from Jun 30, 2022
Merged

Propagate Sentry scope #46

merged 1 commit into from Jun 30, 2022

Conversation

segevfiner
Copy link
Contributor

Sentry keeps async local scope using the legacy domain module, it seems to get lost across pump buffering, propagate it explicitly using AsyncResource, and use withScope to create a copy of the async local scope instead of a blank one.

@segevfiner
Copy link
Contributor Author

Not sure if the withScope change is actually needed, the Sentry SDK might know to merge them when passed like this but their docs aren't the best...

@aandrewww
Copy link
Owner

Thanks for the PR
I don't have free time right now, but I'll review it in a few weeks.

src/transport.ts Outdated
this.extraAttributeKeys.forEach((key: string) => {
if(chunk[key] !== undefined) {
extra[key] = chunk[key];
}

Choose a reason for hiding this comment

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

Shouldn't the forEach close after this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yep 😬

Choose a reason for hiding this comment

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

@segevfiner cheers! By the way, we're currently using this library to add additional contextual data to sentry errors (from the logger). As it stands, none of the data is being sent to sentry. Is this the actual problem this PR is addressing?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Possibly. This fixes an issue where the Sentry SDK isolated its context to the async scope using the domain module (Which uses async_hooks internally), which caused it to not propagate scope that was set using it. It is used when using the requestHandler middleware and maybe in other cases.

Copy link
Collaborator

Choose a reason for hiding this comment

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

@jesushernandez you should create issue of your problem with steps to reproduce to make the problem visible.

Choose a reason for hiding this comment

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

I'll try to reproduce today and get back to you!

- Remove unnecessary withScope change
- Fix messed up curly brace
- Remove deep nesting

Co-authored-by: Elan Ruusamäe <glen@delfi.ee>
@glensc

This comment was marked as resolved.

@segevfiner

This comment was marked as resolved.

@glensc
Copy link
Collaborator

glensc commented Jun 28, 2022

@segevfiner can you re-test it too? or maybe even write jest test?

@segevfiner
Copy link
Contributor Author

segevfiner commented Jun 28, 2022

P.S. CI is failing due to a too old EOL version of node in the used CircleCI base image that has an npm version that doesn't even work properly anymore. The package.json states Node 10 as a minimum though while the README.md says 6.

Only Node 14 and above is maintained ATM https://nodejs.org/en/about/releases/

glensc added a commit that referenced this pull request Jun 28, 2022
@glensc
Copy link
Collaborator

glensc commented Jun 28, 2022

Fixed badge:

@segevfiner
Copy link
Contributor Author

It still seems to work by testing it manually.

Testing this automatically is a bit difficult, as we will need to create some express/connect setup with Sentry.Handlers.requestHandler() that can be unit tested, since this only takes effect when using Sentry.Handlers.requestHandlerm which sets up a domain. Without it, it seems that Sentry scopes are sync, so by trying to test this automatically, I revealed another issue. I tried this test:

// Refactor me plz ;P
test('Scope propogation', async () => {
  const options = {
    level: "info"
  };
  const mockSend = jest.fn(() => Promise.resolve());
  const stream = createWriteStream({dsn: SENTRY_DSN, transport() { return {send: mockSend, flush: jest.fn(() => Promise.resolve(true))}; }});
  const logger = pinoLogger(options, stream);

  Sentry.withScope((scope) => {
    scope.setTag('test', 'foobar');

    logger.error('test');
  });

  await new Promise<void>((resolve, reject) => {
    setImmediate(() => {
      try {
        expect(mockSend.mock.calls.length).toBe(1);
        resolve();
      } catch (error) {
        reject(error);
      }
    });
  });
});

And it only works if I apply the following change:

diff --git a/src/transport.ts b/src/transport.ts
index ea3bf0e..f229e30 100644
--- a/src/transport.ts
+++ b/src/transport.ts
@@ -162,14 +162,14 @@ export class PinoSentryTransport {
     if (this.isSentryException(severity)) {
       const error = message instanceof Error ? message : new ExtendedError({ message, stack });
 
+      Sentry.captureException(error, scope);
       setImmediate(() => {
-        Sentry.captureException(error, scope);
         cb();
       });
     } else {
       // Capturing Messages
+      Sentry.captureMessage(message, scope);
       setImmediate(() => {
-        Sentry.captureMessage(message, scope);
         cb();
       });
     }

The Sentry SDK is already async internally so it is safe to just call it directly like that AFAIK. Not sure about cb so I left that in a setImmediate

@glensc
Copy link
Collaborator

glensc commented Jun 29, 2022

I don't think using Sentry.withScope() is a good idea. I guess the decorateScope was created for that purpose to add data to current context? According to docs, withScope creates new scope, not attaching to current logging scope

altho I see the problem, using decorateScope maybe difficult to add new context in any part of the codebase if it's declaration is in logger constructor...

altho maybe something like this can be handy:

@segevfiner
Copy link
Contributor Author

It clones the current scope and creates a new one that will be reverted once the withScope callback returns. It is how Sentry documents to use itself, and as such users expect this to work. Using async_hooks is something for the Sentry SDK to figure out, they currently only do so via the deprecated domain module while using requestHandler but not otherwise.

@glensc
Copy link
Collaborator

glensc commented Jun 29, 2022

@jesushernandez can you test does this brach solve your problem?

something like:

yarn add https://github.com/swimmio/pino-sentry#propogate-scope

or

npm install https://github.com/swimmio/pino-sentry#propogate-scope

if not, can you also test the diff that @segevfiner indicated:

@glensc
Copy link
Collaborator

glensc commented Jun 30, 2022

I think here's nothing else to do, so merging.

@glensc glensc merged commit 569c776 into aandrewww:master Jun 30, 2022
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.

None yet

4 participants