Skip to content

Commit

Permalink
Setup Sentry instrumentation on top of the existing Signposter (#2985)
Browse files Browse the repository at this point in the history
* Move Sentry setup outside of the BugReportService

* Setup Sentry SwiftUI tracing for the homeScreen and roomScreen roots

* Setup Sentry instrumentation on top of the existing Signposter

* Various tweaks
  • Loading branch information
stefanceriu authored Jun 28, 2024
1 parent 7e999f0 commit 3c50088
Show file tree
Hide file tree
Showing 14 changed files with 149 additions and 177 deletions.
34 changes: 21 additions & 13 deletions ElementX.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -226,14 +226,14 @@
368C8758FCD079E6AAA18C2C /* NoticeRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5B243E7818E5E9F6A4EDC7A /* NoticeRoomTimelineView.swift */; };
369BF960E52BBEE61F8A5BD1 /* BlockedUsersScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED60E4D2CD678E1EBF16F77A /* BlockedUsersScreen.swift */; };
36AC963F2F04069B7FF1AA0C /* UIConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E6D88E8AFFBF2C1D589C0FA /* UIConstants.swift */; };
36AD4DD4C798E22584ED3200 /* Version in Frameworks */ = {isa = PBXBuildFile; productRef = A05AF81DDD14AD58CB0E1B9B /* Version */; };
36CD6E11B37396E14F032CB6 /* Emojibase in Frameworks */ = {isa = PBXBuildFile; productRef = C05729B1684C331F5FFE9232 /* Emojibase */; };
36AD4DD4C798E22584ED3200 /* SentrySwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = 75361A9D8A3C5501EADB225D /* SentrySwiftUI */; };
36CD6E11B37396E14F032CB6 /* Version in Frameworks */ = {isa = PBXBuildFile; productRef = A05AF81DDD14AD58CB0E1B9B /* Version */; };
36DE961B784087D5E18EF9BA /* LogViewerScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A07692536D66E3DA32C4964 /* LogViewerScreen.swift */; };
370AF5BFCD4384DD455479B6 /* ElementCallWidgetDriverProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6C11AD9813045E44F950410 /* ElementCallWidgetDriverProtocol.swift */; };
377980ABF16525114E72DDE2 /* Version in Frameworks */ = {isa = PBXBuildFile; productRef = 2B9ACE4FCACB5A8812154424 /* Version */; };
37906355E207DB5703754675 /* AppLockSetupBiometricsScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9F893F4A111CB7BA5C96949 /* AppLockSetupBiometricsScreenViewModel.swift */; };
37D789F24199B32E3FD1AA7B /* FileRoomTimelineItemContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 216F0DDC98F2A2C162D09C28 /* FileRoomTimelineItemContent.swift */; };
37E47F5101C0C036289D3807 /* DSWaveformImageViews in Frameworks */ = {isa = PBXBuildFile; productRef = 2A4106A0A96DC4C273128AA5 /* DSWaveformImageViews */; };
37E47F5101C0C036289D3807 /* SwiftOGG in Frameworks */ = {isa = PBXBuildFile; productRef = 391D11F92DFC91666AA1503F /* SwiftOGG */; };
384D6B9A7DFD7260139D6852 /* UITestsNotificationCenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBEB8D9F4940E161B18FE4BC /* UITestsNotificationCenter.swift */; };
38546A6010A2CF240EC9AF73 /* BindableState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EA1D2CBAEA5D0BD00B90D1B /* BindableState.swift */; };
386720B603F87D156DB01FB2 /* VoiceMessageMediaManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40076C770A5FB83325252973 /* VoiceMessageMediaManager.swift */; };
Expand Down Expand Up @@ -285,10 +285,11 @@
44121202B4A260C98BF615A7 /* RoomMembersListScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5B7A755E985FA14469E86B2 /* RoomMembersListScreenUITests.swift */; };
44BDD670FF9095ACE240A3A2 /* VoiceMessageMediaManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC4F10BDD56FA77FEC742333 /* VoiceMessageMediaManagerTests.swift */; };
44DA28B1E1F9C97C5795F7B3 /* AppLockSetupUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8A1BBEF7318CA6B6ACCF4AE /* AppLockSetupUITests.swift */; };
44F0E1B576C7599DF8022071 /* Prefire in Frameworks */ = {isa = PBXBuildFile; productRef = 2629CF48B33643CD5F69C612 /* Prefire */; };
44F0E1B576C7599DF8022071 /* WysiwygComposer in Frameworks */ = {isa = PBXBuildFile; productRef = CA07D57389DACE18AEB6A5E2 /* WysiwygComposer */; };
454311EAC17D778E19F46592 /* NotificationPermissionsScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91868EB98818044E6FEBE532 /* NotificationPermissionsScreenCoordinator.swift */; };
454F8DDC4442C0DE54094902 /* LABiometryType.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3F219838588C62198E726E3 /* LABiometryType.swift */; };
4557192F5B15A8D9BB920232 /* AdvancedSettingsScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E492690C8B27A892C194CC4 /* AdvancedSettingsScreenCoordinator.swift */; };
4610C57A4785FFF5E67F0C6D /* DSWaveformImageViews in Frameworks */ = {isa = PBXBuildFile; productRef = 2A4106A0A96DC4C273128AA5 /* DSWaveformImageViews */; };
46562110EE202E580A5FFD9C /* RoomScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93CF7B19FFCF8EFBE0A8696A /* RoomScreenViewModelTests.swift */; };
4681820102DAC8BA586357D4 /* VoiceMessageCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAB8D7926A5684E18196B538 /* VoiceMessageCache.swift */; };
46A183C6125A669AEB005699 /* UserProfileScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = F134D2D91DFF732FB75B2CB7 /* UserProfileScreenViewModelProtocol.swift */; };
Expand Down Expand Up @@ -679,7 +680,7 @@
A009BDFB0A6816D4C392ADCB /* SettingsScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AF715D4FD4710EBB637D661 /* SettingsScreenViewModelProtocol.swift */; };
A021827B528F1EDC9101CA58 /* AppCoordinatorProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = FBC776F301D374A3298C69DA /* AppCoordinatorProtocol.swift */; };
A0A0D2A9564BDA3FDE2E360F /* FormattedBodyText.swift in Sources */ = {isa = PBXBuildFile; fileRef = F73FF1A33198F5FAE9D34B1F /* FormattedBodyText.swift */; };
A0D7E5BD0298A97DCBDCE40B /* WysiwygComposer in Frameworks */ = {isa = PBXBuildFile; productRef = CA07D57389DACE18AEB6A5E2 /* WysiwygComposer */; };
A0D7E5BD0298A97DCBDCE40B /* Emojibase in Frameworks */ = {isa = PBXBuildFile; productRef = C05729B1684C331F5FFE9232 /* Emojibase */; };
A10D6CCDE2010C09EEA1A593 /* HomeScreenRoomList.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7661EFFCAA307A97D71132A /* HomeScreenRoomList.swift */; };
A14A9419105A1CD42F0511C4 /* UserIndicatorModalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E43005941B3A2C9671E23C85 /* UserIndicatorModalView.swift */; };
A17FAD2EBC53E17B5FD384DB /* InviteUsersScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22730A30C50AC2E3D5BA8642 /* InviteUsersScreenViewModelProtocol.swift */; };
Expand Down Expand Up @@ -719,7 +720,7 @@
A851635B3255C6DC07034A12 /* RoomScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8108C8F0ACF6A7EB72D0117 /* RoomScreenCoordinator.swift */; };
A896998A6784DB6F16E912F4 /* MockMediaLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AB7D7DAAAF662DED9D02379 /* MockMediaLoader.swift */; };
A8FA7671948E3DF27F320026 /* BugReportFlowCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7367B3B9A8CAF902220F31D1 /* BugReportFlowCoordinator.swift */; };
A93661C962B12942C08864B6 /* SwiftOGG in Frameworks */ = {isa = PBXBuildFile; productRef = 391D11F92DFC91666AA1503F /* SwiftOGG */; };
A93661C962B12942C08864B6 /* Prefire in Frameworks */ = {isa = PBXBuildFile; productRef = 2629CF48B33643CD5F69C612 /* Prefire */; };
A9482B967FC85DA611514D35 /* VoiceMessageRoomPlaybackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CCD41CD67DB5DA0D436BFE9 /* VoiceMessageRoomPlaybackView.swift */; };
A969147E0EEE0E27EE226570 /* MediaUploadPreviewScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47F29139BC2A804CE5E0757E /* MediaUploadPreviewScreenViewModel.swift */; };
A975D60EA49F6AF73308809F /* RoomMembersListScreenMemberCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC03209FDE8CE0810617BFFF /* RoomMembersListScreenMemberCell.swift */; };
Expand Down Expand Up @@ -2260,12 +2261,13 @@
EAC6FE2CD4F50A43068ADCD8 /* SwiftState in Frameworks */,
754602A7B2AAD443C4228ED4 /* GZIP in Frameworks */,
B0CB16349B96262AA65A04AF /* Sentry in Frameworks */,
36AD4DD4C798E22584ED3200 /* Version in Frameworks */,
36CD6E11B37396E14F032CB6 /* Emojibase in Frameworks */,
A0D7E5BD0298A97DCBDCE40B /* WysiwygComposer in Frameworks */,
44F0E1B576C7599DF8022071 /* Prefire in Frameworks */,
A93661C962B12942C08864B6 /* SwiftOGG in Frameworks */,
37E47F5101C0C036289D3807 /* DSWaveformImageViews in Frameworks */,
36AD4DD4C798E22584ED3200 /* SentrySwiftUI in Frameworks */,
36CD6E11B37396E14F032CB6 /* Version in Frameworks */,
A0D7E5BD0298A97DCBDCE40B /* Emojibase in Frameworks */,
44F0E1B576C7599DF8022071 /* WysiwygComposer in Frameworks */,
A93661C962B12942C08864B6 /* Prefire in Frameworks */,
37E47F5101C0C036289D3807 /* SwiftOGG in Frameworks */,
4610C57A4785FFF5E67F0C6D /* DSWaveformImageViews in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -5333,6 +5335,7 @@
9573B94B1C86C6DF751AF3FD /* SwiftState */,
997C7385E1A07E061D7E2100 /* GZIP */,
7731767AE437BA3BD2CC14A8 /* Sentry */,
75361A9D8A3C5501EADB225D /* SentrySwiftUI */,
A05AF81DDD14AD58CB0E1B9B /* Version */,
C05729B1684C331F5FFE9232 /* Emojibase */,
CA07D57389DACE18AEB6A5E2 /* WysiwygComposer */,
Expand Down Expand Up @@ -7450,7 +7453,7 @@
repositoryURL = "https://github.com/getsentry/sentry-cocoa";
requirement = {
kind = upToNextMinorVersion;
minimumVersion = 8.13.0;
minimumVersion = 8.30.0;
};
};
AC3475112CA40C2C6E78D1EB /* XCRemoteSwiftPackageReference "matrix-analytics-events" */ = {
Expand Down Expand Up @@ -7684,6 +7687,11 @@
package = A08925A9D5E3770DEB9D8509 /* XCRemoteSwiftPackageReference "sentry-cocoa" */;
productName = Sentry;
};
75361A9D8A3C5501EADB225D /* SentrySwiftUI */ = {
isa = XCSwiftPackageProductDependency;
package = A08925A9D5E3770DEB9D8509 /* XCRemoteSwiftPackageReference "sentry-cocoa" */;
productName = SentrySwiftUI;
};
7731767AE437BA3BD2CC14A8 /* Sentry */ = {
isa = XCSwiftPackageProductDependency;
package = A08925A9D5E3770DEB9D8509 /* XCRemoteSwiftPackageReference "sentry-cocoa" */;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,8 +193,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/getsentry/sentry-cocoa",
"state" : {
"revision" : "0a915b93ff3abee1a9752448e47808334d306980",
"version" : "8.13.0"
"revision" : "8fd4e804f2e72e0b9c1b189ce4e8349c4d10b6a2",
"version" : "8.30.0"
}
},
{
Expand Down
85 changes: 72 additions & 13 deletions ElementX/Sources/Application/AppCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import BackgroundTasks
import Combine
import Intents
import MatrixRustSDK
import Sentry
import SwiftUI
import Version

Expand Down Expand Up @@ -96,17 +97,6 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg

Self.setupServiceLocator(appSettings: appSettings)

ServiceLocator.shared.analytics.isRunningPublisher
.removeDuplicates()
.sink { isRunning in
if isRunning {
ServiceLocator.shared.bugReportService.start()
} else {
ServiceLocator.shared.bugReportService.stop()
}
}
.store(in: &cancellables)

ServiceLocator.shared.analytics.startIfEnabled()

stateMachine = AppCoordinatorStateMachine()
Expand Down Expand Up @@ -150,6 +140,17 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg

registerBackgroundAppRefresh()

ServiceLocator.shared.analytics.isRunningPublisher
.removeDuplicates()
.sink { [weak self] isRunning in
if isRunning {
self?.setupSentry()
} else {
self?.teardownSentry()
}
}
.store(in: &cancellables)

elementCallService.actions
.receive(on: DispatchQueue.main)
.sink { [weak self] action in
Expand Down Expand Up @@ -344,7 +345,6 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
ServiceLocator.shared.register(appSettings: appSettings)
ServiceLocator.shared.register(networkMonitor: NetworkMonitor())
ServiceLocator.shared.register(bugReportService: BugReportService(withBaseURL: appSettings.bugReportServiceBaseURL,
sentryURL: appSettings.bugReportSentryURL,
applicationId: appSettings.bugReportApplicationId,
sdkGitSHA: sdkGitSha(),
maxUploadSize: appSettings.bugReportMaxUploadSize))
Expand Down Expand Up @@ -733,6 +733,62 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
}
}

