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

feat: Add onCrashedLastRun #808

Merged
merged 21 commits into from
Nov 25, 2020
Merged

feat: Add onCrashedLastRun #808

merged 21 commits into from
Nov 25, 2020

Conversation

philipphofmann
Copy link
Member

@philipphofmann philipphofmann commented Oct 21, 2020

📜 Description

Add a callback onCrashedLastRun to SentryOptions that is called by the SDK passing the eventId
when Sentry is initialized and the last program execution terminated with a crash.

💡 Motivation and Context

We want to give our users a callback for crashes so they can ask their users to attach feedback on what happened. This would solve #699 and #629.

The API together with user feedback looks like this

SentrySDK.start { options in
    // ...
    
    // Only gets called for the first crash event
    options.onCrashedLastRun = { event in
        self.displayUserFeedback(event: event)
    }
}

// Not a sentry function. Only wraps call to user feedback
func displayUserFeedback(event: Event) {
		
    // Display UI and get feedback from the user. 

    // Then send feedback to Sentry
    let feedback = SentryUserFeedback(eventId: event.eventId)
    feedback.comments = "It broke"
    feedback.name = "John Smith"
    feedback.email = "[email protected]"
    SentrySDK.capture(feedback: feedback)
}

💚 How did you test it?

Unit tests and simulator.

📝 Checklist

  • I reviewed the submitted code
  • I added tests to verify the changes
  • I updated the CHANGELOG
  • I updated the docs if needed
  • No breaking changes

🔮 Next steps

@philipphofmann philipphofmann linked an issue Oct 21, 2020 that may be closed by this pull request
6 tasks
Add a callback onCrashedLastRun to SentryOptions that is called by the SDK passing the eventId
when Sentry is initialised and the last program execution terminated with a crash.
@philipphofmann philipphofmann force-pushed the feat/on-crashed-last-run branch from 97b0105 to 0961c89 Compare October 21, 2020 10:17
@codecov-io
Copy link

codecov-io commented Oct 21, 2020

Codecov Report

Merging #808 (008008d) into master (4647554) will increase coverage by 0.03%.
The diff coverage is 100.00%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #808      +/-   ##
==========================================
+ Coverage   93.56%   93.60%   +0.03%     
==========================================
  Files          74       74              
  Lines        3277     3297      +20     
==========================================
+ Hits         3066     3086      +20     
  Misses        211      211              
Impacted Files Coverage Δ
Sources/Sentry/SentryClient.m 93.40% <100.00%> (+0.58%) ⬆️
Sources/Sentry/SentryHub.m 93.10% <100.00%> (ø)
Sources/Sentry/SentryOptions.m 100.00% <100.00%> (ø)
Sources/Sentry/SentrySDK.m 67.44% <100.00%> (+0.77%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 4647554...008008d. Read the comment docs.

@philipphofmann philipphofmann requested a review from a team October 21, 2020 10:28
@philipphofmann philipphofmann linked an issue Oct 21, 2020 that may be closed by this pull request
options.beforeSend = { event in
return event
}

options.onCrashedLastRun = { eventId in
Copy link
Member Author

@philipphofmann philipphofmann Oct 22, 2020

Choose a reason for hiding this comment

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

h: What happens if there are multiple crash reports? Maybe it would be better to replace it with a property SentrySDK.lastCrashEventId, which is set internally. When multiple crash reports are converted to an event, this is going to be overwritten.

Copy link

Choose a reason for hiding this comment

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

A callback is not strictly necessary. The app can simply check for SentrySDK.lastCrashEventId at startup and if something is returned will know to prompt the user and submit the feedback for that event.

Copy link
Member

Choose a reason for hiding this comment

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

You'd need to know the eventId somehow. SentrySDK.lastEventId wouldn't have the value after restart, at least not in its current form

Copy link

Choose a reason for hiding this comment

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

Understood (that's what trigger my initial request #699). This value needs to be exposed somehow. The fact that the sdk can now submit to the feedback api is gravy but getting the eventId is the key.

Copy link
Member Author

@philipphofmann philipphofmann Nov 12, 2020

Choose a reason for hiding this comment

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

A property won't work really, because it might take a few milliseconds until it is initialized. This happens because the SentryCrashIntegration is converting crash reports on a background thread to SentryEvents. So it wouldn't be really handy to wait for it to be initialized and you don't have a friendly way to achieve this. Instead, I would propose that the onCrashedLastRun only gets called for the first crash event. In case there are multiple crashes to send which is an edge case the others are just sent without calling the callback. What do you think about this @georges?

Copy link

Choose a reason for hiding this comment

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

in this case callback is the way to go and agree, multiple crash should be an edge case anyways. the main concern i had was if the callback fired more than once in the same session and it would keep prompting the user. if it fires only once for the latest crash, that's sufficient.

@philipphofmann philipphofmann force-pushed the feat/on-crashed-last-run branch from 992f493 to b3e49d8 Compare November 19, 2020 18:23
@philipphofmann philipphofmann marked this pull request as ready for review November 19, 2020 18:24
Comment on lines 444 to 449
NSPredicate *unhandledExpeptions = [NSPredicate predicateWithBlock:^BOOL(
id _Nullable object, NSDictionary<NSString *, id> *_Nullable bindings) {
SentryException *exception = object;
return nil != exception.mechanism && nil != exception.mechanism.handled
&& ![exception.mechanism.handled boolValue];
}];
Copy link
Contributor

@marandaneto marandaneto Nov 23, 2020

Choose a reason for hiding this comment

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

H: I think this would work for most of the cases, but it's flakey IMO.
how do we know that the event calling onCrashedLastRun is actually the one that caused the crash?

I'd suggest another approach:

SentryCrash sets fixture.crashAdapter.internalCrashedLastLaunch so it knows the event that caused this flag to be toggled.
What about caching the eventId as well that caused the app to crash and later, we filter by eventId? so we'll be sure that the onCrashedLastRun is actually called with the event that actually crashed the App.

My concern is, if the app is offline and there are multiple and distinct hard crashes, the event called onCrashedLastRun might not be the event that actually caused the last crash and the user feedback won't be accurate.

Copy link
Member Author

@philipphofmann philipphofmann Nov 24, 2020

Choose a reason for hiding this comment

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

It's very good that you think about such edge cases, but I think we can take another simpler approach.

When a crash happens, SentryCrash writes a crash-report.json to disk. The next time the app is launched, the SDK installs the SentryCrashIntegration, which calls the SentryCrashReportConverter, who converts the crash-report.json to a SentryEvent. The SentryCrashReportSink then calls [SentrySDK captureCrashEvent:event];. At that point, SentrySDK knows that it is an event with a crash. The SentryHub also has a captureCrashEvent, but the SentryClient doesn't. Therefore I suggest that the SentryHub just passes this information to the SentryClient and in callOnCrashedLastRun we can simplify the logic a lot.

Good point I'm going to change this.

Copy link
Contributor

@marandaneto marandaneto left a comment

Choose a reason for hiding this comment

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

thanks for doing this :)

I left a few comments and one of them concerns me a bit, looks good overall.

Copy link
Member

@bruno-garcia bruno-garcia left a comment

Choose a reason for hiding this comment

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

LGTM

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.

How to get event_id from crash report Feature: User Feedback
5 participants