Skip to content

Commit

Permalink
Merge pull request #157 from getditto/we/updateLogExporter
Browse files Browse the repository at this point in the history
Update LogExporter to use new DittoLogger.export() api
  • Loading branch information
texasRanger09 authored Nov 8, 2024
2 parents db4eb63 + 49e0736 commit 4f63a6b
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 117 deletions.
1 change: 0 additions & 1 deletion DittoToolsApp/DittoToolsApp/Views/MenuListItem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ struct MenuListItem_Previews: PreviewProvider {
}
Section("Exports") {
MenuListItem(title: "Export Logs", systemImage: "square.and.arrow.up", color: .green)
MenuListItem(title: "Export Logs", systemImage: "square.and.arrow.up", color: .green)
}
}
.listStyle(GroupedListStyle())
Expand Down
Binary file modified Img/diskUsage.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
48 changes: 0 additions & 48 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -261,54 +261,6 @@ Allows you to export a file of the logs from your applcation as a zip file.
First, make sure the "DittoExportLogs" is added to your Target. Then, use `import DittoExportLogs`
to import the Export Logs.

**Important**

Before calling `ditto.startSync()` we need to set the `DittoLogger.setLogFileURL(<logFileURL>)`. This registers a file path where logs will be written to, whenever Ditto wants to issue a log (on top of emitting the log to the console). Use the `LogFileConfig` struct:

```
struct LogFileConfig {
static let logsDirectoryName = "debug-logs"
static let logFileName = "logs.txt"
static let zippedLogFileName = "logs.zip"
static var logsDirectory: URL! = {
let directory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!
return directory.appendingPathComponent(logsDirectoryName, isDirectory: true)
}()
static var logFileURL: URL! = {
return Self.logsDirectory.appendingPathComponent(logFileName)
}()
static var zippedLogsURL: URL! = {
let directory = FileManager.default.temporaryDirectory
return directory.appendingPathComponent(zippedLogFileName)
}()
public static func createLogFileURL() -> URL? {
do {
try FileManager().createDirectory(at: self.logsDirectory,
withIntermediateDirectories: true)
} catch let error {
assertionFailure("Failed to create logs directory: \(error)")
return nil
}
return self.logFileURL
}
}
```

and then before calling `ditto.startSync()` set the log file url with:

```
if let logFileURL = LogFileConfig.createLogFileURL() {
DittoLogger.setLogFileURL(logFileURL)
}
```

Now we can call `ExportLogs()`.

**SwiftUI**