private func setupSentry() {
SentrySDK.start { [weak self] options in
#if DEBUG
options.enabled = false
#endif

options.dsn = self?.appSettings.bugReportSentryURL.absoluteString

// Sentry swizzling shows up quite often as the heaviest stack trace when profiling
// We don't need any of the features it powers (see docs)
options.enableSwizzling = false

// WatchdogTermination is currently the top issue but we've had zero complaints
// so it might very well just all be false positives
options.enableWatchdogTerminationTracking = false

// Disabled as it seems to report a lot of false positives
options.enableAppHangTracking = false

// Most of the network requests are made Rust side, this is useless
options.enableNetworkBreadcrumbs = false

// Doesn't seem to work at all well with SwiftUI
options.enableAutoBreadcrumbTracking = false

// Experimental. Stitches stack traces of asynchronous code together
options.swiftAsyncStacktraces = true

// Uniform sample rate: 1.0 captures 100% of transactions
// In Production you will probably want a smaller number such as 0.5 for 50%
if AppSettings.isDevelopmentBuild {
options.sampleRate = 1.0
options.tracesSampleRate = 1.0
options.profilesSampleRate = 1.0
} else {
options.sampleRate = 0.5
options.tracesSampleRate = 0.5
options.profilesSampleRate = 0.5
}

// This callback is only executed once during the entire run of the program to avoid
// multiple callbacks if there are multiple crash events to send (see method documentation)
options.onCrashedLastRun = { event in
MXLog.error("Sentry detected a crash in the previous run: \(event.eventId.sentryIdString)")
ServiceLocator.shared.bugReportService.lastCrashEventID = event.eventId.sentryIdString
}

MXLog.info("SentrySDK started")
}
}

