邮件编辑器示例 报告示例

Diagnostics 是一个用 Swift 编写的库,它可以非常轻松地将诊断报告分享给您的支持团队。

特性

该库允许轻松地将诊断报告作为附件附加到 MFMailComposeViewController

用法

默认报告已经包含大量有价值的信息,可能足以让您开始。

确保尽早设置 DiagnosticsLogger 以捕获所有系统日志,例如在 didLaunchWithOptions

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    do {
        try DiagnosticsLogger.setup()
    } catch {
        print("Failed to setup the Diagnostics Logger")
    }
    return true
}

然后,只需使用以下代码显示 MFMailComposeViewController

import UIKit
import MessageUI
import Diagnostics

class ViewController: UIViewController {

    @IBAction func sendDiagnostics(_ sender: UIButton) {
        /// Create the report.
        let report = DiagnosticsReporter.create()

        guard MFMailComposeViewController.canSendMail() else {
            /// For debugging purposes you can save the report to desktop when testing on the simulator.
            /// This allows you to iterate fast on your report.
            report.saveToDesktop()
            return
        }

        let mail = MFMailComposeViewController()
        mail.mailComposeDelegate = self
        mail.setToRecipients(["support@yourcompany.com"])
        mail.setSubject("Diagnostics Report")
        mail.setMessageBody("An issue in the app is making me crazy, help!", isHTML: false)

        /// Add the Diagnostics Report as an attachment.
        mail.addDiagnosticReport(report)

        present(mail, animated: true)
    }

}

extension ViewController: MFMailComposeViewControllerDelegate {
    func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
        controller.dismiss(animated: true)
    }
}

在 macOS 上,您可以使用 NSSharingService 发送报告

import AppKit
import Diagnostics

func send(report: DiagnosticsReport) {
    let service = NSSharingService(named: NSSharingService.Name.composeEmail)!
    service.recipients = ["support@yourcompany.com"]
    service.subject = "Diagnostics Report"
            
    let url = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("Diagnostics-Report.html")
    
    // remove previous report
    try? FileManager.default.removeItem(at: url)

    do {
        try report.data.write(to: url)
    } catch {
        print("Failed with error: \(error)")
    }

    service.perform(withItems: [url])
}

使用 UserDefaultsReporter

为了使用 UserDefaultsReporter,您需要指定所需的 UserDefaults 实例以及您想要读取的所有键,并在 DiagnosticsReporter.create(filename:using:filters:smartInsightsProvider) 中使用它来创建一个 DiagnosticsReport

let userDefaultsReporter = UserDefaultsReporter(
    userDefaults: UserDefaults(suiteName: "a.userdefaults.instance"),
    keys: ["key_1"]
)

let diagnosticsReport = DiagnosticsReporter.create(using: [userDefaultsReporter])

过滤敏感数据

您的报告可能包含敏感数据。您可以通过创建一个 DiagnosticsReportFilter 来过滤掉这些数据。

示例项目包含一个示例

struct DiagnosticsDictionaryFilter: DiagnosticsReportFilter {

    // This demonstrates how a filter can be used to filter out sensible data.
    static func filter(_ diagnostics: Diagnostics) -> Diagnostics {
        guard let dictionary = diagnostics as? [String: Any] else { return diagnostics }
        return dictionary.filter { keyValue -> Bool in
            if keyValue.key == "App Display Name" {
                // Filter out the key with the value "App Display Name"
                return false
            } else if keyValue.key == "AppleLanguages" {
                // Filter out a user defaults key.
                return false
            }
            return true
        }
    }
}

可以通过将过滤器传递到 create(..) 方法中使用该示例

let report = DiagnosticsReporter.create(using: reporters, filters: [DiagnosticsDictionaryFilter.self])

添加您自己的自定义日志

要使您自己的日志出现在日志诊断中,您需要使用 DiagnosticsLogger

/// Support logging simple `String` messages.
DiagnosticsLogger.log(message: "Application started")

/// Support logging `Error` types.
DiagnosticsLogger.log(error: ExampleError.missingData)

如果可用,错误记录器将使用本地化描述,您可以通过使您的错误符合 LocalizedError 来添加本地化描述。

添加目录树报告

可以为给定的 URL 集合添加目录树报告,从而产生以下输出

└── Documents
    +-- contents
    |   +-- B3F2F9AD-AB8D-4825-8369-181DEAAFF940.png
    |   +-- 5B9C090E-6CE1-4A2F-956B-15897AB4B0A1.png
    |   +-- 739416EF-8FF8-4502-9B36-CEB778385BBF.png
    |   +-- 27A3C96B-1813-4553-A6B7-436E6F3DBB20.png
    |   +-- 8F176CEE-B28F-49EB-8802-CC0438879FBE.png
    |   +-- 340C2371-A81A-4188-8E04-BC19E94F9DAE.png
    |   +-- E63AFEBC-B7E7-46D3-BC92-E34A53C0CE0A.png
    |   +-- 6B363F44-AB69-4A60-957E-710494381739.png
    |   +-- 9D31CA40-D152-45D9-BDCE-9BB09CCB825E.png
    |   +-- 304E2E41-9697-4F9A-9EE0-8D487ED60C45.jpeg
    |   └── 7 more file(s)
    +-- diagnostics_log.txt
    +-- Okapi.sqlite
    +-- Library
    |   +-- Preferences
    |   |   └── group.com.wetransfer.app.plist
    |   └── Caches
    |       └── com.apple.nsurlsessiond
    |           └── Downloads
    |               └── com.wetransfer
    +-- Coyote.sqlite-shm
    +-- Coyote.sqlite
    +-- Coyote.sqlite-wal
    +-- Okapi.sqlite-shm
    +-- Okapi.sqlite-wal
    └── 1 more file(s)

