Allow users to easily share Diagnostics with your support team to improve the flow of fixing bugs.

Related tags

Logging swift
Overview

Example mail composer Example Report

Diagnostics is a library written in Swift which makes it really easy to share Diagnostics Reports to your support team.

Features

The library allows to easily attach the Diagnostics Report as an attachment to the MFMailComposeViewController.

  • Integrated with the MFMailComposeViewController
  • Default reporters include:
    • App metadata
    • System metadata
    • System logs divided per session
    • UserDefaults
  • Possibility to filter out sensitive data using a DiagnosticsReportFilter
  • A custom DiagnosticsLogger to add your own logs
  • Flexible setup to add your own custom diagnostics
  • Native cross-platform support, e.g. iOS, iPadOS and macOS

Usage

The default report already contains a lot of valuable information and could be enough to get you going.

Make sure to set up the DiagnosticsLogger as early as possible to catch all the system logs, for example in the 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
}

Then, simply show the MFMailComposeViewController using the following code:

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(["[email protected]"])
        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)
    }
}

On macOS you could send the report by using the NSSharingService:

import AppKit
import Diagnostics

func send(report: DiagnosticsReport) {
    let service = NSSharingService(named: NSSharingService.Name.composeEmail)!
    service.recipients = ["[email protected]"]
    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])
}

Using a custom UserDefaults type

Simply set your user defaults instance by making use of:

UserDefaultsReporter.userDefaults = ..

Filtering out sensitive data

It could be that your report is containing sensitive data. You can filter this out by creating a DiagnosticsReportFilter.

The example project contains an example of this:

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
        }
    }
}

Which can be used by passing in the filter into the create(..) method:

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

Adding your own custom logs

To make your own logs appear in the logs diagnostics you need to make use of the DiagnosticsLogger.

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

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

The error logger will make use of the localized description if available which you can add by making your error conform to LocalizedError.

Adding your own custom report

To add your own report you need to make use of the DiagnosticsReporting protocol.

/// 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)
    }
}

You can then add this report to the creation method:

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

Creating a custom HTML formatter for your report

You can make use of the HTMLFormatting protocol to customize the way the HTML is reported.

Simply pass in the formatter into the DiagnosticsChapter initialiser:

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

Communication

  • If you found a bug, open an issue.
  • If you have a feature request, open an issue.
  • If you want to contribute, submit a pull request.

Installation

Swift Package Manager

The Swift Package Manager is a tool for managing the distribution of Swift code. It’s integrated with the Swift build system to automate the process of downloading, compiling, and linking dependencies.

Manifest File

Add Diagnostics as a package to your Package.swift file and then specify it as a dependency of the Target in which you wish to use it.

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

To add Diagnostics as a dependency to your Xcode project, select File > Swift Packages > Add Package Dependency and enter the repository URL: https://github.com/WeTransfer/Diagnostics.git.

Carthage

Carthage is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks.

You can install Carthage with Homebrew using the following command:

$ brew update
$ brew install carthage

To integrate Diagnostics into your Xcode project using Carthage, specify it in your Cartfile:

github "WeTransfer/Diagnostics" ~> 1.00

Run carthage update to build the framework and drag the built Diagnostics.framework into your Xcode project.

Manually

If you prefer not to use any of the aforementioned dependency managers, you can integrate Diagnostics into your project manually.

Embedded Framework

  • Open up Terminal, cd into your top-level project directory, and run the following command "if" your project is not initialized as a git repository:

    $ git init
  • Add Diagnostics as a git submodule by running the following command:

    $ git submodule add https://github.com/WeTransfer/Diagnostics.git
  • Open the new Diagnostics folder, and drag the Diagnostics folder into the Project Navigator of your application's Xcode project. This will add the SPM package as a local package.

    It should appear nested underneath your application's blue project icon. Whether it is above or below all the other Xcode groups does not matter.

  • Next, select your application project in the Project Navigator (blue project icon) to navigate to the target configuration window and select the application target under the "Targets" heading in the sidebar.

  • In the tab bar at the top of that window, open the "General" panel.

  • Click on the + button under the "Embedded Binaries" section.

  • Select Diagnostics.framework.

  • And that's it!

    The Diagnostics.framework is automagically added as a target dependency, linked framework and embedded framework in a copy files build phase which is all you need to build on the simulator and a device.


