Skip to content

Commit c6993cd

Browse files
[PM-19577] Log ErrorReporter errors to the flight recorder (#1547)
1 parent 129973c commit c6993cd

File tree

15 files changed

+106
-42
lines changed

15 files changed

+106
-42
lines changed

Authenticator/Application/AppDelegate.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import AuthenticatorShared
2+
import BitwardenKit
23
import UIKit
34

45
/// A protocol for an `AppDelegate` that can be used by the `SceneDelegate` to look up the

Authenticator/Application/Services/ErrorReporter/CrashlyticsErrorReporter.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ import FirebaseCrashlytics
66
/// An `ErrorReporter` that logs non-fatal errors to Crashlytics for investigation.
77
///
88
final class CrashlyticsErrorReporter: ErrorReporter {
9+
// MARK: Properties
10+
11+
/// A list of additional loggers that errors will be logged to.
12+
private var additionalLoggers: [any BitwardenLogger] = []
13+
914
// MARK: ErrorReporter Properties
1015

1116
var isEnabled: Bool {
@@ -25,7 +30,16 @@ final class CrashlyticsErrorReporter: ErrorReporter {
2530

2631
// MARK: ErrorReporter
2732

33+
public func add(logger: any BitwardenLogger) {
34+
additionalLoggers.append(logger)
35+
}
36+
2837
func log(error: Error) {
38+
let callStack = Thread.callStackSymbols.joined(separator: "\n")
39+
for logger in additionalLoggers {
40+
logger.log("Error: \(error)\n\(callStack)")
41+
}
42+
2943
// Don't log networking related errors to Crashlytics.
3044
guard !error.isNetworkingError else { return }
3145

Bitwarden/Application/AppDelegate.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import BitwardenKit
12
import BitwardenShared
23
import UIKit
34

Bitwarden/Application/Services/ErrorReporter/CrashlyticsErrorReporter.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ import FirebaseCrashlytics
66
/// An `ErrorReporter` that logs non-fatal errors to Crashlytics for investigation.
77
///
88
final class CrashlyticsErrorReporter: ErrorReporter {
9+
// MARK: Properties
10+
11+
/// A list of additional loggers that errors will be logged to.
12+
private var additionalLoggers: [any BitwardenLogger] = []
13+
914
// MARK: ErrorReporter Properties
1015

1116
var isEnabled: Bool {
@@ -25,7 +30,16 @@ final class CrashlyticsErrorReporter: ErrorReporter {
2530

2631
// MARK: ErrorReporter
2732

33+
public func add(logger: any BitwardenLogger) {
34+
additionalLoggers.append(logger)
35+
}
36+
2837
func log(error: Error) {
38+
let callStack = Thread.callStackSymbols.joined(separator: "\n")
39+
for logger in additionalLoggers {
40+
logger.log("Error: \(error)\n\(callStack)")
41+
}
42+
2943
// Don't log networking related errors to Crashlytics.
3044
guard !error.isNetworkingError else { return }
3145

BitwardenActionExtension/ActionViewController.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import BitwardenKit
12
import BitwardenShared
23
import MobileCoreServices
34
import UIKit

BitwardenAutoFillExtension/CredentialProviderViewController.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import AuthenticationServices
2+
import BitwardenKit
23
import BitwardenSdk
34
import BitwardenShared
45
import Combine

BitwardenKit/Core/Platform/Services/ErrorReporter/ErrorReporter.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ public protocol ErrorReporter: AnyObject {
88

99
// MARK: Methods
1010

11+
/// Add an additional logger that will any errors will be logged to.
12+
///
13+
/// - Parameter logger: The additional logger that any errors will be logged to.
14+
///
15+
func add(logger: BitwardenLogger)
16+
1117
/// Logs an error to be reported.
1218
///
1319
/// - Parameter error: The error to log.

BitwardenKit/Core/Platform/Services/ErrorReporter/Mocks/MockErrorReporter.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
@testable import BitwardenKit
22

33
public class MockErrorReporter: ErrorReporter {
4+
public var additionalLoggers = [any BitwardenLogger]()
45
public var currentUserId: String?
56
public var errors = [Error]()
67
public var isEnabled = false
78
public var region: (region: String, isPreAuth: Bool)?
89

910
public init() {}
1011

12+
public func add(logger: any BitwardenLogger) {
13+
additionalLoggers.append(logger)
14+
}
15+
1116
public func log(error: Error) {
1217
errors.append(error)
1318
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/// A protocol for an object that handles logging app messages.
2+
///
3+
public protocol BitwardenLogger {
4+
/// Logs a message.
5+
///
6+
/// - Parameters:
7+
/// - message: The message to log.
8+
/// - file: The file that called the log method.
9+
/// - line: The line number in the file that called the log method.
10+
///
11+
func log(_ message: String, file: String, line: UInt)
12+
}
13+
14+
public extension BitwardenLogger {
15+
/// Logs a message.
16+
///
17+
/// - Parameters:
18+
/// - message: The message to log.
19+
/// - file: The file that called the log method.
20+
/// - line: The line number in the file that called the log method.
21+
///
22+
func log(_ message: String, file: String = #file, line: UInt = #line) {
23+
log(message, file: file, line: line)
24+
}
25+
}

AuthenticatorShared/Core/Platform/Utilities/OSLogErrorReporter.swift renamed to BitwardenKit/Core/Platform/Utilities/OSLogErrorReporter.swift

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
import BitwardenKit
21
import OSLog
32

43
/// An `ErrorReporter` that logs non-fatal errors to the console via OSLog.
54
///
65
public final class OSLogErrorReporter: ErrorReporter {
76
// MARK: Properties
87

8+
/// A list of additional loggers that errors will be logged to.
9+
private var additionalLoggers: [any BitwardenLogger] = []
10+
911
/// The logger instance to log local messages.
1012
let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "ErrorReporter")
1113

@@ -21,9 +23,18 @@ public final class OSLogErrorReporter: ErrorReporter {
2123

2224
// MARK: ErrorReporter
2325

26+
public func add(logger: any BitwardenLogger) {
27+
additionalLoggers.append(logger)
28+
}
29+
2430
public func log(error: Error) {
2531
logger.error("Error: \(error)")
2632

33+
let callStack = Thread.callStackSymbols.joined(separator: "\n")
34+
for logger in additionalLoggers {
35+
logger.log("Error: \(error as NSError)\n\(callStack)")
36+
}
37+
2738
// Don't crash for networking related errors.
2839
guard !error.isNetworkingError else { return }
2940

BitwardenShareExtension/ShareViewController.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import BitwardenKit
12
import BitwardenShared
23
import Social
34
import UIKit

BitwardenShared/Core/Platform/Services/FlightRecorder.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,3 +424,13 @@ extension DefaultFlightRecorder: FlightRecorder {
424424
}
425425
}
426426
}
427+
428+
// MARK: DefaultFlightRecorder + BitwardenLogger
429+
430+
extension DefaultFlightRecorder: BitwardenLogger {
431+
nonisolated func log(_ message: String, file: String, line: UInt) {
432+
Task {
433+
await log(message, file: file, line: line)
434+
}
435+
}
436+
}

BitwardenShared/Core/Platform/Services/FlightRecorderTests.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -598,4 +598,18 @@ class FlightRecorderTests: BitwardenTestCase { // swiftlint:disable:this type_bo
598598
await subject.log("Hello world!")
599599
XCTAssertNil(stateService.flightRecorderData)
600600
}
601+
602+
// MARK: DefaultFlightRecorder Tests
603+
604+
/// `DefaultFlightRecorder` implements `BitwardenLogger.log()` which logs to the active log.
605+
func test_log_bitwardenLogger() throws {
606+
stateService.flightRecorderData = FlightRecorderData(activeLog: activeLog)
607+
608+
(subject as? DefaultFlightRecorder)?.log("Hello world!")
609+
waitFor { self.fileManager.appendDataData != nil }
610+
611+
let appendedMessage = try String(data: XCTUnwrap(fileManager.appendDataData), encoding: .utf8)
612+
XCTAssertEqual(appendedMessage, "2025-01-01T00:00:00Z: Hello world!\n")
613+
XCTAssertEqual(stateService.flightRecorderData, FlightRecorderData(activeLog: activeLog))
614+
}
601615
}

BitwardenShared/Core/Platform/Services/ServiceContainer.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,7 @@ public class ServiceContainer: Services { // swiftlint:disable:this type_body_le
398398
stateService: stateService,
399399
timeProvider: timeProvider
400400
)
401+
errorReporter.add(logger: flightRecorder)
401402

402403
let rehydrationHelper = DefaultRehydrationHelper(
403404
errorReporter: errorReporter,

BitwardenShared/Core/Platform/Utilities/OSLogErrorReporter.swift

Lines changed: 0 additions & 41 deletions
This file was deleted.

0 commit comments

Comments
 (0)