-
Notifications
You must be signed in to change notification settings - Fork 1
CORE-951 - Use beforeCapture
to set level in Sentry scope
#89
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
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These changes look fine but looking at the file I'm not sure how calling setError actually gets reported to sentry, where does that happen?
Nevermind, I see it's called here, we aren't calling setError externally we're calling that Line 17 in be1777d
|
src/components/ErrorBoundary.tsx
Outdated
@@ -20,6 +20,11 @@ const defaultErrorFallbacks = { | |||
</Error> | |||
}; | |||
|
|||
const errorLevelByType: Record<string, Sentry.SeverityLevel> = { | |||
'ScoresSyncError': 'warning', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ScoresSyncError
is only defined within lti-gateway, we should make this solution extensible so that the implementing app can specify the error levels when it defines the error displays
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@TomWoodward That the error is defined in assignments.
I considered an alternative approach of having the other packages attach additional metadata, such as level
, to their errors and then referencing that metadata here. That would have prevented direct dependence on error names. I used this approach to be consistent. This is already handling SessionExpiredError
in a similar manner in the fallback rendering. That error is defined in ts-utils.
If the other approach is preferable, then I wonder if I should try to change the fallback rendering too. Maybe that should be a different issue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think to avoid a massive refactor of the dependencies, it might be best to pass a map of error names and log levels into the boundary as a separate prop
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, but I think that would still require refactoring each instance of ErrorBoundary
that could encounter SessionExpiredError
or ScoresSyncError
. That is, unless we want to have a default, but then we have the same problem with assumptions about error TYPE
remaining the same. If we added level
to some errors, specifically only the ones we want reported as warnings, then we could use that level if it exists, or default to reporting them as errors. My approach would require less refactoring and have a more global effect, but yours would be more explicit and potentially easier to follow.
Maybe I am assuming too much. The issue makes it sound like these errors should always have level set to warning. It sounds like you may want more granular control.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right now I have a separate prop for error levels map with a default (similar to default fallbacks). I still feel like this is kind of brittle since there's nothing that would tell us if the TYPE
of ScoresSyncError
changes. Personally, I would prefer to define a default error level on the error class, like TYPE
, and use that. Then we could keep this error levels map as a way to override the level set on the error class or have a different level for some errors in particular one-off cases. This would allow for maximum granularity while also reducing the number of assumptions made in ui-components. Here's an example of how it might work:
// ... (assignments)
export class ScoresSyncError extends Error {
static readonly TYPE = 'ScoresSyncError';
static readonly DEFAULT_LEVEL = 'warning';
failures: Failure[];
totalGrades: number;
constructor(failures: Failure[], totalGrades: number ) {
const uniqueMessages = uniq(failures.map((failure) => failure.message));
super(uniqueMessages.length === 1 ? uniqueMessages[0] : `${uniqueMessages.length} errors encountered`);
this.failures = failures;
this.totalGrades = totalGrades;
}
}
// ...
// ... (ui-components)
if (error) {
const type = getTypeFromError(error);
const defaultLevel = getDefaultLevelFromError(error);
// If we find a level in the map, that takes priority over default
const errorLevel = errorLevels[type];
if (errorLevel) {
scope.setLevel(errorLevel);
} else if (defaultLevel) {
scope.setLevel(defaultLevel);
}
// otherwise this would be an error.
}
That would mean that we would not need to reference the TYPE
in ui-components, but we could still do it that way in special situations from the other packages. The only downside I can think of to this approach is that it might be confusing to have multiple ways to set error level.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
adding a LOG_LEVEL
static to the error class is fine with me if thats how you would prefer to do it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@TomWoodward I ran into some problems accessing static members. I read a bit about what causes these problems and that made me hesitant to add more static members like TYPE
. It also prompted me to check my initial assumption that TYPE
could be used to map to error level. Using TYPE
to map back to an error level does seem to work as expected.
If the current approach--having a default map of error names and log levels with a prop that supports overriding the defaults--looks good to you, then I would rather do it this way and avoid adding more static members to the errors. It is a little bit brittle, but it's also more explicit, probably adds less to the cognitive load, and requires less refactoring.
Only set level if there is a reason to (override exists)
af64539
to
6252c7c
Compare
6252c7c
to
037ac0f
Compare
037ac0f
to
95d9c12
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
waiting for revisions
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i still don't want to reference ScoresSyncError
in this repo because its not defined here. i think SessionExpired might be defined here, or else its so generic (defined in ts-utils maaybe?) that its used everywhere, but the scores sync error is only relevant in assignments i think
OK, I can see your point there. I will make a PR for assignments as part of this issue to add the error levels mapping. As for the problems I ran into with static member access: I wanted to add an error for unit testing, so I did something like this: // src/components/ErrorBoundary.spec.tsx
class WarningError extends Error {
static readonly LOG_LEVEL = 'warning';
constructor(readonly a: string, readonly b: string) { }
} When it came time to test, however, I could not get
The problem was that I even tried to copy/paste export const errorIsType = <T extends {TYPE: string}>(t: T) => (e: any): e is T =>
e instanceof Error
&& (e as any).constructor.TYPE === t.TYPE;
// ...
export class WarningError extends Error {
static readonly TYPE = 'WarningError';
constructor(stuff: number, readonly otherStuff: string) {
super(`${stuff}`);
}
}
// ...
console.log(errorIsType(WarningError)(new WarningError(20, 'otherstuff'))); // false I also tried adding an instance method that returns the static member: export class WarningError extends global.Error {
static readonly TYPE = 'WarningError';
constructor(stuff: number, readonly otherStuff: string) {
super(`${stuff}`);
}
getType() {
return WarningError.TYPE;
}
} Then I tried a variety of approaches to getting the instance method and instance properties. I was able to get the instance property, // I create and throw the error in the unit test like this:
const error = new WarningError(20, 'otherstuff');
const WarningErrorComponent = () => {
throw error;
};
// Then I log details of each error like this:
console.log(
Reflect.get(error, 'otherStuff'),
Reflect.get(error, 'getType'),
errorIsType(WarningError)(error)
); The result looks like:
I tried an integration test by using this version of ui-components in Assignments and throwing an error in the book selection. In that case the type of the error was correctly recognized. My theories of why this happens are:
There was an issue filed with jest where jest was hijacking |
I moved the mapping for |
https://openstax.atlassian.net/browse/CORE-951
Only set level if there is a reason to (override exists)
...and a helpful link to the source too.