Use `ExportLogs()` to export the logs. It is recommended to call `ExportLogs` from within a [sheet](https://developer.apple.com/documentation/swiftui/view/sheet(ispresented:ondismiss:content:)).
Expand Down
55 changes: 28 additions & 27 deletions Sources/DittoExportLogs/DittoLogManager.swift
Original file line number Diff line number Diff line change
@@ -1,55 +1,56 @@
//
// DittoLogManager.swift
//
//
// Created by Walker Erekson on 1/13/23.
// Copyright © 2021 DittoLive Incorporated. All rights reserved.
//

import Foundation
#if canImport(MessageUI)
import MessageUI
#endif
import UIKit
import DittoSwift

private struct Config {
static let logsDirectoryName = "debug-logs"
static let zippedLogFileName = "DittoLogs.zip"
static let logFileName = "logs.txt"
static let zippedLogFileName = "ditto.jsonl.gz"

/// Directory into which debug logs are to be stored. We use a dedicated
/// directory to keep logs grouped (in the event that we begin generating
/// more than one log - either from multiple sub-systems or due to log
/// rotation).
static var logsDirectory: URL! = {
let directory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!
return directory.appendingPathComponent(logsDirectoryName, isDirectory: true)
}()

/// A temporary location into which we can store zipped logs before sharing
/// them via a share sheet.
static var zippedLogsURL: URL! = {
let directory = FileManager.default.temporaryDirectory
return directory.appendingPathComponent(Config.zippedLogFileName)
}()

}

public struct DittoLogManager {
public static let shared = DittoLogManager()
/// LogManager acts as a thin interface over our stored log files and
/// offers functionality to share zipped logs with an iOS share sheet.
struct LogManager {

private init() {}
// MARK: - Singleton

public func createLogsZip() -> URL? {
try? FileManager().removeItem(at: Config.zippedLogsURL)
public static let shared = LogManager()

let coordinator = NSFileCoordinator()
var nsError: NSError?
// MARK: - Initialization

// Runs synchronously, so no need to co-ordinate multiple callers
coordinator.coordinate(readingItemAt: Config.logsDirectory,
options: [.forUploading], error: &nsError) { tempURL in
do {
try FileManager().moveItem(at: tempURL, to: Config.zippedLogsURL)
} catch let error {
assertionFailure("Failed to move zipped logs into location: \(error)")
}
}
private init() {
// Private singleton constructor
}

if let error = nsError {
assertionFailure("Failed to zip logs: \(error)")
return nil
}
// MARK: - Functions

/// Zips all contents in our log directory, placing an updated zip file at URL returned.
public func exportLogs() async throws -> URL {
try? FileManager().removeItem(at: Config.zippedLogsURL)
try await DittoLogger.export(to: Config.zippedLogsURL)
return Config.zippedLogsURL
}

}
48 changes: 31 additions & 17 deletions Sources/DittoExportLogs/ExportLogs.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,31 +11,45 @@ import UIKit
@available(tvOS, unavailable)
public struct ExportLogs: UIViewControllerRepresentable {

public init() {}
@Binding var activityViewController: UIActivityViewController?

public init(activityViewController: Binding<UIActivityViewController?>) {
self._activityViewController = activityViewController
}

public func makeUIViewController(context: Context) -> UIActivityViewController {

let zippedLogs = getZippedLogs()
public func makeUIViewController(context: Context) -> UIViewController {
// Create a dummy UIViewController to host the UIActivityViewController later
let viewController = UIViewController()

let avc = UIActivityViewController(activityItems: [zippedLogs as Any], applicationActivities: nil)
avc.excludedActivityTypes = [.postToVimeo, .postToWeibo, .postToFlickr, .postToTwitter, .postToFacebook, .postToTencentWeibo, .addToReadingList, .assignToContact, .openInIBooks]
Task {
if let zippedLogs = await getZippedLogs() {
let avc = UIActivityViewController(activityItems: [zippedLogs], applicationActivities: nil)
avc.excludedActivityTypes = [.postToVimeo, .postToWeibo, .postToFlickr, .postToTwitter, .postToFacebook, .postToTencentWeibo, .addToReadingList, .assignToContact, .openInIBooks]

DispatchQueue.main.async {
self.activityViewController = avc
}
}
}

return avc
return viewController
}


public func updateUIViewController(_ uiViewController: UIActivityViewController, context: Context) {
public func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
// Present the activity view controller if it’s available
if let avc = activityViewController, uiViewController.presentedViewController == nil {
uiViewController.present(avc, animated: true)
}
}


func getZippedLogs() -> URL? {

guard let zippedLogs = DittoLogManager.shared.createLogsZip() else {
assertionFailure(); return nil
func getZippedLogs() async -> URL? {
do {
return try await LogManager.shared.exportLogs()
} catch {
print("Error exporting logs")
return nil
}

return zippedLogs
}

}


69 changes: 45 additions & 24 deletions Sources/DittoExportLogs/LoggingDetailsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,17 @@
import Combine
import DittoSwift
import SwiftUI
import UIKit


public struct LoggingDetailsView: View {
@Environment(\.colorScheme) private var colorScheme
@State private var presentExportLogsShare: Bool = false
@State private var presentExportLogsAlert: Bool = false
@Binding var selectedLoggingOption: DittoLogger.LoggingOptions

@State private var activityViewController: UIActivityViewController?

public init(loggingOption: Binding<DittoLogger.LoggingOptions>) {
self._selectedLoggingOption = loggingOption
}
Expand All @@ -42,6 +46,7 @@ public struct LoggingDetailsView: View {
// Export Logs
Button(action: {
self.presentExportLogsAlert.toggle()
print(self.presentExportLogsAlert)
}) {
HStack {
Text("Export Logs")
Expand All @@ -52,40 +57,56 @@ public struct LoggingDetailsView: View {
.foregroundColor(textColor)
.frame(maxWidth: .infinity, maxHeight: .infinity)
#if !os(tvOS)
.sheet(isPresented: $presentExportLogsShare) {
ExportLogs()
}
.sheet(isPresented: $presentExportLogsShare) {
if let activityVC = activityViewController {
// Use a wrapper UIViewController to present the activity controller
ActivityViewControllerWrapper(activityViewController: activityVC)
} else {
// Pass the binding for the `UIActivityViewController?`
ExportLogs(activityViewController: $activityViewController)
}
}
#endif
}
.alert(isPresented: $presentExportLogsAlert) {
#if os(tvOS)
Alert(title: Text("Export Logs"),
message: Text("Exporting logs is not supported on tvOS at this time."),
dismissButton: .cancel()
)
#else
Alert(title: Text("Export Logs"),
message: Text("Compressing the logs may take a few seconds."),
primaryButton: .default(
Text("Export"),
action: {
presentExportLogsShare = true
}),
secondaryButton: .cancel()
)
#endif
}
}
#if os(tvOS)
.listStyle(GroupedListStyle())
#else
.listStyle(InsetGroupedListStyle())
#endif
.alert(isPresented: $presentExportLogsAlert) {
#if os(tvOS)
Alert(title: Text("Export Logs"),
message: Text("Exporting logs is not supported on tvOS at this time."),
dismissButton: .cancel()
)
#else
Alert(title: Text("Export Logs"),
message: Text("Compressing the logs may take a few seconds."),
primaryButton: .default(
Text("Export"),
action: {
presentExportLogsShare = true
}),
secondaryButton: .cancel()
)
#endif
}
}
}

struct LoggingDetailsView_Previews: PreviewProvider {
static var previews: some View {
LoggingDetailsView(loggingOption: .constant(.debug))
struct ActivityViewControllerWrapper: UIViewControllerRepresentable {
let activityViewController: UIActivityViewController

func makeUIViewController(context: Context) -> UIViewController {
let viewController = UIViewController()
DispatchQueue.main.async {
viewController.present(activityViewController, animated: true)
}
return viewController
}

func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
// No need to update the view controller here
}
}

0 comments on commit 4f63a6b

Please sign in to comment.