mirror of
https://github.com/bitwarden/ios.git
synced 2025-12-10 17:46:07 -06:00
[PM-19577] Log ErrorReporter errors to the flight recorder (#1547)
This commit is contained in:
parent
129973cad5
commit
c6993cd212
@ -1,4 +1,5 @@
|
||||
import AuthenticatorShared
|
||||
import BitwardenKit
|
||||
import UIKit
|
||||
|
||||
/// A protocol for an `AppDelegate` that can be used by the `SceneDelegate` to look up the
|
||||
|
||||
@ -6,6 +6,11 @@ import FirebaseCrashlytics
|
||||
/// An `ErrorReporter` that logs non-fatal errors to Crashlytics for investigation.
|
||||
///
|
||||
final class CrashlyticsErrorReporter: ErrorReporter {
|
||||
// MARK: Properties
|
||||
|
||||
/// A list of additional loggers that errors will be logged to.
|
||||
private var additionalLoggers: [any BitwardenLogger] = []
|
||||
|
||||
// MARK: ErrorReporter Properties
|
||||
|
||||
var isEnabled: Bool {
|
||||
@ -25,7 +30,16 @@ final class CrashlyticsErrorReporter: ErrorReporter {
|
||||
|
||||
// MARK: ErrorReporter
|
||||
|
||||
public func add(logger: any BitwardenLogger) {
|
||||
additionalLoggers.append(logger)
|
||||
}
|
||||
|
||||
func log(error: Error) {
|
||||
let callStack = Thread.callStackSymbols.joined(separator: "\n")
|
||||
for logger in additionalLoggers {
|
||||
logger.log("Error: \(error)\n\(callStack)")
|
||||
}
|
||||
|
||||
// Don't log networking related errors to Crashlytics.
|
||||
guard !error.isNetworkingError else { return }
|
||||
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import BitwardenKit
|
||||
import BitwardenShared
|
||||
import UIKit
|
||||
|
||||
|
||||
@ -6,6 +6,11 @@ import FirebaseCrashlytics
|
||||
/// An `ErrorReporter` that logs non-fatal errors to Crashlytics for investigation.
|
||||
///
|
||||
final class CrashlyticsErrorReporter: ErrorReporter {
|
||||
// MARK: Properties
|
||||
|
||||
/// A list of additional loggers that errors will be logged to.
|
||||
private var additionalLoggers: [any BitwardenLogger] = []
|
||||
|
||||
// MARK: ErrorReporter Properties
|
||||
|
||||
var isEnabled: Bool {
|
||||
@ -25,7 +30,16 @@ final class CrashlyticsErrorReporter: ErrorReporter {
|
||||
|
||||
// MARK: ErrorReporter
|
||||
|
||||
public func add(logger: any BitwardenLogger) {
|
||||
additionalLoggers.append(logger)
|
||||
}
|
||||
|
||||
func log(error: Error) {
|
||||
let callStack = Thread.callStackSymbols.joined(separator: "\n")
|
||||
for logger in additionalLoggers {
|
||||
logger.log("Error: \(error)\n\(callStack)")
|
||||
}
|
||||
|
||||
// Don't log networking related errors to Crashlytics.
|
||||
guard !error.isNetworkingError else { return }
|
||||
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import BitwardenKit
|
||||
import BitwardenShared
|
||||
import MobileCoreServices
|
||||
import UIKit
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import AuthenticationServices
|
||||
import BitwardenKit
|
||||
import BitwardenSdk
|
||||
import BitwardenShared
|
||||
import Combine
|
||||
|
||||
@ -8,6 +8,12 @@ public protocol ErrorReporter: AnyObject {
|
||||
|
||||
// MARK: Methods
|
||||
|
||||
/// Add an additional logger that will any errors will be logged to.
|
||||
///
|
||||
/// - Parameter logger: The additional logger that any errors will be logged to.
|
||||
///
|
||||
func add(logger: BitwardenLogger)
|
||||
|
||||
/// Logs an error to be reported.
|
||||
///
|
||||
/// - Parameter error: The error to log.
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
@testable import BitwardenKit
|
||||
|
||||
public class MockErrorReporter: ErrorReporter {
|
||||
public var additionalLoggers = [any BitwardenLogger]()
|
||||
public var currentUserId: String?
|
||||
public var errors = [Error]()
|
||||
public var isEnabled = false
|
||||
@ -8,6 +9,10 @@ public class MockErrorReporter: ErrorReporter {
|
||||
|
||||
public init() {}
|
||||
|
||||
public func add(logger: any BitwardenLogger) {
|
||||
additionalLoggers.append(logger)
|
||||
}
|
||||
|
||||
public func log(error: Error) {
|
||||
errors.append(error)
|
||||
}
|
||||
|
||||
25
BitwardenKit/Core/Platform/Utilities/BitwardenLogger.swift
Normal file
25
BitwardenKit/Core/Platform/Utilities/BitwardenLogger.swift
Normal file
@ -0,0 +1,25 @@
|
||||
/// A protocol for an object that handles logging app messages.
|
||||
///
|
||||
public protocol BitwardenLogger {
|
||||
/// Logs a message.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - message: The message to log.
|
||||
/// - file: The file that called the log method.
|
||||
/// - line: The line number in the file that called the log method.
|
||||
///
|
||||
func log(_ message: String, file: String, line: UInt)
|
||||
}
|
||||
|
||||
public extension BitwardenLogger {
|
||||
/// Logs a message.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - message: The message to log.
|
||||
/// - file: The file that called the log method.
|
||||
/// - line: The line number in the file that called the log method.
|
||||
///
|
||||
func log(_ message: String, file: String = #file, line: UInt = #line) {
|
||||
log(message, file: file, line: line)
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,3 @@
|
||||
import BitwardenKit
|
||||
import OSLog
|
||||
|
||||
/// An `ErrorReporter` that logs non-fatal errors to the console via OSLog.
|
||||
@ -6,6 +5,9 @@ import OSLog
|
||||
public final class OSLogErrorReporter: ErrorReporter {
|
||||
// MARK: Properties
|
||||
|
||||
/// A list of additional loggers that errors will be logged to.
|
||||
private var additionalLoggers: [any BitwardenLogger] = []
|
||||
|
||||
/// The logger instance to log local messages.
|
||||
let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "ErrorReporter")
|
||||
|
||||
@ -21,9 +23,18 @@ public final class OSLogErrorReporter: ErrorReporter {
|
||||
|
||||
// MARK: ErrorReporter
|
||||
|
||||
public func add(logger: any BitwardenLogger) {
|
||||
additionalLoggers.append(logger)
|
||||
}
|
||||
|
||||
public func log(error: Error) {
|
||||
logger.error("Error: \(error)")
|
||||
|
||||
let callStack = Thread.callStackSymbols.joined(separator: "\n")
|
||||
for logger in additionalLoggers {
|
||||
logger.log("Error: \(error as NSError)\n\(callStack)")
|
||||
}
|
||||
|
||||
// Don't crash for networking related errors.
|
||||
guard !error.isNetworkingError else { return }
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import BitwardenKit
|
||||
import BitwardenShared
|
||||
import Social
|
||||
import UIKit
|
||||
|
||||
@ -424,3 +424,13 @@ extension DefaultFlightRecorder: FlightRecorder {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: DefaultFlightRecorder + BitwardenLogger
|
||||
|
||||
extension DefaultFlightRecorder: BitwardenLogger {
|
||||
nonisolated func log(_ message: String, file: String, line: UInt) {
|
||||
Task {
|
||||
await log(message, file: file, line: line)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -598,4 +598,18 @@ class FlightRecorderTests: BitwardenTestCase { // swiftlint:disable:this type_bo
|
||||
await subject.log("Hello world!")
|
||||
XCTAssertNil(stateService.flightRecorderData)
|
||||
}
|
||||
|
||||
// MARK: DefaultFlightRecorder Tests
|
||||
|
||||
/// `DefaultFlightRecorder` implements `BitwardenLogger.log()` which logs to the active log.
|
||||
func test_log_bitwardenLogger() throws {
|
||||
stateService.flightRecorderData = FlightRecorderData(activeLog: activeLog)
|
||||
|
||||
(subject as? DefaultFlightRecorder)?.log("Hello world!")
|
||||
waitFor { self.fileManager.appendDataData != nil }
|
||||
|
||||
let appendedMessage = try String(data: XCTUnwrap(fileManager.appendDataData), encoding: .utf8)
|
||||
XCTAssertEqual(appendedMessage, "2025-01-01T00:00:00Z: Hello world!\n")
|
||||
XCTAssertEqual(stateService.flightRecorderData, FlightRecorderData(activeLog: activeLog))
|
||||
}
|
||||
}
|
||||
|
||||
@ -398,6 +398,7 @@ public class ServiceContainer: Services { // swiftlint:disable:this type_body_le
|
||||
stateService: stateService,
|
||||
timeProvider: timeProvider
|
||||
)
|
||||
errorReporter.add(logger: flightRecorder)
|
||||
|
||||
let rehydrationHelper = DefaultRehydrationHelper(
|
||||
errorReporter: errorReporter,
|
||||
|
||||
@ -1,41 +0,0 @@
|
||||
import BitwardenKit
|
||||
import OSLog
|
||||
|
||||
/// An `ErrorReporter` that logs non-fatal errors to the console via OSLog.
|
||||
///
|
||||
public final class OSLogErrorReporter: ErrorReporter {
|
||||
// MARK: Properties
|
||||
|
||||
/// The logger instance to log local messages.
|
||||
let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "ErrorReporter")
|
||||
|
||||
// MARK: ErrorReporter Properties
|
||||
|
||||
/// This exists here satisfy the `ErrorReporter` protocol, but doesn't do anything since we
|
||||
/// don't report these errors to an external service.
|
||||
public var isEnabled = true
|
||||
|
||||
// MARK: Initialization
|
||||
|
||||
public init() {}
|
||||
|
||||
// MARK: ErrorReporter
|
||||
|
||||
public func log(error: Error) {
|
||||
logger.error("Error: \(error)")
|
||||
|
||||
// Don't crash for networking related errors.
|
||||
guard !error.isNetworkingError else { return }
|
||||
|
||||
// Crash in debug builds to make the error more visible during development.
|
||||
assertionFailure("Unexpected error: \(error)")
|
||||
}
|
||||
|
||||
public func setRegion(_ region: String, isPreAuth: Bool) {
|
||||
// No-op
|
||||
}
|
||||
|
||||
public func setUserId(_ userId: String?) {
|
||||
// No-op
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user