Supports exporting OSLogs from ErrorLogViewController

This commit is contained in:
Riley Testut
2023-10-18 14:26:37 -05:00
committed by Magesh K
parent 2ccf01cf9c
commit 1ece687e37
2 changed files with 89 additions and 0 deletions

View File

@@ -34,6 +34,9 @@ final class ErrorLogViewController: UITableViewController
return dateFormatter
}()
@IBOutlet private var exportLogButton: UIBarButtonItem!
@IBOutlet private var clearLogButton: UIBarButtonItem!
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
@@ -44,6 +47,14 @@ final class ErrorLogViewController: UITableViewController
self.tableView.dataSource = self.dataSource
self.tableView.prefetchDataSource = self.dataSource
self.exportLogButton.activityIndicatorView.color = .white
if #unavailable(iOS 15)
{
// Assign just clearLogButton to hide export button.
self.navigationItem.rightBarButtonItems = [self.clearLogButton]
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
@@ -262,6 +273,77 @@ private extension ErrorLogViewController
func viewMoreDetails(for loggedError: LoggedError) {
self.performSegue(withIdentifier: "showErrorDetails", sender: loggedError)
}
@available(iOS 15, *)
@IBAction func exportDetailedLog(_ sender: UIBarButtonItem)
{
self.exportLogButton.isIndicatingActivity = true
Task<Void, Never>.detached(priority: .userInitiated) {
do
{
let store = try OSLogStore(scope: .currentProcessIdentifier)
// All logs since the app launched.
let position = store.position(timeIntervalSinceLatestBoot: 0)
let entries = try store.getEntries(at: position)
.compactMap { $0 as? OSLogEntryLog }
.filter { $0.subsystem.contains(Logger.altstoreSubsystem) }
.map { "[\($0.date.formatted())] [\($0.category)] [\($0.level.localizedName)] \($0.composedMessage)" }
let outputText = entries.joined(separator: "\n")
let outputDirectory = FileManager.default.uniqueTemporaryURL()
try FileManager.default.createDirectory(at: outputDirectory, withIntermediateDirectories: true)
defer {
do
{
try FileManager.default.removeItem(at: outputDirectory)
}
catch
{
Logger.main.error("Failed to remove temporary log directory \(outputDirectory.lastPathComponent, privacy: .public). \(error.localizedDescription, privacy: .public)")
}
}
let outputURL = outputDirectory.appendingPathComponent("altlog.txt")
try outputText.write(to: outputURL, atomically: true, encoding: .utf8)
try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<Void, Error>) in
Task<Void, Never> { @MainActor in
let activityViewController = UIActivityViewController(activityItems: [outputURL], applicationActivities: nil)
activityViewController.completionWithItemsHandler = { (activityType, completed, _, error) in
if let error
{
continuation.resume(throwing: error)
}
else
{
continuation.resume()
}
}
self.present(activityViewController, animated: true)
}
}
}
catch
{
Logger.main.error("Failed to export OSLog entries. \(error.localizedDescription, privacy: .public)")
await MainActor.run {
let alertController = UIAlertController(title: NSLocalizedString("Unable to Export Detailed Log", comment: ""), message: error.localizedDescription, preferredStyle: .alert)
alertController.addAction(.ok)
self.present(alertController, animated: true)
}
}
await MainActor.run {
self.exportLogButton.isIndicatingActivity = false
}
}
}
}
extension ErrorLogViewController

View File

@@ -1325,9 +1325,16 @@ Settings by i cons from the Noun Project</string>
<action selector="showMinimuxerLogs:" destination="g8a-Rf-zWa" id="V0f-0y-C6C"/>
</connections>
</barButtonItem>
<barButtonItem systemItem="action" id="BNj-HE-KHr">
<connections>
<action selector="exportDetailedLog:" destination="g8a-Rf-zWa" id="Kbw-Q5-9WO"/>
</connections>
</barButtonItem>
</rightBarButtonItems>
</navigationItem>
<connections>
<outlet property="clearLogButton" destination="BnQ-Eh-1gC" id="MHl-Kh-ick"/>
<outlet property="exportLogButton" destination="BNj-HE-KHr" id="w9g-yA-0Yx"/>
<segue destination="7gm-d1-zWK" kind="presentation" identifier="showErrorDetails" id="9vz-y6-evp"/>
</connections>
</tableViewController>