0

I'm asking this question because all answers I've found online are either outdated or not working for me.

I'm working with a customers framework and for some reason they require me to use CocoaLumberjack in the project so any suggestions on other Log-tools are useless for me, at least for this project, thank you in advance for understanding

The question:

How do I get the logs from users? I am not that familiar to logging so this is all new to me.

I've written some code with the help of numerous SO-answers and CocoaLumberjacks documentation on Github.

I'm pretty sure that I am actually logging because I can get the logs from Xcode when I run my app on a real device by doing: Xcode -> Window -> Devices and Simulators -> Select the device (and app) -> Download Container.

From the container I can see the logs. But how can I see the logs from user that are not physically here with their device?

AppDelegate.swift :

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool  {
    let formatter = LogFormatter()
    DDTTYLogger.sharedInstance.logFormatter = formatter

return true
}

my logformatter is from StackOverflow https://stackoverflow.com/a/14000342/4189589

class LogFormatter: NSObject, DDLogFormatter {
    let dateFormatter: DateFormatter

    static let sharedInstance = LogFormatter()

    override init() {
        dateFormatter = DateFormatter()
        dateFormatter.formatterBehavior = .behavior10_4
        dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss:SSS"

        DDLog.add(DDOSLogger.sharedInstance) // Uses os_log

        let fileLogger: DDFileLogger = DDFileLogger() // File Logger
        fileLogger.rollingFrequency = 60 * 60 * 24 // 24 hours
        fileLogger.logFileManager.maximumNumberOfLogFiles = 7
        DDLog.add(fileLogger)


        DDLogDebug("Debug")
        DDLogVerbose("Verbose")
        DDLogInfo("Info")
        DDLogWarn("Warn")
        DDLogError("Error")


        super.init()
    }
    func format(message logMessage: DDLogMessage) -> String? {
        let dateAndTime = dateFormatter.string(from: logMessage.timestamp)
        return "\(dateAndTime) [\(logMessage.fileName):\(logMessage.line)]: \(logMessage.message)"
    }


    var ddFileLogger: DDFileLogger!

    var logFileDataArray: [NSData] {
        get {
            let logFilePaths = ddFileLogger.logFileManager.sortedLogFilePaths
            var logFileDataArray = [NSData]()
            for logFilePath in logFilePaths {
                let fileURL = NSURL(fileURLWithPath: logFilePath)
                if let logFileData = try? NSData(contentsOf: fileURL as URL, options: NSData.ReadingOptions.mappedIfSafe) {
                    // Insert at front to reverse the order, so that oldest logs appear first.
                    logFileDataArray.insert(logFileData, at: 0)
                }
            }
            return logFileDataArray
        }
    }
}

And then I want to email the logs with a button (from same SO-answer)

func emailLogsTo(email: String) {
        if MFMailComposeViewController.canSendMail() {
            let composeVC = MFMailComposeViewController()
            composeVC.mailComposeDelegate = self

            // Configure the fields of the interface.
            composeVC.setToRecipients([email])
            composeVC.setSubject("Feedback for app")
            composeVC.setMessageBody("", isHTML: false)

            let attachmentData = NSMutableData()
            for logFileData in LogFormatter.sharedInstance.logFileDataArray {
                attachmentData.append(logFileData as Data)
            }
            composeVC.addAttachmentData(attachmentData as Data, mimeType: "text/plain", fileName: "diagnostic.log")
            self.present(composeVC, animated: true, completion: nil)
        } else {
            // Tell user about not able to send email directly.
        }
    }

What happens when I call the function to send the email is that I get an "unexpectedly found nil while implicitly unwrapping an Optional value"-error on

let logFilePaths = ddFileLogger.logFileManager.sortedLogFilePaths

in LogFormatter()

What am I doing wrong?

maniponken
  • 265
  • 3
  • 15

1 Answers1

0

I only have it in Obj-C I'm afraid:

NSMutableData *logData = [NSMutableData data];
for (NSData *logFileData2 in [self.utilities logfileData]) {
    [logData appendData:logFileData2];
}
[picker addAttachmentData:logData mimeType:@"text/plain" fileName:@"logs.txt"];

Then in my utilities class:

- (NSMutableArray *)logfileData {
    
    NSUInteger maximumLogFilesToReturn = MIN(fileLogger.logFileManager.maximumNumberOfLogFiles, 10);
    NSMutableArray *logFiles = [NSMutableArray arrayWithCapacity:maximumLogFilesToReturn];
    DDFileLogger *logger = fileLogger;
    NSArray *sortedLogFileInfos = [logger.logFileManager sortedLogFileInfos];
    for (NSUInteger i = 0; i < MIN(sortedLogFileInfos.count, maximumLogFilesToReturn); i++) {
        DDLogFileInfo *logFileInfo = [sortedLogFileInfos objectAtIndex:i];
        NSData *fileData = [NSData dataWithContentsOfFile:logFileInfo.filePath];
        [logFiles addObject:fileData];
    }
    return logFiles;
}

So maybe for you:

var logFileDataArray: [NSData] {
        get {
            let sortedLogFileInfos = ddFileLogger.logFileManager.sortedLogFileInfos
            var logFileDataArray = [NSData]()
            for logFileInfo in sortedLogFileInfos {
                if let logFileData = try? NSData(contentsOf: logFileInfo.filePath as String, options: NSData.ReadingOptions.mappedIfSafe) {
                    // Insert at front to reverse the order, so that oldest logs appear first.
                    logFileDataArray.insert(logFileData, at: 0)
                }
            }
            return logFileDataArray
        }
    }
stoutyhk
  • 243
  • 4
  • 10
  • If you want to automatically compress and upload the log files to a server, check out https://cocoapods.org/pods/Compress-Upload-CocoaLumberjack, instructions here: https://stouty.xyz/ios/2020/05/09/compress-upload-cocoalumberjack/ – stoutyhk Oct 18 '20 at 08:22