private func teardownSentry() {
SentrySDK.close()
MXLog.info("SentrySDK stopped")
}

// MARK: Toasts and loading indicators

private static let loadingIndicatorIdentifier = "\(AppCoordinator.self)-Loading"
Expand Down Expand Up @@ -762,7 +818,10 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
private func startSync() {
guard let userSession else { return }

ServiceLocator.shared.analytics.signpost.beginFirstSync()
// FIXME: replace this with `user_id_server_name` from https://github.com/matrix-org/matrix-rust-sdk/pull/3617
let serverName = String(userSession.clientProxy.userID.split(separator: ":").last ?? "Unknown")

ServiceLocator.shared.analytics.signpost.beginFirstSync(serverName: serverName)
userSession.clientProxy.startSync()

guard clientProxyObserver == nil else {
Expand Down
76 changes: 1 addition & 75 deletions ElementX/Sources/Mocks/Generated/GeneratedMocks.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1815,87 +1815,13 @@ class AudioSessionMock: AudioSessionProtocol {
}
}
class BugReportServiceMock: BugReportServiceProtocol {
var isRunning: Bool {
get { return underlyingIsRunning }
set(value) { underlyingIsRunning = value }
}
var underlyingIsRunning: Bool!
var crashedLastRun: Bool {
get { return underlyingCrashedLastRun }
set(value) { underlyingCrashedLastRun = value }
}
var underlyingCrashedLastRun: Bool!
var lastCrashEventID: String?

//MARK: - start

var startUnderlyingCallsCount = 0
var startCallsCount: Int {
get {
if Thread.isMainThread {
return startUnderlyingCallsCount
} else {
var returnValue: Int? = nil
DispatchQueue.main.sync {
returnValue = startUnderlyingCallsCount
}

return returnValue!
}
}
set {
if Thread.isMainThread {
startUnderlyingCallsCount = newValue
} else {
DispatchQueue.main.sync {
startUnderlyingCallsCount = newValue
}
}
}
}
var startCalled: Bool {
return startCallsCount > 0
}
var startClosure: (() -> Void)?

func start() {
startCallsCount += 1
startClosure?()
}
//MARK: - stop

var stopUnderlyingCallsCount = 0
var stopCallsCount: Int {
get {
if Thread.isMainThread {
return stopUnderlyingCallsCount
} else {
var returnValue: Int? = nil
DispatchQueue.main.sync {
returnValue = stopUnderlyingCallsCount
}

return returnValue!
}
}
set {
if Thread.isMainThread {
stopUnderlyingCallsCount = newValue
} else {
DispatchQueue.main.sync {
stopUnderlyingCallsCount = newValue
}
}
}
}
var stopCalled: Bool {
return stopCallsCount > 0
}
var stopClosure: (() -> Void)?

func stop() {
stopCallsCount += 1
stopClosure?()
}
//MARK: - submitBugReport

var submitBugReportProgressListenerUnderlyingCallsCount = 0
Expand Down
1 change: 1 addition & 0 deletions ElementX/Sources/Screens/HomeScreen/View/HomeScreen.swift
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ struct HomeScreen: View {
gradientView.alpha = 1
}
}
.sentryTrace("\(Self.self)")
}

// MARK: - Private
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
//

import Compound
import SentrySwiftUI
import SwiftUI

struct HomeScreenContent: View {
Expand All @@ -29,6 +30,7 @@ struct HomeScreenContent: View {
migrationView
default:
roomList
.sentryTrace("\(Self.self)")
}
}

Expand Down
1 change: 1 addition & 0 deletions ElementX/Sources/Screens/RoomScreen/View/RoomScreen.swift
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ struct RoomScreen: View {
context.send(viewAction: .handlePasteOrDrop(provider: provider))
return true
}
.sentryTrace("\(Self.self)")
}

private var timeline: some View {
Expand Down
2 changes: 1 addition & 1 deletion ElementX/Sources/Services/Analytics/AnalyticsService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class AnalyticsService {

/// A signpost client for performance testing the app. This client doesn't respect the
/// `isRunning` state or behave any differently when `start`/`reset` are called.
let signpost = Signposter()
let signpost = Signposter(isDevelopmentBuild: AppSettings.isDevelopmentBuild)

/// Whether or not the object is enabled and sending events to the server.
private let isRunningSubject: CurrentValueSubject<Bool, Never> = .init(false)
Expand Down
Loading

0 comments on commit 3c50088

Please sign in to comment.