Where is Logfile stored using cocoaLumberjack
Asked Answered
S

9

65

I am Using cocoaLumberjack logging framework for iOS logging. For storing logs in a file I used this code.

DDFileLogger* fileLogger = [[DDFileLogger alloc] init];
fileLogger.rollingFrequency = 60 * 60 * 24; 
fileLogger.logFileManager.maximumNumberOfLogFiles = 7;

[DDLog addLogger:fileLogger];

DDLogVerbose(@"hello");
NSLog(@"hihihihihi");

I am unable to find where exactly the logfile generated by this code is stored. Can someone help me with this problem ?

Squadron answered 20/6, 2011 at 13:2 Comment(1)
fileLogger.logFileManager.logsDirectoryHorologe
C
75

You can download the log files from connected device, or you can send directly from app. Both approaches are described below.

Send log files from app through email, in Swift

Write this in the class where you have a reference to DDFileLogger. I would put this in a custom logger class e.g. MyLogger.swift

var ddFileLogger: DDFileLogger!

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

Then, when user taps on a button to indicate that they want to send the logs,

// Required by MFMailComposeViewController
import MessageUI

@IBAction func writeEmailTapped(sender: AnyObject) {
    if MFMailComposeViewController.canSendMail() {
        let composeVC = MFMailComposeViewController()
        composeVC.mailComposeDelegate = self

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

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

This results in a compose email pop-up with an attachment file named diagnostic.log, which is all the log files concatenated together.

Special thanks - This is pretty much a Swift translation from the Objective-C version given by the other answer.

Get log file(s) from device directly, through USB cable

If you want to get the log files that your app created while running on device,

  1. Connect your device to your mac
  2. In Xcode, go to Window -> Devices
  3. On top-left in the device list, click on the connected device.
  4. In the main panel, under Installed Apps section, click on the application in which you ran CocoaLumberjack.
  5. At the bottom of the Installed Apps list, click on the gear icon and then Download Container.
  6. In Finder, right click (show menu) on the saved .xcappdata file and select Show Package Contents
  7. Log files are saved in /AppData/Library/Caches/Logs/

Up-vote would be nice if this is helpful to you!

Carmelo answered 22/12, 2012 at 5:33 Comment(6)
Simple and works, I'm wondering if it's possible to get the logs without using Xcode? As sometimes I need other users to send the logs to meLevania
I afraid such feature has to be custom-built, i.e. allow the user to tap a button to email the files or send by web service.Carmelo
Any way this can be updated for XCode 7.1? Can't seem to follow these steps anymore. edit: Figured it out Window -> Devices -> Download Container for specific app.Oralee
Updated for Xcode 7.2 and added Swift code that sends your logs through email.Carmelo
Make sure that you import 'import MessageUI' at the top. Other wise you will get errors.Libbey
I'm using Xcode to download the container and access the log files of my iOS app. Does anyone know how to do this for a watchOS app? I cannot find a way to download the container of an Apple Watch app.Feinberg
S
65

The answers here don't seem to account for the fact that there may be multiple log files. You can use your DDFileLogger instance's logFileManager property to loop through file information. Check out DDFileLogger.h for public methods and properties. The following may be of use:

- (NSString *)logsDirectory;

- (NSArray *)unsortedLogFilePaths;
- (NSArray *)unsortedLogFileNames;
- (NSArray *)unsortedLogFileInfos;

- (NSArray *)sortedLogFilePaths;
- (NSArray *)sortedLogFileNames;
- (NSArray *)sortedLogFileInfos;

Here is my solution for getting log data (and emailing it). Note that the default number of log files is 5 as of this writing.

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

- (void)composeEmailWithDebugAttachment {
    if ([MFMailComposeViewController canSendMail]) {

        MFMailComposeViewController *mailViewController = [[MFMailComposeViewController alloc] init];
        mailViewController.mailComposeDelegate = self;
        NSMutableData *errorLogData = [NSMutableData data];
        for (NSData *errorLogFileData in [self errorLogData]) {
            [errorLogData appendData:errorLogFileData];
        }
        [mailViewController addAttachmentData:errorLogData mimeType:@"text/plain" fileName:@"errorLog.txt"];
        [mailViewController setSubject:NSLocalizedString(@"Good Subject", @"")];
        [mailViewController setToRecipients:[NSArray arrayWithObject:@"[email protected]"]];

        [self presentModalViewController:mailViewController animated:YES];

    }

    else {
        NSString *message = NSLocalizedString(@"Sorry, your issue can't be reported right now. This is most likely because no mail accounts are set up on your mobile device.", @"");
        [[[UIAlertView alloc] initWithTitle:nil message:message delegate:nil cancelButtonTitle:NSLocalizedString(@"OK", @"") otherButtonTitles: nil] show];
    }
}
Serena answered 18/7, 2012 at 15:16 Comment(7)
The problem of your code is the final log file does not sort strictly by time. You can have a test to find out it.Injure
Why not just simply to set logFileManager.maximumNumberOfLogFiles = 1?Injure
@Injure You can customize maximumNumberOfLogFiles to be whatever you want. By default the maximumFileSize for the log files (as of this writing) is 1 MB. If you simply set maximumNumberOfLogFiles to 1, then you cut the amount of logging you're capable of by 1/5. If you set maximumFileSize to 5 MB, then you're back to where you started. I'm happy to stick with the defaults chosen by the maintainer of Cocoa Lumberjack, but you're free to do whatever you want. I limit the email method to 10 log files just to keep the email size reasonable.Serena
my biggest concern is the log files does notInjure
where is KRLogManager in lumberjack?Gotthard
@DeepakThakur sorry for the confusion. KRLogManager is not part of Cocoa Lumberjack. It's something I used in the past to keep my file logger. There are multiple ways to do it. You can replace "[KRLogManager sharedInstance].fileLogger" with your own file logger instance.Serena
note: presentModalViewController:(UIViewController *)modalViewController animated:(BOOL)animated is deprecated. Use presentViewController:animated:completion: instead.Anorthic
A
33

If you're using CocoaLumberjack, you have DDFileLogger.h and you can expose the currentLogFileInfo: method that is implemented already:

@interface DDFileLogger : DDAbstractLogger <DDLogger>
...
- (DDLogFileInfo *)currentLogFileInfo;
@end

Then, you can programmatically access the path to the current file with:

// configure logger
DDFileLogger *fileLogger = [DDFileLogger new];
[DDLog addLogger:fileLogger];
[DDLog addLogger:[DDTTYLogger sharedInstance]];
DDLogInfo(@"log file at: %@", [[fileLogger currentLogFileInfo] filePath]);

Which, on my iPhone, printed:

log file at: /var/mobile/Applications/3BE1219F-78BE-491C-B68C-74D6FA0C2EF1/Library/Caches/Logs/log-5D1286.txt

to both the console and the file.

Archeology answered 10/7, 2012 at 19:45 Comment(1)
Excellent answer. Many thanks. This needs a few upvotes people.Antonio
R
17

You can control where it is stored, for example, I sometime store it in the iTunes folder for easy retrieval. Use this in the AppDelegate when setting up the fileLogger:

NSString * applicationDocumentsDirectory = [[[[NSFileManager defaultManager]    
     URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject] path];
DDLogFileManagerDefault *documentsFileManager = [[DDLogFileManagerDefault alloc]
     initWithLogsDirectory:applicationDocumentsDirectory];
DDFileLogger *fileLogger = [[DDFileLogger alloc]
     initWithLogFileManager:documentsFileManager];
Rhombohedron answered 28/9, 2013 at 9:27 Comment(2)
Can we change the file name too?Marthmartha
Excellent- I was amazed that wasn't where it was stored by default!Weaponeer
H
8

Found this to be the latest:

DDFileLogger *fileLogger = [[DDFileLogger alloc] init];

fileLogger.logFileManager.logsDirectory;//THIS

From the official code:

Default log file manager.

All log files are placed inside the logsDirectory. If a specific logsDirectory isn't specified, the default directory is used. On Mac, this is in ~/Library/Logs/. On iPhone, this is in ~/Library/Caches/Logs. Log files are named "log-.txt", where uuid is a 6 character hexadecimal consisting of the set [0123456789ABCDEF]. Archived log files are automatically deleted according to the maximumNumberOfLogFiles property.

Horologe answered 18/2, 2014 at 7:20 Comment(0)
S
7

Got the answer

It is stored in Library/Appication Support/Iphone Simulator/#version no#/applications/#your application#/documents/logs/log-3hex no>

Squadron answered 21/6, 2011 at 8:44 Comment(6)
How about accessing it on an actual iPhone device?Thornburg
Anyone else have any luck pulling the saved log files from the device?Wentz
On my simulator I found the logs were stored in ~/Library/Application Support/iPhone Simulator/#version no#/Applications/#your application uuid#/Library/Caches/Logs/log-#hex-no#>.Sender
This answer is for simulator. For logs in the device, follow the answer I gave below on Dec 22 '12. Remember to vote up if that helps!Carmelo
Try using iFunBox to check the application directories for log files.Horologe
There is one more app names iExplorer. Can use that too.Doting
E
7

Send log files from app through email, in Objective-C

Objective-C code:

In you header:

#import <MessageUI/MessageUI.h>
#import "DDLog.h"
#import "DDFileLogger.h"

In your implementation:

- (NSMutableArray *)errorLogData {
    DDFileLogger *ddFileLogger = [DDFileLogger new];
    NSArray <NSString *> *logFilePaths = [ddFileLogger.logFileManager sortedLogFilePaths];
    NSMutableArray <NSData *> *logFileDataArray = [NSMutableArray new];
    for (NSString* logFilePath in logFilePaths) {
        NSURL *fileUrl = [NSURL fileURLWithPath:logFilePath];
        NSData *logFileData = [NSData dataWithContentsOfURL:fileUrl options:NSDataReadingMappedIfSafe error:nil];
        if (logFileData) {
            [logFileDataArray insertObject:logFileData atIndex:0];
        }
    }
    return logFileDataArray;
}

- (void)composeEmailWithDebugAttachment {
    if ([MFMailComposeViewController canSendMail]) {

        MFMailComposeViewController *mailViewController = [[MFMailComposeViewController alloc] init];
        mailViewController.mailComposeDelegate = self;
        NSMutableData *errorLogData = [NSMutableData data];
        for (NSData *errorLogFileData in [self errorLogData]) {
            [errorLogData appendData:errorLogFileData];
        }
        [mailViewController addAttachmentData:errorLogData mimeType:@"text/plain" fileName:@"filename.log"];
        [mailViewController setSubject:NSLocalizedString(@"LogFile Subject", @"")];
        [mailViewController setToRecipients:[NSArray arrayWithObject:@"[email protected]"]];

        [self presentViewController:mailViewController animated:YES completion:nil];

    } else {
        NSString *message = NSLocalizedString(@"Sorry, your issue can't be reported right now. This is most likely because no mail accounts are set up on your mobile device.", @"");
        [[[UIAlertView alloc] initWithTitle:nil message:message delegate:nil cancelButtonTitle:NSLocalizedString(@"OK", @"") otherButtonTitles: nil] show];
    }
}

- (void)mailComposeController:(MFMailComposeViewController *)mailer didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error {
    [self becomeFirstResponder];
    [mailer dismissViewControllerAnimated:YES completion:nil];
}

Here is how you could add something in your Log-file:

DDLogError(@"This is an error.");
DDLogWarn(@"This is a warning.");
DDLogInfo(@"This is just a message.");
DDLogVerbose(@"This is a verbose message.");

Don't forget to set your ddLogLevel in your Constants-file.

Constants.h :

extern NSUInteger const ddLogLevel;

Comstants.m :

NSUInteger const ddLogLevel =
#ifdef DEBUG
LOG_LEVEL_VERBOSE;
#else
LOG_LEVEL_ERROR;
#endif

It is very obvious but don't forget to configure CocoaLumberjack in your AppDelegate.m file.

[DDLog addLogger:[DDASLLogger sharedInstance]];
[DDLog addLogger:[DDTTYLogger sharedInstance]];

DDFileLogger *fileLogger = [[DDFileLogger alloc] init];
[fileLogger setMaximumFileSize:(1024 * 1024)];
[fileLogger setRollingFrequency:(3600.0 * 24.0)];
[[fileLogger logFileManager] setMaximumNumberOfLogFiles:7];
[DDLog addLogger:fileLogger];

The best place to paste this code is - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions; in your AppDelegate.m file. Also you should add this line of code in your AppDelegate.m header:

#import <CocoaLumberjack/CocoaLumberjack.h>

Hope it will help.

Eirena answered 4/10, 2016 at 14:53 Comment(0)
H
2

I had to rewrite it a little to be compatible with Swift 4...

var logFileDataArray: [Data] {
    let logFilePaths = delegate.fileLogger.logFileManager.sortedLogFilePaths
    var logFileDataArray = [Data]()
    for logFilePath in logFilePaths {
        let fileURL = URL(fileURLWithPath: logFilePath)
        if let logFileData =
            try? Data(contentsOf: fileURL, options: Data.ReadingOptions.mappedIfSafe) {
            logFileDataArray.insert(logFileData, at: 0)
        }
    }
    return logFileDataArray
}

func sendApplicationLog(text: String) {
    if MFMailComposeViewController.canSendMail() {
        let controller = MFMailComposeViewController()
        controller.mailComposeDelegate = self
        controller.setToRecipients(["[email protected]"])
        controller.setSubject("Log of motorkari iOS")
        controller.setMessageBody(text, isHTML: false)
        var attachmentData = Data()
        for logFileData in logFileDataArray { attachmentData.append(logFileData) }
        controller.addAttachmentData(attachmentData, mimeType: "text/plain",
                                    fileName: "motorkari_ios_application.log")
        present(controller, animated: true, completion: nil)
    } else {
        showMessage("Log cannot be send !")
    }
}
Hervey answered 13/3, 2019 at 6:16 Comment(0)
W
1

I don't know if this might help somebody else... I took the previous answers and passed it to Swift 5. Apart from getting all the paths, it prints the content.

let logFileLogger = DDFileLogger()
print(logFileLogger.logFileManager.logsDirectory)

for path in logFileLogger.logFileManager.sortedLogFilePaths {
    do {
        let content = try String(contentsOfFile: path, encoding: .utf8)
        print(content)
    } catch let error as NSError {
        print("Error: \(error.localizedDescription)")
    }
}
Wichman answered 2/4, 2020 at 16:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.