mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-05 11:58:32 +00:00
Setup Sentry instrumentation on top of the existing Signposter (#2985)
* 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
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
//
|
||||
|
||||
import OSLog
|
||||
import Sentry
|
||||
|
||||
/// A simple wrapper around OSSignposter for easy performance testing.
|
||||
class Signposter {
|
||||
@@ -24,22 +25,37 @@ class Signposter {
|
||||
private let logger = Logger(subsystem: subsystem, category: category)
|
||||
|
||||
/// Signpost name constants.
|
||||
enum Name {
|
||||
private enum Name {
|
||||
static let login: StaticString = "Login"
|
||||
static let firstSync: StaticString = "FirstSync"
|
||||
static let firstRooms: StaticString = "FirstRooms"
|
||||
static let roomFlow: StaticString = "RoomFlow"
|
||||
|
||||
static let appStartup = "AppStartup"
|
||||
static let appStarted = "AppStarted"
|
||||
|
||||
static let homeserver = "homeserver"
|
||||
static let isDevelopmentBuild = "isDevelopmentBuild"
|
||||
}
|
||||
|
||||
static let subsystem = "ElementX"
|
||||
static let category = "PerformanceTests"
|
||||
|
||||
private var appStartupSpan: Span
|
||||
|
||||
init(isDevelopmentBuild: Bool) {
|
||||
appStartupSpan = SentrySDK.startTransaction(name: Name.appStartup, operation: Name.appStarted)
|
||||
appStartupSpan.setData(value: isDevelopmentBuild, key: Name.isDevelopmentBuild)
|
||||
}
|
||||
|
||||
// MARK: - Login
|
||||
|
||||
private var loginState: OSSignpostIntervalState?
|
||||
private var loginSpan: Span?
|
||||
|
||||
func beginLogin() {
|
||||
loginState = signposter.beginInterval(Name.login)
|
||||
loginSpan = appStartupSpan.startChild(operation: "\(Name.login)")
|
||||
}
|
||||
|
||||
func endLogin() {
|
||||
@@ -49,37 +65,56 @@ class Signposter {
|
||||
}
|
||||
|
||||
signposter.endInterval(Name.login, loginState)
|
||||
loginSpan?.finish()
|
||||
|
||||
self.loginState = nil
|
||||
loginSpan = nil
|
||||
}
|
||||
|
||||
// MARK: - FirstSync
|
||||
|
||||
private var firstSyncState: OSSignpostIntervalState?
|
||||
private var firstSyncSpan: Span?
|
||||
|
||||
func beginFirstSync() {
|
||||
func beginFirstSync(serverName: String) {
|
||||
appStartupSpan.setTag(value: serverName, key: Name.homeserver)
|
||||
|
||||
firstSyncState = signposter.beginInterval(Name.firstSync)
|
||||
firstSyncSpan = appStartupSpan.startChild(operation: "\(Name.firstSync)")
|
||||
}
|
||||
|
||||
func endFirstSync() {
|
||||
guard let firstSyncState else { return }
|
||||
|
||||
signposter.endInterval(Name.firstSync, firstSyncState)
|
||||
firstSyncSpan?.finish()
|
||||
|
||||
self.firstSyncState = nil
|
||||
firstSyncSpan = nil
|
||||
}
|
||||
|
||||
// MARK: - FirstRooms
|
||||
|
||||
private var firstRoomsState: OSSignpostIntervalState?
|
||||
private var firstRoomsSpan: Span?
|
||||
|
||||
func beginFirstRooms() {
|
||||
firstRoomsState = signposter.beginInterval(Name.firstRooms)
|
||||
firstRoomsSpan = appStartupSpan.startChild(operation: "\(Name.firstRooms)")
|
||||
}
|
||||
|
||||
func endFirstRooms() {
|
||||
defer {
|
||||
appStartupSpan.finish()
|
||||
}
|
||||
|
||||
guard let firstRoomsState else { return }
|
||||
|
||||
signposter.endInterval(Name.firstRooms, firstRoomsState)
|
||||
firstRoomsSpan?.finish()
|
||||
|
||||
self.firstRoomsState = nil
|
||||
firstRoomsSpan = nil
|
||||
}
|
||||
|
||||
// MARK: - Room Flow
|
||||
|
||||
@@ -22,23 +22,21 @@ import UIKit
|
||||
|
||||
class BugReportService: NSObject, BugReportServiceProtocol {
|
||||
private let baseURL: URL
|
||||
private let sentryURL: URL
|
||||
private let applicationId: String
|
||||
private let sdkGitSHA: String
|
||||
private let maxUploadSize: Int
|
||||
private let session: URLSession
|
||||
private var lastCrashEventId: String?
|
||||
private let progressSubject = PassthroughSubject<Double, Never>()
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
var lastCrashEventID: String?
|
||||
|
||||
init(withBaseURL baseURL: URL,
|
||||
sentryURL: URL,
|
||||
applicationId: String,
|
||||
sdkGitSHA: String,
|
||||
maxUploadSize: Int,
|
||||
session: URLSession = .shared) {
|
||||
self.baseURL = baseURL
|
||||
self.sentryURL = sentryURL
|
||||
self.applicationId = applicationId
|
||||
self.sdkGitSHA = sdkGitSHA
|
||||
self.maxUploadSize = maxUploadSize
|
||||
@@ -47,64 +45,10 @@ class BugReportService: NSObject, BugReportServiceProtocol {
|
||||
}
|
||||
|
||||
// MARK: - BugReportServiceProtocol
|
||||
|
||||
var isRunning: Bool {
|
||||
SentrySDK.isEnabled
|
||||
}
|
||||
|
||||
var crashedLastRun: Bool {
|
||||
SentrySDK.crashedLastRun
|
||||
}
|
||||
|
||||
func start() {
|
||||
guard !isRunning else { return }
|
||||
|
||||
SentrySDK.start { options in
|
||||
#if DEBUG
|
||||
options.enabled = false
|
||||
#endif
|
||||
|
||||
options.dsn = self.sentryURL.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
|
||||
|
||||
// Set tracesSampleRate to 1.0 to capture 100% of transactions for performance monitoring.
|
||||
// We recommend adjusting this value in production.
|
||||
options.tracesSampleRate = 1.0
|
||||
|
||||
// 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 = { [weak self] event in
|
||||
MXLog.error("Sentry detected a crash in the previous run: \(event.eventId.sentryIdString)")
|
||||
self?.lastCrashEventId = event.eventId.sentryIdString
|
||||
}
|
||||
}
|
||||
MXLog.info("Started.")
|
||||
}
|
||||
|
||||
func stop() {
|
||||
guard isRunning else { return }
|
||||
SentrySDK.close()
|
||||
MXLog.info("Stopped.")
|
||||
}
|
||||
|
||||
// swiftlint:disable:next cyclomatic_complexity
|
||||
func submitBugReport(_ bugReport: BugReport,
|
||||
@@ -140,8 +84,8 @@ class BugReportService: NSObject, BugReportServiceProtocol {
|
||||
}
|
||||
}
|
||||
|
||||
if let crashEventId = lastCrashEventId {
|
||||
params.append(MultipartFormData(key: "crash_report", type: .text(value: "<https://sentry.tools.element.io/organizations/element/issues/?project=44&query=\(crashEventId)>")))
|
||||
if let crashEventID = lastCrashEventID {
|
||||
params.append(MultipartFormData(key: "crash_report", type: .text(value: "<https://sentry.tools.element.io/organizations/element/issues/?project=44&query=\(crashEventID)>")))
|
||||
}
|
||||
|
||||
for url in bugReport.files {
|
||||
@@ -194,7 +138,7 @@ class BugReportService: NSObject, BugReportServiceProtocol {
|
||||
let uploadResponse = try decoder.decode(SubmitBugReportResponse.self, from: data)
|
||||
|
||||
if !uploadResponse.reportUrl.isEmpty {
|
||||
lastCrashEventId = nil
|
||||
lastCrashEventID = nil
|
||||
}
|
||||
|
||||
MXLog.info("Feedback submitted.")
|
||||
|
||||
@@ -52,15 +52,10 @@ enum BugReportServiceError: LocalizedError {
|
||||
}
|
||||
|
||||
// sourcery: AutoMockable
|
||||
protocol BugReportServiceProtocol {
|
||||
// periphery: ignore
|
||||
var isRunning: Bool { get }
|
||||
|
||||
protocol BugReportServiceProtocol: AnyObject {
|
||||
var crashedLastRun: Bool { get }
|
||||
|
||||
func start()
|
||||
|
||||
func stop()
|
||||
var lastCrashEventID: String? { get set }
|
||||
|
||||
func submitBugReport(_ bugReport: BugReport,
|
||||
progressListener: CurrentValueSubject<Double, Never>) async -> Result<SubmitBugReportResponse, BugReportServiceError>
|
||||
|
||||
Reference in New Issue
Block a user