mirror of
https://github.com/element-hq/element-x-ios.git
synced 2025-03-10 21:39:12 +00:00
Simplify how we setup Sentry to make sure it's configured before any spans get created - they don't get reported otherwise
This commit is contained in:
parent
95d53f1edf
commit
65dd6f3496
@ -96,10 +96,6 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
|
|||||||
|
|
||||||
navigationRootCoordinator = NavigationRootCoordinator()
|
navigationRootCoordinator = NavigationRootCoordinator()
|
||||||
|
|
||||||
Self.setupServiceLocator(appSettings: appSettings, appHooks: appHooks)
|
|
||||||
|
|
||||||
ServiceLocator.shared.analytics.startIfEnabled()
|
|
||||||
|
|
||||||
stateMachine = AppCoordinatorStateMachine()
|
stateMachine = AppCoordinatorStateMachine()
|
||||||
|
|
||||||
navigationRootCoordinator.setRootCoordinator(SplashScreenCoordinator())
|
navigationRootCoordinator.setRootCoordinator(SplashScreenCoordinator())
|
||||||
@ -116,6 +112,12 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
|
|||||||
notificationManager = NotificationManager(notificationCenter: UNUserNotificationCenter.current(),
|
notificationManager = NotificationManager(notificationCenter: UNUserNotificationCenter.current(),
|
||||||
appSettings: appSettings)
|
appSettings: appSettings)
|
||||||
|
|
||||||
|
Self.setupSentry(appSettings: appSettings)
|
||||||
|
|
||||||
|
Self.setupServiceLocator(appSettings: appSettings, appHooks: appHooks)
|
||||||
|
|
||||||
|
ServiceLocator.shared.analytics.startIfEnabled()
|
||||||
|
|
||||||
windowManager.delegate = self
|
windowManager.delegate = self
|
||||||
|
|
||||||
notificationManager.delegate = self
|
notificationManager.delegate = self
|
||||||
@ -141,14 +143,10 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
|
|||||||
|
|
||||||
registerBackgroundAppRefresh()
|
registerBackgroundAppRefresh()
|
||||||
|
|
||||||
ServiceLocator.shared.analytics.isRunningPublisher
|
appSettings.$analyticsConsentState
|
||||||
.removeDuplicates()
|
.dropFirst() // Called above before configuring the ServiceLocator
|
||||||
.sink { [weak self] isRunning in
|
.sink { _ in
|
||||||
if isRunning {
|
Self.setupSentry(appSettings: appSettings)
|
||||||
self?.setupSentry()
|
|
||||||
} else {
|
|
||||||
self?.teardownSentry()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|
||||||
@ -743,55 +741,63 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func setupSentry() {
|
private static func setupSentry(appSettings: AppSettings) {
|
||||||
SentrySDK.start { [weak self] options in
|
let options: Options = .init()
|
||||||
#if DEBUG
|
|
||||||
options.enabled = false
|
#if DEBUG
|
||||||
#endif
|
options.enabled = false
|
||||||
|
#else
|
||||||
options.dsn = self?.appSettings.bugReportSentryURL.absoluteString
|
options.enabled = appSettings.analyticsConsentState == .optedIn
|
||||||
|
#endif
|
||||||
// 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.dsn = appSettings.bugReportSentryURL.absoluteString
|
||||||
options.enableSwizzling = false
|
|
||||||
|
if AppSettings.isDevelopmentBuild {
|
||||||
// WatchdogTermination is currently the top issue but we've had zero complaints
|
options.environment = "development"
|
||||||
// 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")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
SentrySDK.start(options: options)
|
||||||
|
|
||||||
|
MXLog.info("SentrySDK started")
|
||||||
}
|
}
|
||||||
|
|
||||||
private func teardownSentry() {
|
private func teardownSentry() {
|
||||||
|
@ -38,19 +38,11 @@ class AnalyticsService {
|
|||||||
|
|
||||||
/// A signpost client for performance testing the app. This client doesn't respect the
|
/// 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.
|
/// `isRunning` state or behave any differently when `start`/`reset` are called.
|
||||||
let signpost = Signposter(isDevelopmentBuild: AppSettings.isDevelopmentBuild)
|
let signpost = Signposter()
|
||||||
|
|
||||||
/// Whether or not the object is enabled and sending events to the server.
|
|
||||||
private let isRunningSubject: CurrentValueSubject<Bool, Never> = .init(false)
|
|
||||||
var isRunningPublisher: CurrentValuePublisher<Bool, Never> {
|
|
||||||
isRunningSubject.asCurrentValuePublisher()
|
|
||||||
}
|
|
||||||
|
|
||||||
init(client: AnalyticsClientProtocol, appSettings: AppSettings) {
|
init(client: AnalyticsClientProtocol, appSettings: AppSettings) {
|
||||||
self.client = client
|
self.client = client
|
||||||
self.appSettings = appSettings
|
self.appSettings = appSettings
|
||||||
|
|
||||||
isRunningSubject.send(client.isRunning)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether to show the user the analytics opt in prompt.
|
/// Whether to show the user the analytics opt in prompt.
|
||||||
@ -76,21 +68,19 @@ class AnalyticsService {
|
|||||||
// The order is important here. PostHog ignores the reset if stopped.
|
// The order is important here. PostHog ignores the reset if stopped.
|
||||||
reset()
|
reset()
|
||||||
client.stop()
|
client.stop()
|
||||||
|
|
||||||
isRunningSubject.send(false)
|
|
||||||
MXLog.info("Stopped.")
|
MXLog.info("Stopped.")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Starts the analytics client if the user has opted in, otherwise does nothing.
|
/// Starts the analytics client if the user has opted in, otherwise does nothing.
|
||||||
func startIfEnabled() {
|
func startIfEnabled() {
|
||||||
guard isEnabled, !isRunningPublisher.value else { return }
|
guard isEnabled, !client.isRunning else { return }
|
||||||
|
|
||||||
client.start(analyticsConfiguration: appSettings.analyticsConfiguration)
|
client.start(analyticsConfiguration: appSettings.analyticsConfiguration)
|
||||||
|
|
||||||
// Sanity check in case something went wrong.
|
// Sanity check in case something went wrong.
|
||||||
guard client.isRunning else { return }
|
guard client.isRunning else { return }
|
||||||
|
|
||||||
isRunningSubject.send(true)
|
|
||||||
MXLog.info("Started.")
|
MXLog.info("Started.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,7 +35,6 @@ class Signposter {
|
|||||||
static let appStarted = "AppStarted"
|
static let appStarted = "AppStarted"
|
||||||
|
|
||||||
static let homeserver = "homeserver"
|
static let homeserver = "homeserver"
|
||||||
static let isDevelopmentBuild = "isDevelopmentBuild"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static let subsystem = "ElementX"
|
static let subsystem = "ElementX"
|
||||||
@ -43,9 +42,8 @@ class Signposter {
|
|||||||
|
|
||||||
private var appStartupSpan: Span
|
private var appStartupSpan: Span
|
||||||
|
|
||||||
init(isDevelopmentBuild: Bool) {
|
init() {
|
||||||
appStartupSpan = SentrySDK.startTransaction(name: Name.appStartup, operation: Name.appStarted)
|
appStartupSpan = SentrySDK.startTransaction(name: Name.appStartup, operation: Name.appStarted)
|
||||||
appStartupSpan.setData(value: isDevelopmentBuild, key: Name.isDevelopmentBuild)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Login
|
// MARK: - Login
|
||||||
@ -55,7 +53,7 @@ class Signposter {
|
|||||||
|
|
||||||
func beginLogin() {
|
func beginLogin() {
|
||||||
loginState = signposter.beginInterval(Name.login)
|
loginState = signposter.beginInterval(Name.login)
|
||||||
loginSpan = appStartupSpan.startChild(operation: "\(Name.login)")
|
loginSpan = appStartupSpan.startChild(operation: "\(Name.login)", description: "\(Name.login)")
|
||||||
}
|
}
|
||||||
|
|
||||||
func endLogin() {
|
func endLogin() {
|
||||||
@ -80,7 +78,7 @@ class Signposter {
|
|||||||
appStartupSpan.setTag(value: serverName, key: Name.homeserver)
|
appStartupSpan.setTag(value: serverName, key: Name.homeserver)
|
||||||
|
|
||||||
firstSyncState = signposter.beginInterval(Name.firstSync)
|
firstSyncState = signposter.beginInterval(Name.firstSync)
|
||||||
firstSyncSpan = appStartupSpan.startChild(operation: "\(Name.firstSync)")
|
firstSyncSpan = appStartupSpan.startChild(operation: "\(Name.firstSync)", description: "\(Name.firstSync)")
|
||||||
}
|
}
|
||||||
|
|
||||||
func endFirstSync() {
|
func endFirstSync() {
|
||||||
@ -100,7 +98,7 @@ class Signposter {
|
|||||||
|
|
||||||
func beginFirstRooms() {
|
func beginFirstRooms() {
|
||||||
firstRoomsState = signposter.beginInterval(Name.firstRooms)
|
firstRoomsState = signposter.beginInterval(Name.firstRooms)
|
||||||
firstRoomsSpan = appStartupSpan.startChild(operation: "\(Name.firstRooms)")
|
firstRoomsSpan = appStartupSpan.startChild(operation: "\(Name.firstRooms)", description: "\(Name.firstRooms)")
|
||||||
}
|
}
|
||||||
|
|
||||||
func endFirstRooms() {
|
func endFirstRooms() {
|
||||||
|
@ -76,7 +76,6 @@ class AnalyticsTests: XCTestCase {
|
|||||||
// Given a fresh install of the app Analytics should be disabled
|
// Given a fresh install of the app Analytics should be disabled
|
||||||
XCTAssertEqual(appSettings.analyticsConsentState, .unknown)
|
XCTAssertEqual(appSettings.analyticsConsentState, .unknown)
|
||||||
XCTAssertFalse(ServiceLocator.shared.analytics.isEnabled)
|
XCTAssertFalse(ServiceLocator.shared.analytics.isEnabled)
|
||||||
XCTAssertFalse(ServiceLocator.shared.analytics.isRunningPublisher.value)
|
|
||||||
XCTAssertFalse(analyticsClient.startAnalyticsConfigurationCalled)
|
XCTAssertFalse(analyticsClient.startAnalyticsConfigurationCalled)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,7 +86,6 @@ class AnalyticsTests: XCTestCase {
|
|||||||
// Then analytics should be disabled
|
// Then analytics should be disabled
|
||||||
XCTAssertEqual(appSettings.analyticsConsentState, .optedOut)
|
XCTAssertEqual(appSettings.analyticsConsentState, .optedOut)
|
||||||
XCTAssertFalse(ServiceLocator.shared.analytics.isEnabled)
|
XCTAssertFalse(ServiceLocator.shared.analytics.isEnabled)
|
||||||
XCTAssertFalse(ServiceLocator.shared.analytics.isRunningPublisher.value)
|
|
||||||
XCTAssertFalse(analyticsClient.isRunning)
|
XCTAssertFalse(analyticsClient.isRunning)
|
||||||
// Analytics client should have been stopped
|
// Analytics client should have been stopped
|
||||||
XCTAssertTrue(analyticsClient.stopCalled)
|
XCTAssertTrue(analyticsClient.stopCalled)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user