Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/breadcrumbs tvos #95

Draft
wants to merge 8 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions Backtrace.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,7 @@ Pod::Spec.new do |s|
s.ios.source_files = ["Sources/**/*.{swift}", "Backtrace-iOS/**/*.{h*,swift}"]
s.osx.source_files = ["Sources/**/*.{swift}", "Backtrace-macOS/**/*.{h*,swift}"]
s.tvos.source_files = ["Sources/**/*.{swift}", "Backtrace-tvOS/**/*.{h*,swift}"]

s.tvos.exclude_files = ["Sources/Features/Breadcrumb/**/*.{swift}"]


s.ios.public_header_files = ["Backtrace-iOS/**/*.h*"]
s.osx.public_header_files = ["Backtrace-macOS/**/*.h*"]
s.tvos.public_header_files = ["Backtrace-tvOS/**/*.h*"]
Expand Down
478 changes: 248 additions & 230 deletions Backtrace.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions Examples/Example-tvOS/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate {

BacktraceClient.shared = try? BacktraceClient(configuration: backtraceConfiguration)
BacktraceClient.shared?.delegate = self
BacktraceClient.shared?.enableBreadcrumbs()

BacktraceClient.shared?.attributes = ["foo": "bar", "testing": true]

do {
Expand All @@ -38,6 +40,12 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
}
}

let attributes = ["My Attribute":"My Attribute Value"]
_ = BacktraceClient.shared?.addBreadcrumb("My Breadcrumb",
attributes: attributes,
type: .user,
level: .error)

// Enable error free metrics https://docs.saucelabs.com/error-reporting/web-console/overview/#stability-metrics-widgets
BacktraceClient.shared?.metrics.enable(settings: BacktraceMetricsSettings())

Expand Down
2 changes: 1 addition & 1 deletion Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ EXTERNAL SOURCES:
:path: "./Backtrace.podspec"

SPEC CHECKSUMS:
Backtrace: c0124ca7e1a84bc7a3b3407671fb99a90be474e9
Backtrace: 944489289bed0c8f1d05ecac0142424759c67605
Backtrace-PLCrashReporter: 71ddeba11834d2bcc3c19f357aaec7bf87131f89
Cassette: 074c6991391733888990dba728b7ffe00299a0a6
Nimble: 5316ef81a170ce87baf72dd961f22f89a602ff84
Expand Down
57 changes: 48 additions & 9 deletions Sources/Features/Breadcrumb/BacktraceBreadcrumbFileHelper.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Foundation
#if os(iOS) || os(macOS)
import Cassette

