Conversation
db8106c to
87540f7
Compare
be0b60c to
270a1d8
Compare
d6306ad to
ed1c788
Compare
| import Sentry | ||
| import CocoaLumberjack | ||
|
|
||
| struct CrashLoggingInternals { |
There was a problem hiding this comment.
This class isn't fully untangled from all the static business, so we keep an internal-only static reference around until we can get rid of it.
There was a problem hiding this comment.
I like the idea of shoving all of the stuff that we cannot easily migrate into a dedicated place to make it easier to find and change or remove later 👍
I'm addicted to nested types recently. I can see this fit nicely as one
public class CrashLogging {
/// Comment explaining why this is here...
class Internals {
// ...
}
// ...
Internals.crashLogging = self
// ...
}No need to implement it if you don't see the benefit. Just playing code golf.
There was a problem hiding this comment.
I like this idea a lot – this is done in 389e8c1
| /// A class that provides support for logging crashes. Not compatible with Objective-C. | ||
| public class CrashLogging { | ||
|
|
||
| /// A singleton is maintained, but the host application needn't be aware of its existence. |
There was a problem hiding this comment.
No more static instance internally – makes things safer, easier to test, and lets us remove a bunch of code.
| - SeeAlso: CrashLoggingDataProvider | ||
| */ | ||
| public static func start(withDataProvider dataProvider: CrashLoggingDataProvider, eventLogging: EventLogging? = nil) { | ||
| public func start() throws -> CrashLogging { |
There was a problem hiding this comment.
throws because the DSN may be invalid, and we don't want to hide that error – otherwise we might ship an app to production with no ability to track crashes
Fix a test that shouldn’t have passed
Ensure stack traces show up in manual events
Fix crash logging not working in demo app
7d5089a to
6fb784b
Compare
mokagio
left a comment
There was a problem hiding this comment.
I need to take a break, so I'm submitting this incomplete review.
So far everything looks good. I really only left nitpicks.
| import Sentry | ||
| import CocoaLumberjack | ||
|
|
||
| struct CrashLoggingInternals { |
There was a problem hiding this comment.
I like the idea of shoving all of the stuff that we cannot easily migrate into a dedicated place to make it easier to find and change or remove later 👍
I'm addicted to nested types recently. I can see this fit nicely as one
public class CrashLogging {
/// Comment explaining why this is here...
class Internals {
// ...
}
// ...
Internals.crashLogging = self
// ...
}No need to implement it if you don't see the benefit. Just playing code golf.
| // Only allow initializing this system once | ||
| guard !isStarted else { return } | ||
| isStarted = true | ||
| /// Validate the DSN ourselves before initializing, because the SentrySDK silently prints the error to the log instead of telling us if the DSN is valid |
There was a problem hiding this comment.
| /// Validate the DSN ourselves before initializing, because the SentrySDK silently prints the error to the log instead of telling us if the DSN is valid | |
| // Validate the DSN ourselves before initializing, because the SentrySDK silently prints the error to the log instead of telling us if the DSN is valid |
I'm suggesting to go from /// to // because the former is to be used for documentation comments, but this one is not an header doc of a type or method.
There was a problem hiding this comment.
the Sentry SDK silently prints the error to the log instead of telling us if the DSN is valid
Have you considered opening an issue with a feature suggestion for this.
There was a problem hiding this comment.
Yeah, I like the internals thing a lot – done in 389e8c1
There was a problem hiding this comment.
Regarding the /// vs // thing – WooCommerce started the trend of using the /// format for comments because they support markdown, links, etc (and are far smaller).
There's some of that already in the ExPlat stuff, so I've been aiming to use the /// format everywhere. However, I did have instances of // that should have been annotated with MARK:, so I've added that where appropriate.
There was a problem hiding this comment.
Okay. Maybe I'm wrong about Apple's desired purpose for /// vs //.
Anyways, it's just a style thing 👌
There was a problem hiding this comment.
Maybe I'm wrong about Apple's desired purpose
Probably not – we just abuse it for other things 😅
| switch sendErrorAndWaitStatus { | ||
| case .none: | ||
| Group {} /// An empty view | ||
| case .uploading: | ||
| Text("⏳") | ||
| case .success: | ||
| Text("✅") | ||
| case .error: | ||
| Text("⚠️") | ||
| } |
There was a problem hiding this comment.
I'm a big fan of using enums to describe mutually exclusive states in the UI. Nicely done 😄
| Button("Send Test Crash", action: sendTestCrash) | ||
| Button("Send Test Event", action: sendTestEvent) | ||
| HStack { | ||
| Button(action: sendErrorAndWait) { |
There was a problem hiding this comment.
I'm obviously over thinking this, given this is a debug view, but... as far as I can see, there is no logic to prevent running the action when the state is .uploading.
There was a problem hiding this comment.
Agreed. I had to strongly resist the urge to over-build this because it is a demo app, so I'll stay strong and leave this for now.
Though it will bug me lol
|
|
||
| XCTAssert(CrashLogging.sharedInstance.currentUser.email == testUser.email) | ||
| XCTAssert(CrashLogging.sharedInstance.cachedUser?.email == testUser.email) | ||
| XCTAssertEqual(testUser.email, event?.user?.email) |
There was a problem hiding this comment.
There was a problem hiding this comment.
No – that convention is correct!
Here, the testUser is the "constant" (ie – not the subject of the test) so the ordering is as expected.
Definitely something to nitpick – IMHO this is important for test readability so we should keep it consistent!!
There was a problem hiding this comment.
Is it though? @mokagio 's suggestion is that the convention is XCTAssert(sut, expectedConstantValue), so the constant being as the second parameter. That's also always the convention I used I think.
So if the SUT is event?.user?.email and the testUser is the "constant" (= the expected value), then the order in your code is reversed, isn't it?
| platform :ios, '12.0' | ||
|
|
||
| pod 'Automattic-Tracks-iOS', :path => '../' | ||
| pod 'Automattic-Tracks-iOS', path: '../' |
There was a problem hiding this comment.
🥳 it's the little things...
mokagio
left a comment
There was a problem hiding this comment.
Requesting changes only because of the demo project not building for iOS, which is an easy fix.
I tested on iOS and Mac with my puny Intel and it worked as described; I saw the events with my data on Sentry. I used my work email if you want to double check. 👏 👏
Love that we have a test application. There's a lot we can do to unit test the heck out of this library, but the Sentry integration will always need a bit of manual testing.
On tiny note: I couldn't find the iOS/tracks-demo folder mentioned in the description in the secrets store.
I left a lot of nitpicks, which you should take as what they are: just nitpicks.
In particular, I worry my many /// vs // suggestions will come across as overly pedantic. After adding the first couple, I felt the sunk cost pressure and kept on adding them 😳 I put them there to hopefully help with the conversion, but that's not a requirement for this PR to be merged.
| IPHONEOS_DEPLOYMENT_TARGET = 10.0; | ||
| IPHONEOS_DEPLOYMENT_TARGET = 14.0; |
There was a problem hiding this comment.
The Podfile for the demo project sets 12.0 as the deployment target. We can't use that here because of SwiftUI, but should we make them meet in the middle at 13.0?
I can see an argument for running on the latest iOS only and not have to worry about backward compatibility. At the same time, Tracks is not restricted to iOS 14.0 only, so it might be beneficial for the demo app to run against as many compatible versions of iOS as reasonable - that is, iOS 13 so we can keep test the SwiftUI-related stuff.
There was a problem hiding this comment.
Yeah seems reasonable – changed to iOS 13 in 9ed5b7a
| MACOSX_DEPLOYMENT_TARGET = 10.14; | ||
| MACOSX_DEPLOYMENT_TARGET = 11.0; |
There was a problem hiding this comment.
Similar comment as the iOS 13.0 vs 14.0 above. Should we set this as 10.15 and do the same on the Podfile?
| let crashLogging = CrashLogging(dataProvider: CrashLoggingDataSource()) | ||
|
|
||
| init() { | ||
| crashLogging.start() |
There was a problem hiding this comment.
I had to update this to a brute for the iOS demo to build.
| crashLogging.start() | |
| _ = try! crashLogging.start() |
There was a problem hiding this comment.
Shoot, sorry about that – fixed in b3ba4d2 (in a slightly different way)
There was a problem hiding this comment.
Slightly different but still effective 👍
Co-authored-by: Gio Lodi <giovanni.lodi42@gmail.com>
|
@mokagio and @AliSoftware, thanks for the feedback – it should all be addressed now – ready for another look when you're ready 😃 One last change – I didn't realize the schemes weren't shared – I've added them to the repo in 901bf1d. That should fix the issue where in testing the "Send Test Event" wouldn't work (it needs to override the |
|
Tested on iPhone Simulator (iPhone 11 Pro, iOS 13.5) and on Intel Mac. Was able to see both events in Sentry ("Send Test Event" + "Send Error and Wait") in both cases I got this log in Xcode console each time I clicked the button when testing with the iPhone Simulator app. I'm not entirely sure it's expected to especially get the "Cancelling log file attachment"? 🤔 I also still got a diff on the git clean + pod install + git diff |
That's expected – the encrypted logging system isn't injected into |
|
Thanks for the detailed instructions – I was able to replicate that issue this time! This time I think it was caused by the merge with But should be resolved now 😅 |
There was a problem hiding this comment.
Took some time to also re-read the code.
Overall LGTM; I left a couple of nits but that are optional and shouldn't block that PR.
The other nits about XCTAssertEqual(sut, expected) as convention also still apply (but are also only nits, so…)
I still left one question about the assumption for active thread that might need checking and validating before merging though (but didn't want to keep the "Request Changes" status just for this one.) so I'll let you address them before hitting the button.
| if(eventWithStackTrace.threads.firstObject.stacktrace != nil) { | ||
| eventWithStackTrace.stacktrace = eventWithStackTrace.threads.firstObject.stacktrace; | ||
| } |
There was a problem hiding this comment.
I am guessing this is based on the assumption that the real crash is always guaranteed to be in the threads.firstObject? I mean I guess that makes sense given that that's probably the thread that was active when the crash occurred, but is the currently active thread always the one that is first in this array though? 🤔
There was a problem hiding this comment.
Yeah, for now it's a reasonable assumption – this functionality is designed for logging crashes on startup so that we can log the message before another crash happens. The startup stuff is all currently on the main thread, so this should work fine for quite some time.
As part of the core project to re-work app initialization, I'm hoping to kill this code and be able to put up a UI for something like "the app crashed last time – would you like to try again or reset?" and in that time Sentry could do its thing and we could kill this code.
There was a problem hiding this comment.
put up a UI for something like "the app crashed last time – would you like to try again or reset?"
That would be such a great thing to have!
Automattic-Tracks-iOS/Crash Logging/SentryEventSerializer.swift
Outdated
Show resolved
Hide resolved
Co-authored-by: Olivier Halligon <olivier.halligon@automattic.com>
Co-authored-by: Olivier Halligon <olivier.halligon@automattic.com>
Co-authored-by: Olivier Halligon <olivier.halligon@automattic.com>
Co-authored-by: Olivier Halligon <olivier.halligon@automattic.com>
loremattei
left a comment
There was a problem hiding this comment.
I haven't re-reviewed the code as I see that @AliSoftware and @mokagio already did it.
I tested on a M1 Mac following the instructions:
☑️ Try building for Any iOS Device
☑️ Try building for My Mac, and Any Mac (Apple Silicon, Intel)
and everything (including archiving it) was successful.
I've seen a few warnings, but I suppose they are expected at this point.


Updates the Sentry library to v6, which enables:
Unfortunately, the v6 library takes away our ability to manually send events to Sentry, so this PR adds that ability back by using the API directly.
Previously to this PR, testing Sentry integration was always a bit tricky because we'd need to integrate into a host app and make weird changes to it in order to test the integration.
This PR adds the ability to test Sentry functionality from the example project.
To Test:
cdinto theTracksDemodirectory and runbundle exec pod installShared/Secrets.swiftfile:iOS/tracks-demo). You might need to update (git pull) the mobile secrets repo first.Shared/Secrets.example.swiftand fill in the missing detailsconfigurefunctionality to automate this, but it's not part of this PR in order to avoid making it any larger.tracks-demoproject with an associated stack trace.Additional M1-specific tests:
Any iOS DeviceMy Mac, andAny Mac (Apple Silicon, Intel)[ note: you may need to clean your build directory first – this tends to fail otherwise – I assume this is an Xcode bug ]I'd love one review on this from a mobile infrastructure engineer as well as a review from someone who has M1 hardware to test with.