diff --git a/Sources/Features/Attributes/AttributesProvider.swift b/Sources/Features/Attributes/AttributesProvider.swift index 258945dd..acd7ea5e 100644 --- a/Sources/Features/Attributes/AttributesProvider.swift +++ b/Sources/Features/Attributes/AttributesProvider.swift @@ -44,10 +44,6 @@ extension AttributesProvider: SignalContext { self.faultInfo.faultMessage = faultMessage } - func set(errorType: String?) { - self.attributes["error.type"] = errorType - } - var attachmentPaths: [String] { return allAttachments.map(\.path) } @@ -55,13 +51,20 @@ extension AttributesProvider: SignalContext { var allAttachments: Attachments { attachments + attributesSources.map(\.attachments).reduce([], +) } + + var scopedAttributes: Attributes { + return immutable + attributes; + } + var dynamicAttributes: Attributes { + return attributesSources.map(\.mutable).merging() + } var allAttributes: Attributes { return attributes + defaultAttributes } var defaultAttributes: Attributes { - return immutable + attributesSources.map(\.mutable).merging() + return immutable + dynamicAttributes } } diff --git a/Sources/Features/Attributes/DefaultAttributes.swift b/Sources/Features/Attributes/DefaultAttributes.swift index 7b9f76b1..df4640bb 100644 --- a/Sources/Features/Attributes/DefaultAttributes.swift +++ b/Sources/Features/Attributes/DefaultAttributes.swift @@ -11,6 +11,10 @@ import UIKit final class FaultInfo: AttributesSource { var faultMessage: String? + + var immutable: [String : Any?] { + return ["error.type": "Crash"] + } var mutable: [String: Any?] { return ["error.message": faultMessage] } diff --git a/Sources/Features/Client/BacktraceReporter.swift b/Sources/Features/Client/BacktraceReporter.swift index ba4f491c..8c46ed1f 100644 --- a/Sources/Features/Client/BacktraceReporter.swift +++ b/Sources/Features/Client/BacktraceReporter.swift @@ -76,6 +76,12 @@ extension BacktraceReporter: BacktraceClientCustomizing { return attributesProvider.attributes } set { attributesProvider.attributes = newValue + + guard let attributeData = try? JSONSerialization.data(withJSONObject: attributesProvider.scopedAttributes) else { + return + } + self.reporter.setCustomData(data: attributeData) + } } @@ -111,10 +117,11 @@ extension BacktraceReporter { func generate(exception: NSException? = nil, attachmentPaths: [String] = [], faultMessage: String? = nil) throws -> BacktraceReport { attributesProvider.set(faultMessage: faultMessage) - attributesProvider.set(errorType: "Exception") let resource = try reporter.generateLiveReport(exception: exception, attributes: attributesProvider.allAttributes, attachmentPaths: attachmentPaths + attributesProvider.attachmentPaths) + + resource.attributes["error.type"] = "Exception" return resource } } diff --git a/Sources/Features/Extensions/Foundation+Extensions.swift b/Sources/Features/Extensions/Foundation+Extensions.swift index 1bd8fefc..85fa9aef 100644 --- a/Sources/Features/Extensions/Foundation+Extensions.swift +++ b/Sources/Features/Extensions/Foundation+Extensions.swift @@ -5,6 +5,12 @@ extension Dictionary { static func + (lhs: Dictionary, rhs: Dictionary) -> Dictionary { return lhs.merging(rhs, uniquingKeysWith: {_, new in new}) } + + static func += (left: inout Dictionary, right: Dictionary) { + for (key, value) in right { + left[key] = value + } + } } // From: https://stackoverflow.com/a/57886995 diff --git a/Sources/Features/Repository/Model/BacktraceReport.swift b/Sources/Features/Repository/Model/BacktraceReport.swift index 62fcc9bd..aa04a1f1 100644 --- a/Sources/Features/Repository/Model/BacktraceReport.swift +++ b/Sources/Features/Repository/Model/BacktraceReport.swift @@ -23,8 +23,10 @@ import CrashReporter self.attachmentPaths = attachmentPaths self.attributes = attributes super.init() + + self.extendCrashAttributes() } - + init(managedObject: Crash) throws { guard let reportData = managedObject.reportData, let identifierString = managedObject.hashProperty, @@ -37,7 +39,10 @@ import CrashReporter self.identifier = identifier self.attachmentPaths = attachmentPaths self.attributes = (try? AttributesStorage.retrieve(fileName: identifier.uuidString)) ?? [:] + super.init() + + self.extendCrashAttributes() } } @@ -46,4 +51,18 @@ extension BacktraceReport: PersistentStorable { typealias ManagedObjectType = Crash static var entityName: String { return "Crash" } + + private func extendCrashAttributes() { + guard let customData = self.plCrashReport.customData else { + return + } + + if let attributes = try? JSONSerialization.jsonObject(with: customData, options: []) as? [String: Any] { + self.attributes += attributes + } else { + return + } + } + + } diff --git a/Sources/Public/BacktraceCrashReporter.swift b/Sources/Public/BacktraceCrashReporter.swift index 7099d648..1e2c1e80 100644 --- a/Sources/Public/BacktraceCrashReporter.swift +++ b/Sources/Public/BacktraceCrashReporter.swift @@ -33,10 +33,10 @@ extension BacktraceCrashReporter: CrashReporting { let signalInfo = signalInfoPointer?.pointee else { return } - attributesProvider.set(errorType: "Crash") + attributesProvider.set(faultMessage: "siginfo_t.si_signo: \(signalInfo.si_signo)") - try? AttributesStorage.store(attributesProvider.allAttributes, fileName: BacktraceCrashReporter.crashName) + try? AttributesStorage.store(attributesProvider.dynamicAttributes, fileName: BacktraceCrashReporter.crashName) try? AttachmentsStorage.store(attributesProvider.allAttachments, fileName: BacktraceCrashReporter.crashName) } @@ -60,10 +60,15 @@ extension BacktraceCrashReporter: CrashReporting { // This function retrieves, constructs, and sends the pending crash report func pendingCrashReport() throws -> BacktraceReport { let reportData = try reporter.loadPendingCrashReportDataAndReturnError() + let attributes = (try? AttributesStorage.retrieve(fileName: BacktraceCrashReporter.crashName)) ?? [:] let attachmentPaths = copiedFileAttachments.map(\.path) return try BacktraceReport(report: reportData, attributes: attributes, attachmentPaths: attachmentPaths) } + + func setCustomData(data: Data) { + self.reporter.customData = data + } // This function is called to copy stored file attachments // from pending crashes so that they are not overwritten by the diff --git a/Sources/Public/Internal/CrashReporting.swift b/Sources/Public/Internal/CrashReporting.swift index 05deeacd..f314b25e 100644 --- a/Sources/Public/Internal/CrashReporting.swift +++ b/Sources/Public/Internal/CrashReporting.swift @@ -8,4 +8,5 @@ protocol CrashReporting { func hasPendingCrashes() -> Bool func enableCrashReporting() throws func signalContext(_ mutableContext: inout SignalContext) + func setCustomData(data: Data) -> Void } diff --git a/Sources/Public/Internal/SignalContext.swift b/Sources/Public/Internal/SignalContext.swift index 6c670018..4613ae57 100644 --- a/Sources/Public/Internal/SignalContext.swift +++ b/Sources/Public/Internal/SignalContext.swift @@ -1,7 +1,9 @@ import Foundation protocol SignalContext: CustomStringConvertible { + var scopedAttributes: Attributes { get } var allAttributes: Attributes { get } + var dynamicAttributes: Attributes { get } var allAttachments: Attachments { get } var attributes: Attributes { get set } // File attachments are stored to disk as URLs @@ -9,5 +11,4 @@ protocol SignalContext: CustomStringConvertible { // File attachments are used in `BacktraceReport` as string paths var attachmentPaths: [String] { get } func set(faultMessage: String?) - func set(errorType: String?) }