您可以通过添加 DirectoryTreesReporter 来做到这一点

var reporters = DiagnosticsReporter.DefaultReporter.allReporters
let documentsURL = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
let directoryTreesReporter = DirectoryTreesReporter(
    directories: [
        documentsURL
    ]
)
reporters.insert(directoryTreesReporter, at: 1)

添加您自己的自定义报告

要添加您自己的报告,您需要使用 DiagnosticsReporting 协议。

/// An example Custom Reporter.
struct CustomReporter: DiagnosticsReporting {
    static func report() -> DiagnosticsChapter {
        let diagnostics: [String: String] = [
            "Logged In": Session.isLoggedIn.description
        ]

        return DiagnosticsChapter(title: "My custom report", diagnostics: diagnostics)
    }
}

然后,您可以将此报告添加到创建方法中

var reporters = DiagnosticsReporter.DefaultReporter.allReporters
reporters.insert(CustomReporter.self, at: 1)
let report = DiagnosticsReporter.create(using: reporters)

智能洞察

默认情况下,提供标准的智能洞察

添加您自己的自定义洞察

可以根据报告中的章节提供您自己的自定义洞察。一个常见的例子是解析错误并显示关于发生的错误的智能洞察

struct SmartInsightsProvider: SmartInsightsProviding {
    func smartInsights(for chapter: DiagnosticsChapter) -> [SmartInsightProviding] {
        guard let html = chapter.diagnostics as? HTML else { return [] }
        if html.errorLogs.contains(where: { $0.contains("AppDelegate.ExampleLocalizedError") }) {
            return [
                SmartInsight(
                    name: "Localized data",
                    result: .warn(message: "An error was found regarding missing localisation.")
                )
            ]
        }
        return []
    }
}

示例项目为您提供了上述示例代码以供试用。您可以利用 html.errorLogs.debugLogs.systemLogs 快速访问报告中的特定日志。

为您的报告创建自定义 HTML 格式化程序

您可以使用 HTMLFormatting 协议来自定义 HTML 的报告方式。

只需将格式化程序传递到 DiagnosticsChapter 初始化程序中

DiagnosticsChapter(title: "UserDefaults", diagnostics: userDefaults, formatter: <#HTMLFormatting.Type#>)

交流

安装

Swift Package Manager

Swift Package Manager 是一种用于管理 Swift 代码分发的工具。 它与 Swift 构建系统集成,可以自动执行下载、编译和链接依赖项的过程。

清单文件

将 Diagnostics 作为包添加到您的 Package.swift 文件中,然后将其指定为您希望使用它的 Target 的依赖项。

import PackageDescription

let package = Package(
    name: "MyProject",
    platforms: [
       .macOS(.v10_15)
    ],
    dependencies: [
        .package(url: "https://github.com/WeTransfer/Diagnostics.git", .upToNextMajor(from: "1.8.0"))
    ],
    targets: [
        .target(
            name: "MyProject",
            dependencies: ["Diagnostics"]),
        .testTarget(
            name: "MyProjectTests",
            dependencies: ["MyProject"]),
    ]
)

Xcode

要将 Diagnostics 作为 依赖项 添加到您的 Xcode 项目中,请选择 *File > Swift Packages > Add Package Dependency*,然后输入存储库 URL:https://github.com/WeTransfer/Diagnostics.git

Carthage

Carthage 是一个分散的依赖管理工具,它可以构建您的依赖项并为您提供二进制框架。

您可以使用以下命令通过 Homebrew 安装 Carthage

$ brew update
$ brew install carthage

要使用 Carthage 将 Diagnostics 集成到您的 Xcode 项目中,请在您的 Cartfile 中指定它

github "WeTransfer/Diagnostics" ~> 1.00

运行 carthage update 以构建框架,并将构建的 Diagnostics.framework 拖到您的 Xcode 项目中。

手动

如果您不想使用上述任何依赖管理器,您可以手动将 Diagnostics 集成到您的项目中。

嵌入式框架


发行说明

有关更改列表,请参阅 CHANGELOG.md

作者

该库是作为 WeTransfer 黑客马拉松的一部分创建的。 流程已在 Twitter 上报告。

感谢

此外,还要向 1Password 致敬,感谢他们 激励我们 创建了这个库。

许可证

Diagnostics 在 MIT 许可证下可用。 有关更多信息,请参阅 LICENSE 文件。