-
Notifications
You must be signed in to change notification settings - Fork 12
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
Use Sentry flush
to send events immediately
#232
Conversation
f927fc2
to
84ce7c3
Compare
84ce7c3
to
53fd031
Compare
0a375bb
to
82475d1
Compare
} | ||
try crashLogging.logErrorImmediately( | ||
error, | ||
userInfo: ["custom-userInfo-key": "custom-userInfo-value"] |
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.
error, | ||
userInfo: ["custom-userInfo-key": "custom-userInfo-value"] | ||
) { | ||
sendErrorAndWaitStatus = .success |
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.
If we decide to adopt flush
, it might be appropriate to update this enum
to remove the error-tracking state, since there's no longer any error information with flush
.
@@ -168,54 +171,24 @@ public extension CrashLogging { | |||
} | |||
|
|||
/// Sends an `Event` to Sentry and triggers a callback on completion | |||
func logErrorImmediately(_ error: Error, userInfo: [String: Any]? = nil, level: SentryLevel = .error, callback: @escaping (Result<Bool, Error>) -> Void) throws { | |||
func logErrorImmediately(_ error: Error, userInfo: [String: Any]? = nil, level: SentryLevel = .error, callback: @escaping () -> Void) throws { |
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.
Surprisingly, I'm not getting any errors or warnings from the compiler because of these methods still having throws
despite there not being any genuine try
call in the chain.
If we commit to flush
, we should remove throws
as well.
queue.async { | ||
SentrySDK.flush(timeout: CrashLogging.eventsFlushingTimeout) | ||
callback() | ||
} |
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.
@crazytonyli what do you think of this as an implementation of your suggestion to make the call in a background queue?
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.
In regards to your suggestion to add a wait: Bool
parameter, are you thinking of something along the lines of WordPress iOS where we use a Bool
parameter to decide whether to call context.performAndWait(_:)
or context.perform(_:)
?
In this, I'm thinking if wait == true
we wouldn't run the code in queue.async
.
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.
Yes, so that we don't need to use semaphore like the original implementation 😄
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.
😕 The new diff makes this conversation odd because the code it referred to has moved. Anyways... @crazytonyli I had an initial go at adding a wait
parameter.
I did this in the form of a private
func for the other methods to call.
private func logErrorsImmediately(
_ errors: [Error],
userInfo: [String: Any]? = nil,
level: SentryLevel = .error,
andWait wait: Bool,
callback: @escaping () -> Void
) {
I didn't modify the original method signatures to avoid a breaking change, but also because I wasn't sure how it would have felt to have a andWait:,
together with a callback:
. If wait == true
, then there's no need for a callback because the code that would run in there essentially runs once the method has finished anyway.
This is based on the suggestion at #232 (comment)
flush
to send events immediatelyflush
to send events immediately
if wait { | ||
flushEventThenCallCallback() | ||
} else { | ||
let queue = DispatchQueue(label: "com.automattic.tracks.flushSentry", qos: .background) |
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.
Any reason why using a custom queue instead of DispatchQueue.global
? Also, any reason why choose .background
QoS, instead of the default one? (I almost always use the default QoS since I've heard scary things about QoS but don't really understand 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.
I followed the suggestion from one of the Sentry engineers from here.
I trust your advice on this matter, though. I'll update.
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.
let didSucceed = 200...299 ~= (urlResponse as! HTTPURLResponse).statusCode | ||
callback(.success(didSucceed)) | ||
}.resume() | ||
func logErrorsImmediately(_ errors: [Error], userInfo: [String: Any]? = nil, level: SentryLevel = .error, callback: @escaping () -> Void) throws { |
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 had a quick look, it appears we can't get the result of flush
call, is that correct? I think it would be good to at least have a "success or not" result, if we can't get a concrete error. But I guess there isn't anything we can do if Sentry doesn't expose such API.
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.
Correct. See the details given here. It's quite the let down. There must be a reason for the lack of callback in their unified API rationale, but to be honest I haven't read that documentation.
Notice that, because `flush` doesn't support callbacks but instead blocks the main thread up to a given timeout interval, we had to remove our more refined callback mechanism and UI state management logic.
This code was all custom interaction with the Sentry SDK for the purpose of extracting information to add to events sent to Sentry "manually" via HTTP requests. Now that we use Sentry's native method for that, all the information tracking is handled by the Sentry SDK.
This is based on the suggestion at #232 (comment)
The word "events" made me think it ought to belong as part of `EventLogging` which was confusing because performance monitoring is currently implemented via Sentry (crash logging) instead.
2a89afa
to
30a873e
Compare
…tatus` We no longer have error handling because of Sentry's `flush`.
None of the code left from the `flush` adoption threw errors.
The change in the `SendErrorAndWaitStatus` `enum` is not mentioned because the type is `internal`.
This PR removes our custom logic to implement the "send all enqueue events and wait" functionality in favor of using the new Sentry's native
flush
method.One big disadvantage of this approach is that we lose the precise callback management, because
flush
is blocking for as long as it takes to send all events or for a given timeout to run out.I used the demo app to verify our custom implementation works and the events it sends reach Sentry. In the screenshot below, the top two events have been sent from this branch. Also notice the third and fourth events have "(no value)" for release and environment. That's a bug introduced at some point when migrating to version Sentry version 7. I haven't looked into it yet.
I'm of two minds on whether to go for using
flush
under the hood. I really dislike howflush
doesn't provide a callback out of the box, and how it results in a less precise UI state handling on our end. At the same time, relying on Sentry's native code instead of implementing our own logic accessing their private API simplifies and de-risks the library.I'd like other folks input on this, but I'm tempted to adopt this implementation on the basis that
logErrorAndWait
,logErrorImmediately
, andlogErrorsImmediately
are used very sparingly by the library's clients. Namely, in one occasion in Jetpack/WordPress and in two occasions in WooCommerce](https://github.com/woocommerce/woocommerce-ios/search?q=logFatalErrorAndExit).