From d661870401bad6ca91cfb8a34ff82f0de79e1f44 Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Sun, 9 Oct 2022 14:26:44 -0400 Subject: [PATCH] Include log data in issue/crash reports --- Tusker/Info.plist | 31 ++++----- .../IssueReporterViewController.swift | 63 ++++++++++++++++--- 2 files changed, 71 insertions(+), 23 deletions(-) diff --git a/Tusker/Info.plist b/Tusker/Info.plist index f778036920..494e77db1a 100644 --- a/Tusker/Info.plist +++ b/Tusker/Info.plist @@ -2,6 +2,22 @@ + OSLogPreferences + + $(PRODUCT_BUNDLE_IDENTIFIER) + + DEFAULT-OPTIONS + + Level + + Persist + Debug + Enable + Debug + + + + CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleExecutable @@ -16,19 +32,6 @@ APPL CFBundleShortVersionString $(MARKETING_VERSION) - CFBundleURLTypes - - - CFBundleTypeRole - Editor - CFBundleURLName - net.shadowfacts.Tusker - CFBundleURLSchemes - - tusker - - - CFBundleVersion $(CURRENT_PROJECT_VERSION) LSApplicationCategoryType @@ -56,7 +59,7 @@ NSMicrophoneUsageDescription Post videos from the camera. NSPhotoLibraryAddUsageDescription - Save photos directly from other people's posts. + Save photos directly from other people's posts. NSPhotoLibraryUsageDescription Post photos from the photo library. NSUserActivityTypes diff --git a/Tusker/Screens/Crash Reporter/IssueReporterViewController.swift b/Tusker/Screens/Crash Reporter/IssueReporterViewController.swift index 95a5470b95..9b7e60ebf7 100644 --- a/Tusker/Screens/Crash Reporter/IssueReporterViewController.swift +++ b/Tusker/Screens/Crash Reporter/IssueReporterViewController.swift @@ -9,6 +9,7 @@ import UIKit import CrashReporter import MessageUI +import OSLog class IssueReporterViewController: UIViewController { @@ -34,6 +35,8 @@ class IssueReporterViewController: UIViewController { "Tusker Error Report" } + private let logDataTask: Task + @IBOutlet weak var crashReportTextView: UITextView! @IBOutlet weak var sendReportButton: UIButton! @@ -41,6 +44,15 @@ class IssueReporterViewController: UIViewController { self.reportText = reportText self.reportFilename = reportFilename self.dismiss = dismiss + + self.logDataTask = Task(priority: .userInitiated) { + return await withCheckedContinuation({ continuation in + DispatchQueue.global().async { + continuation.resume(returning: getLogData()) + } + }) + } + super.init(nibName: "IssueReporterViewController", bundle: .main) } @@ -107,15 +119,22 @@ class IssueReporterViewController: UIViewController { @IBAction func sendReportTouchUpInside(_ sender: Any) { updateSendReportButtonColor(lightened: false, animate: true) - let composeVC = MFMailComposeViewController() - composeVC.mailComposeDelegate = self - composeVC.setToRecipients(["me@shadowfacts.net"]) - composeVC.setSubject(subject) - - let data = reportText.data(using: .utf8)! - composeVC.addAttachmentData(data, mimeType: "text/plain", fileName: reportFilename) - - self.present(composeVC, animated: true) + Task { + let composeVC = MFMailComposeViewController() + composeVC.mailComposeDelegate = self + composeVC.setToRecipients(["me@shadowfacts.net"]) + composeVC.setSubject(subject) + + let data = reportText.data(using: .utf8)! + composeVC.addAttachmentData(data, mimeType: "text/plain", fileName: reportFilename) + + if let logData = await logDataTask.value { + let timestamp = ISO8601DateFormatter().string(from: Date()) + composeVC.addAttachmentData(logData, mimeType: "text/plain", fileName: "Tusker-\(timestamp).log") + } + + self.present(composeVC, animated: true) + } } @objc func sendReportButtonLongPressed() { @@ -139,3 +158,29 @@ extension IssueReporterViewController: MFMailComposeViewControllerDelegate { } } } + +fileprivate func getLogData() -> Data? { + do { + let store = try OSLogStore(scope: .currentProcessIdentifier) + // past hour + let position = store.position(date: Date().addingTimeInterval(-60 * 60)) + let entries = try store.getEntries(at: position, matching: NSPredicate(format: "subsystem = %@", Bundle.main.bundleIdentifier!)) + var data = Data() + for entry in entries { + guard let entry = entry as? OSLogEntryLog else { + continue + } + data.append(contentsOf: entry.date.formatted(.iso8601).utf8) + data.append(32) // ' ' + data.append(91) // '[' + data.append(contentsOf: entry.category.utf8) + data.append(93) // ']' + data.append(32) // ' ' + data.append(contentsOf: entry.composedMessage.utf8) + data.append(10) // '\n' + } + return data + } catch { + return nil + } +}