#endif
enum BacktraceBreadcrumbFileHelperError: Error {
case invalidFormat
}
Expand All @@ -15,16 +16,20 @@ enum BacktraceBreadcrumbFileHelperError: Error {

private let maximumIndividualBreadcrumbSize: Int
private let maxQueueFileSizeBytes: Int
private let breadcrumbLogURL: URL
#if os(tvOS)
private lazy var breadcrumbs: [String] = {
let breadcrumbs = getSavedBreadcrumbList()
return breadcrumbs
}()
#else
private let queue: CASQueueFile

#endif
/** CASQueueFile is not thread safe, so all interactions with it should be done synchronously through this DispathQueue */
private let dispatchQueue = DispatchQueue(label: "io.backtrace.BacktraceBreadcrumbFileHelper@\(UUID().uuidString)")

public init(_ breadcrumbSettings: BacktraceBreadcrumbSettings) throws {
self.queue = try CASQueueFile.init(path: breadcrumbSettings.getBreadcrumbLogPath().path)

self.maximumIndividualBreadcrumbSize = breadcrumbSettings.maxIndividualBreadcrumbSizeBytes

if breadcrumbSettings.maxQueueFileSizeBytes < BacktraceBreadcrumbFileHelper.minimumQueueFileSizeBytes {
BacktraceLogger.warning("\(breadcrumbSettings.maxQueueFileSizeBytes) is smaller than the minimum of " +
"\(BacktraceBreadcrumbFileHelper.minimumQueueFileSizeBytes)" +
Expand All @@ -33,7 +38,10 @@ enum BacktraceBreadcrumbFileHelperError: Error {
} else {
self.maxQueueFileSizeBytes = breadcrumbSettings.maxQueueFileSizeBytes
}

self.breadcrumbLogURL = try breadcrumbSettings.getBreadcrumbLogPath()
#if os(iOS) || os(macOS)
self.queue = try CASQueueFile.init(path: self.breadcrumbLogURL.path)
#endif
super.init()
}

Expand All @@ -55,12 +63,20 @@ enum BacktraceBreadcrumbFileHelperError: Error {

do {
try dispatchQueue.sync {
#if os(tvOS)
while (queueByteSize() + textBytes.count) > (maxQueueFileSizeBytes - 512),
!breadcrumbs.isEmpty {
breadcrumbs.remove(at: 0)
}
breadcrumbs.append(text)
try breadcrumbsText.write(to: breadcrumbLogURL, atomically: true, encoding: .utf8)
#else
// Keep removing until there's enough space to add the new breadcrumb (leaving 512 bytes room)
while (queueByteSize() + textBytes.count) > (maxQueueFileSizeBytes - 512) {
try queue.pop(1, error: ())
}

try queue.add(textBytes, error: ())
#endif
}
} catch {
BacktraceLogger.warning("\(error.localizedDescription) \nWhen adding breadcrumb to file")
Expand All @@ -73,7 +89,11 @@ enum BacktraceBreadcrumbFileHelperError: Error {
func clear() -> Bool {
do {
try dispatchQueue.sync {
try queue.clearAndReturnError()
#if os(tvOS)
try "".write(to: breadcrumbLogURL, atomically: false, encoding: .utf8)
#else
try queue.clearAndReturnError()
#endif
}
} catch {
BacktraceLogger.warning("\(error.localizedDescription) \nWhen clearing breadcrumb file")
Expand All @@ -92,8 +112,26 @@ extension BacktraceBreadcrumbFileHelper {
}
throw BacktraceBreadcrumbFileHelperError.invalidFormat
}

#if os(tvOS)
var breadcrumbsText: String {
breadcrumbs.joined(separator: "")
}

func getSavedBreadcrumbList() -> [String] {
do {
let fileContent = try String(contentsOf: breadcrumbLogURL, encoding: .utf8).trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
return fileContent.components(separatedBy: "\n").filter({ !$0.isEmpty }).map({ "\n" + $0 + "\n" })
} catch {
print(error.localizedDescription)
return [String]()
}
}
#endif
func queueByteSize() -> Int {
#if os(tvOS)
let textBytes = Data(breadcrumbsText.utf8)
return textBytes.count
#else
// This is the current fileLength of the QueueFile
guard let fileLength = queue.value(forKey: "fileLength") as? Int else {
BacktraceLogger.error("fileLength is not an Int, this is unexpected!")
Expand All @@ -109,5 +147,6 @@ extension BacktraceBreadcrumbFileHelper {
}

return fileLength - remainingBytes
#endif
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,13 @@ protocol BacktraceNotificationObserverDelegate: class {
init(breadcrumbs: BacktraceBreadcrumbs) {
self.breadcrumbs = breadcrumbs
var handlerDelegates: [BacktraceNotificationHandlerDelegate] = [
BacktraceMemoryNotificationObserver(),
BacktraceBatteryNotificationObserver()]
BacktraceMemoryNotificationObserver()]
#if os(iOS)
handlerDelegates.append(BacktraceOrientationNotificationObserver())
handlerDelegates.append(BacktraceAppStateNotificationObserver())
handlerDelegates.append(BacktraceCallNotificationObserver())
#elseif os(OSX)
handlerDelegates.append(BacktraceBatteryNotificationObserver())
#endif
self.handlerDelegates = handlerDelegates
super.init()
Expand Down Expand Up @@ -212,6 +213,7 @@ func powerSourceObserver(context: UnsafeMutableRawPointer?) {
}
#endif

#if os(iOS) || os(OSX)
class BacktraceBatteryNotificationObserver: NSObject, BacktraceNotificationHandlerDelegate {

weak var delegate: BacktraceNotificationObserverDelegate?
Expand Down Expand Up @@ -336,6 +338,7 @@ class BacktraceBatteryNotificationObserver: NSObject, BacktraceNotificationHandl
}
#endif
}
#endif

#if os(iOS)
// MARK: - Application State Observer
Expand Down
4 changes: 0 additions & 4 deletions Sources/Public/BacktraceClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,8 @@ import Foundation
/// Error-free metrics class instance
@objc private let metricsInstance: BacktraceMetrics

#if os(iOS) || os(OSX)
/// Breadcrumbs class instance
@objc private let breadcrumbsInstance: BacktraceBreadcrumbs = BacktraceBreadcrumbs()
#endif

private let reporter: BacktraceReporter
private let dispatcher: Dispatching
Expand Down Expand Up @@ -220,7 +218,6 @@ extension BacktraceClient: BacktraceMetricsProtocol {
}

// MARK: - BacktraceBreadcrumbProtocol
#if os(iOS) || os(OSX)
extension BacktraceClient: BacktraceBreadcrumbProtocol {
@objc public var breadcrumbs: BacktraceBreadcrumbs {
return self.breadcrumbsInstance
Expand Down Expand Up @@ -265,4 +262,3 @@ extension BacktraceClient: BacktraceBreadcrumbProtocol {
return breadcrumbsInstance.clear()
}
}
#endif
7 changes: 0 additions & 7 deletions Sources/Public/BacktraceClientCustomizing.swift
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
import Foundation

/// Type-alias of `BacktraceClient` type. Custom Backtrace client have to implement all of these protocols.
#if os(iOS) || os(OSX)
public typealias BacktraceClientProtocol = BacktraceReporting & BacktraceClientCustomizing &
BacktraceLogging & BacktraceMetricsProtocol & BacktraceBreadcrumbProtocol
#else
public typealias BacktraceClientProtocol = BacktraceReporting & BacktraceClientCustomizing &
BacktraceLogging & BacktraceMetricsProtocol
#endif

/// Type-alias of passing attributes to library.
public typealias Attributes = [String: Any]
Expand Down Expand Up @@ -104,7 +99,6 @@ enum BacktraceUrlParsingError: Error {
case invalidInput(String)
}

#if os(iOS) || os(OSX)
/// Provides Breadcrumb adding functionality to `BacktraceClient`.
@objc public protocol BacktraceBreadcrumbProtocol {
@objc var breadcrumbs: BacktraceBreadcrumbs { get }
Expand Down Expand Up @@ -175,4 +169,3 @@ enum BacktraceUrlParsingError: Error {
///
@objc func clearBreadcrumbs() -> Bool
}
#endif
22 changes: 20 additions & 2 deletions Tests/BacktraceBreadcrumbTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,11 @@ final class BacktraceBreadcrumbTests: QuickSpec {
let breadcrumbText = self.readBreadcrumbText()!

// Not very scientific, but this is apparently when the file wraps
#if canImport(Cassette)
let wrapIndex = 742
#else
let wrapIndex = 733
#endif
for readIndex in 0...wrapIndex {
// should have been rolled away
expect { breadcrumbText }.toNot(contain("\"this is Breadcrumb number \(readIndex)\""))
Expand Down Expand Up @@ -230,12 +234,26 @@ final class BacktraceBreadcrumbTests: QuickSpec {
expect { backtraceObserverMock2.startObservingCalled }.to(beTrue())
}
}
#if os(iOS)

#if os(tvOS)
describe("when tvOS notifications update") {
context("for memory warning notification") {
it("tvOS breadcrumb added") {
backtraceBreadcrumbs.enableBreadcrumbs()
// Simulate memory event:
// https://stackoverflow.com/questions/4717138/ios-development-how-can-i-induce-low-memory-warnings-on-device
// Can't seem to control much of the levels (warning vs fatal, etc), so we just test the warning level
UIControl().sendAction(Selector(("_performMemoryWarning")), to: UIApplication.shared, for: nil)

expect { self.readBreadcrumbText() }.toEventually(contain("Warning level memory pressure event"))
}
}
}
#elseif os(iOS)
describe("when iOS notifications update") {
context("for memory warning notification") {
it("iOS breadcrumb added") {
backtraceBreadcrumbs.enableBreadcrumbs()

// Simulate memory event:
// https://stackoverflow.com/questions/4717138/ios-development-how-can-i-induce-low-memory-warnings-on-device
// Can't seem to control much of the levels (warning vs fatal, etc), so we just test the warning level
Expand Down
2 changes: 0 additions & 2 deletions Tests/Mocks/BacktraceNotificationObserverMock.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import Foundation
import XCTest
@testable import Backtrace

#if os(iOS) || os(OSX)
class BacktraceObserverMock: BacktraceNotificationHandlerDelegate {

var delegate: BacktraceNotificationObserverDelegate?
Expand All @@ -14,4 +13,3 @@ class BacktraceObserverMock: BacktraceNotificationHandlerDelegate {
startObservingCalled = true
}
}
#endif