Release Notes

See CHANGELOG.md for a list of changes.

Authors

This library is created as part of the WeTransfer Hackathon. Process has been reported on Twitter.

Thanks to:

Also, a little shoutout to 1Password for inspiring us to create this library.

License

Diagnostics is available under the MIT license. See the LICENSE file for more info.

Comments
  • directory modelling suggestion

    directory modelling suggestion

    proposing something like this (or better & complete) to have all properties of directory like includeHiddenFiles, maxDepth etc. ik this is quite a big change and would not be backward compatible but i guess worth to include something similar. your thoughts on this? @AvdLee

    opened by entunidotdeb 15
  • Encode Logging for HTML so object descriptions are visible

    Encode Logging for HTML so object descriptions are visible

    Would be useful to include object description strings in the logs, but it seems they aren't logged when passed as part of the message argument in the static func DiagnosticsLogger.log(message:).

    For example, if I try to log self.description where self is of type UIViewController by calling DiagnosticsLogger.log(message: "This is the view controller description: \(self.description).") what I see in the log is something like "2020-01-27 16:00:00 | ViewController.swift:L100 | This is the view controller description: ."

    bug 
    opened by davidsteppenbeck 13
  • Diagnostics.xcodeproj is Missing

    Diagnostics.xcodeproj is Missing

    I'm trying to update to the latest version of this library but the Diagnostics.xcodeproj file is missing from the repo. Was this deliberate? If so can the installation instructions be updated please?

    opened by LeoSnek 11
  • native (aka non-catalyst) macOS support added

    native (aka non-catalyst) macOS support added

    Hi, first of all: thank you for this awesome framework! 🙂

    I want to use it with a non-catalyst app and added a enum Device that wraps the os specific information. What do you think of this solution?

    opened by JulianKahnert 7
  • Use String instead of StaticString to be more inline with swift-log behaviour

    Use String instead of StaticString to be more inline with swift-log behaviour

    I am wrapping DiagnosticsLogger inside a LogHandler from swift-log package so that I can use swift-log as the main logger for my app.

    But currently, Diagnostics is using StaticString for #file, #line, #function in the log method

    swift-log is using String instead of StaticString for #file, #line, #function

    I think, changing StaticString to String would make it more interoperable with swift-log and provides more flexibility to integrate with other libraries as well.

    opened by antranapp 5
  • Update example and readme

    Update example and readme

    Hey there, thanks for making Diagnostics public, I think it's a cool way to debug apps in production. I saw that you had a discussion about whether to use an optional array or just an empty array (https://github.com/WeTransfer/Diagnostics/pull/113/commits/e75d7e4a1d9ad647552195d6aa5b3ecbe2d79464) and decided to go the non-optional way. It seems though that you forgot to change the example, so that's what this pull request is.

    opened by mikakruschel 5
  • Support different platforms/APIs

    Support different platforms/APIs

    👋 Love the idea of this project.

    I was wondering would you be open to the idea of support sending these via other channels other than email?

    I was thinking maybe supporting a pluggable API to support different APIs for reporting issues.

    Happy to explore the idea and open a PR but wanted to see if it was inline with your vision for this?

    enhancement Stale 
    opened by a1cooke 5
  • SwiftLog Compatibility (Issue)

    SwiftLog Compatibility (Issue)

    Hi there 👋

    swift-log is a package by Apple which allows for extensive logging within applications and packages. I'd love to continue using this but route errors through to this Diagnostics package - the best of both worlds!

    In order to do this, we can create a custom "LogHandler" and simply route the data through to the existing DiagnosticsLogger. Fabulous.

    Two issues at the moment though:

    1. file/function are expected to be of type "StaticString" but Swift Log simply uses "String". It would be nice to change the signature here to allow for more flexibility in where the data comes from.
    2. the error type requires conforming to Error which is not always possible (well, isn't required by Swift Log). To get around this I've created a custom DiagnosticsError struct which accepts the contents of the SwiftLog message payload which does get around this but it would be awesome if the package allowed us to simply provide a string and potentially even metadata*.

    Support for metadata in logging messages would be handy. I'd expect this to be something that is shown to the developer if they tap on that line in the HTML report generated. It would then show a simple table with key/value pairs allowing for more context on the error.

    Hope this makes sense!

    Stale 
    opened by Sherlouk 4
  • Is it possible to separate Custom Logs & System Logs

    Is it possible to separate Custom Logs & System Logs

    Hi Guys,

    As far as I have tried there is no way to separate the custom log messages which are clubbed together in automatically created logs.

    So is it possible to create a separate report for the Custom Logs ? As the "SYSTEM:" prefixed logs have too much debug prints from different frameworks that I don't want.

    I also tried the filtering for the report, but the logs report is just on big string.

    Let me know if I missing something that I haven't tried 😅

    enhancement 
    opened by nik6018 4
  • Only Include Subset of Logs

    Only Include Subset of Logs

    Hi there, loving using the library, but had a question about how to provide less data. Every time a user sends me their diagnostics, they're sending me diagnostics for every session they've ever had. This makes parsing the report cumbersome and unnecessarily large.

    I was wondering if it'd be possible to have an option to receive only the last N session logs, via some sort of API in Diagnostics.

    Thanks a lot!

    enhancement good first issue Stale 
    opened by mergesort 4
  • Memory leaks in DiagnosticsLogger.handlePipeNotification

    Memory leaks in DiagnosticsLogger.handlePipeNotification

    I'm getting memory leaks DiagnosticsLogger.handlePipeNotification. I'm not sure if this is an actual leak or the NSData object being created is being added to an autorelease pool. Does anyone else experience this?

    Screenshot 2020-04-25 at 11 15 09
    opened by tomislav 4
  • Set minimum iOS support to iOS 11

    Set minimum iOS support to iOS 11

    We would like to integrate this great package into our app but our app is still supporting iOS 11+.

    It looks like that the main functionality of the package is available for iOS 11, only a default SmartInsight requires iOS 13+ due to Combine

    I have added some availability checks for that SmartInsight and set the minimum iOS platform version to 11.

    opened by antranapp 0
  • [Feature Request] Track user app usage path (or screen)

    [Feature Request] Track user app usage path (or screen)

    I would like to see a feature where Diagnostics has a tool that allow developers to define and track users usage path so that figuring out how a user got to a situation is a bit easier.

    For ex:

    I define screens Home, Profile, AddProfile and add event/action addProfile

    if a user has a problem and they send the Diagnostics. I could see something like...

    User session: [Home] -> [Profile] - (addProfile) -> [AddProfile]

    where [] represents the screen () represents the action

    Looking forward to hearing your feedback and opinion on this!

    opened by Prince2k3 0
  • Add (or allow for) note about device identifiers

    Add (or allow for) note about device identifiers

    For diagnostics, we add the so called "machine ID" of devices, like for example "iPhone14,5" (which is an iPhone 13).

    This can be confusing as it somewhat closely matches iOS versions (in this case, iPhone 13 running iOS 15 and up).

    Adding a note that this number (in the machine ID) is part of that identifier would alleviate the confusion.

    enhancement good first issue 
    opened by BasThomas 0
Releases(4.3.1)
Owner
WeTransfer
WeTransfer
Log messages to text files and share them by email or other way.

LogToFiles How to log messages to text files and share them by email or share center. 1 - Add the Log.swift file to your App 2 - Just log the messages

Miguel Chaves 0 Jan 9, 2022
This is how you can manage and share logs in iOS application.

Logging in Swift In this example, you can find how to print all the logs effciently in iOS application. Along with, you will find how to share logs fo

Nitin Aggarwal 8 Mar 1, 2022
A lightweight Swift logger, uses `print` in development and `NSLog` in production. Support colourful and formatted output.

Loggerithm A lightweight Swift logger, uses print in Debug and NSLog in Production with colourful output. Why In Swift, we usually use print to log in

HongHao Zhang 270 Oct 8, 2022
JustLog brings logging on iOS to the next level. It supports console, file and remote Logstash logging via TCP socket with no effort. Support for logz.io available.

JustLog JustLog takes logging on iOS to the next level. It supports console, file and remote Logstash logging via TCP socket with no effort. Support f

Just Eat 509 Dec 10, 2022
Shows your current framerate (fps) in the status bar of your iOS app

WatchdogInspector Shows your current framerate (fps) in the status bar of your iOS app Be a good citizen! Don't block your main thread! WatchdogInspec

Christian Menschel 510 Nov 24, 2022
Customizable Console UI overlay with debug log on top of your iOS App

AEConsole Customizable Console UI overlay with debug log on top of your iOS App AEConsole is built on top of AELog, so you should probably see that fi

Marko Tadić 142 Dec 21, 2022
Styling and coloring your XCTest logs on Xcode Console

XLTestLog Notes with Xcode 8 and XLTestLog Since Xcode 8 killed XcodeColors, the current way using XCTestLog on Xcode 8 is just plain texts with emoji

Xaree Lee 58 Feb 2, 2022
A simple logger for your swift applications.

AHQSLogger A simple logging system. Usage import AHQSLogger Use the following methods for loggging. Logging an information / debug You can log a simp

André Henrique da Silva 0 Dec 29, 2021
XCLog is a Swift extension that helps you print something in console when debugging your projects.

XCLog XCLog is a Swift extension that helps you print something in console when debugging your projects. Installation Open Xcode > File > Add Packages

null 1 Jan 9, 2022
BadgeLog - A light lib that helps and centralize logs in your application

BadgeLog BadgeLog is an iOS Swift library that helps you manage logs within your

Daniele 1 Feb 2, 2022
🔥 🔥 🔥Support for ORM operation,Customize the PQL syntax for quick queries,Support dynamic query,Secure thread protection mechanism,Support native operation,Support for XML configuration operations,Support compression, backup, porting MySQL, SQL Server operation,Support transaction operations.

?? ?? ??Support for ORM operation,Customize the PQL syntax for quick queries,Support dynamic query,Secure thread protection mechanism,Support native operation,Support for XML configuration operations,Support compression, backup, porting MySQL, SQL Server operation,Support transaction operations.

null 60 Dec 12, 2022
Makes it easier to support older versions of iOS by fixing things and adding missing methods

PSTModernizer PSTModernizer carefully applies patches to UIKit and related Apple frameworks to fix known radars with the least impact. The current set

PSPDFKit Labs 217 Aug 9, 2022
Push notifications allow developers to reach users, even when users aren't actively using an app!

Push notifications allow developers to reach users, even when users aren't actively using an app! With the latest update of iOS Apple provide very useful extensions which are user-friendly. In this tutorial, I am going to share the configuration, set up of Notification with the media attachments like.

MindInventory 16 Mar 3, 2022
Lightweight MetricKit-based diagnostics reporting

MeterReporter Lightweight MetricKit-based diagnostics reporting. MeterReporter will capture MetricKit payloads and relay them to a backend. It uses Me

Chime 24 Dec 26, 2022
A collection of common diagnostics and debugging utilities.

KippleDiagnostics A collection of common diagnostics and debugging utilities. ⚠️ The code in this library has been made public as-is for the purposes

Kipple 10 Sep 2, 2022
Share-sheet-example - A sample project that reproduces an issue with Share Sheets

Hello, DTS! This project demonstrates the issue I'm having with the Share Sheet.

Marcos Tanaka 0 Feb 11, 2022
Blueprints is a collection of flow layouts that is meant to make your life easier when working with collection view flow layouts.

Blueprints is a collection of flow layouts that is meant to make your life easier when working with collection view flow layouts. It comes

Christoffer Winterkvist 982 Dec 7, 2022
Make your logic flow and data flow clean and human readable

Flow What's Flow Flow is an utility/ design pattern that help developers to write simple and readable code. There are two main concerns: Flow of opera

null 18 Jun 17, 2022
Sample project displaying bugs in the StarSDK while using Kotlin Native

StarSampleSdk iOS Bugs that need to be reproduced Retrieving network printer status fails after subsequent attempts Star Bluetooth printer with an act

Bailey Pollard 1 Aug 18, 2022
A collection of bugs present in the SwiftUI beta.

Gosh Darn Bugs! GoshDarnBugs is a collection of... you guessed it, bugs. Usage Clone the repository. Open GoshDarnBugs.xcodeproj Click Run. Why? Swift

SwiftUIX 33 Aug 28, 2021