Sentry-javascript: Handling multiple user contexts

Created on 23 Jul 2019  路  7Comments  路  Source: getsentry/sentry-javascript

Hi,

I was wondering how to add a user context to something like

Async.each(users, (user, callback) => {

    Sentry.configureScope((scope) => {

        scope.setUser(user);
    });

    // Do something async for the user
    // capture error for user in/after that async function
    // Resulting user context will be the last user
}, callback);

I've tried creating a specific hub but that had the same issue

const userSpecificHub = new Sentry.Hub(Sentry.getCurrentHub().getClient());
userSpecificHub.run(() => {/* above example code here */})

Trying to do userSpecificHub.configureScope to set the user resulted in the second hub/scope to have no user context.

I'm probably doing something wrong but I'm trying to add some context to some background processes (most of them get started by https://github.com/kelektiv/node-cron/)

Question

Most helpful comment

Do you have access to the user throughout the whole life-span of that async function and you capture errors manually? Or do you want to rely on integrations?

If former, then it should be:

const users = [
  { id: 0 },
  { id: 1 },
  { id: 2 },
  { id: 3 },
  { id: 4 },
  { id: 5 },
  { id: 6 }
];

const processUser = user => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (user.id % 2 === 0) {
        Sentry.withScope(scope => {
          scope.setUser(user);
          Sentry.captureException(new Error("whoops"));
        });
      }
      resolve();
    }, Math.random() * 1000);
  });
};

Promise.all(users.map(processUser)).then(() => {
  console.log("done");
});

However, if you want to have a separate hub for a whole life-span and modify it in various places, then this will work:

const users = [
  { id: 0 },
  { id: 1 },
  { id: 2 },
  { id: 3 },
  { id: 4 },
  { id: 5 },
  { id: 6 }
];

const processUser = user => {
  return new Promise((resolve, reject) => {
    const hub = new Sentry.Hub(Sentry.getCurrentHub().getClient());
    hub.configureScope(scope => scope.setUser(user));

    setTimeout(() => {
      if (user.id % 2 === 0) {
        hub.captureException(new Error("whoops"));
      }
      resolve();
    }, Math.random() * 1000);
  });
};

Promise.all(users.map(processUser)).then(() => {
  console.log("done");
});

All 7 comments

Do you have access to the user throughout the whole life-span of that async function and you capture errors manually? Or do you want to rely on integrations?

If former, then it should be:

const users = [
  { id: 0 },
  { id: 1 },
  { id: 2 },
  { id: 3 },
  { id: 4 },
  { id: 5 },
  { id: 6 }
];

const processUser = user => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (user.id % 2 === 0) {
        Sentry.withScope(scope => {
          scope.setUser(user);
          Sentry.captureException(new Error("whoops"));
        });
      }
      resolve();
    }, Math.random() * 1000);
  });
};

Promise.all(users.map(processUser)).then(() => {
  console.log("done");
});

However, if you want to have a separate hub for a whole life-span and modify it in various places, then this will work:

const users = [
  { id: 0 },
  { id: 1 },
  { id: 2 },
  { id: 3 },
  { id: 4 },
  { id: 5 },
  { id: 6 }
];

const processUser = user => {
  return new Promise((resolve, reject) => {
    const hub = new Sentry.Hub(Sentry.getCurrentHub().getClient());
    hub.configureScope(scope => scope.setUser(user));

    setTimeout(() => {
      if (user.id % 2 === 0) {
        hub.captureException(new Error("whoops"));
      }
      resolve();
    }, Math.random() * 1000);
  });
};

Promise.all(users.map(processUser)).then(() => {
  console.log("done");
});

The second option seems what I want but couldn't get to work.
Is there a difference between using callbacks and promises? As that is the main difference I see with your example and my attempts :P
I will try again with your suggestion, thanks!

Is there a difference between using callbacks and promises?

As long as you dont do a for-loop and mess up your functions scope, then no, not at all :p

I'm not a 100% sure what you mean with don't do a for-loop :/

for (var i = 0; i < 10; i++) {
  // mimick async call
  setTimeout(() => console.log(i))
}

This will print 10 ten times, and if you do it with hub, it'll override it as well.

This will preserve correct scoping:

for (var i = 0; i < 10; i++) {
  (function (x) {
    // mimick async call
    setTimeout(() => console.log(x))
  })(i)
}

Oh I see, yeah that would be weird to do anyway :P we use Async for those operations :D

Closing as this seems to be solved.

Was this page helpful?
0 / 5 - 0 ratings