19

I have cocoa application running on OS X. I have used NSLog for debugging purpose. Now I want to redirect the log statements to file instead of console.

I have used this method but it results logging in Console as well as in file.

- (BOOL)redirectNSLog
{
    // Create log file
    [@"" writeToFile:@"/NSLog.txt" atomically:YES encoding:NSUTF8StringEncoding error:nil];
    id fileHandle = [NSFileHandle fileHandleForWritingAtPath:@"/NSLog.txt"];
    if (!fileHandle)    return NSLog(@"Opening log failed"), NO;
    [fileHandle retain];

    // Redirect stderr
    int err = dup2([fileHandle fileDescriptor], STDERR_FILENO);
    if (!err)   return  NSLog(@"Couldn't redirect stderr"), NO;

    return  YES;
}

Is it possible to not have log statement in console but only in file ??

Unicorn
  • 2,382
  • 6
  • 29
  • 42

4 Answers4

70

Step 1: Include following function in AppDelegate:

 - (void) redirectConsoleLogToDocumentFolder
 {
       NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
                                                     NSUserDomainMask, YES);
       NSString *documentsDirectory = [paths objectAtIndex:0];
       NSString *logPath = [documentsDirectory stringByAppendingPathComponent:@"console.log"];
       freopen([logPath fileSystemRepresentation],"a+",stderr);
 }

Step 2: Call this function at the start of function applicationDidFinishLaunchingWithOptions...

Thats it, Every NSLog() will now get redirected to this console.log file, which you can find in the documents directory.

Yuji
  • 34,103
  • 3
  • 70
  • 88
  • 3
    Definitely a better answer than mine:) Changed `cStringWithEncoding:NSASCII...` to `fileSystemRepresentation`. People can have non-ASCII letters in the path (which is unlikely on `~/Documents/`) – Yuji May 09 '11 at 17:37
  • 1
    Wonderful! It also works for iOS apps. You will find the console.log inside a directory like this: `/Users/Fernando/Library/Application Support/iPhone Simulator/6.1/Applications/18FB521C-A782-44E4-9DFA-B963FD7DC44B/Documents/console.log` Thanks! :-) – backslash-f Mar 25 '13 at 17:29
  • 1
    To close/stop writing output in console - use `fflush(fileP); fclose(fileP);` To get file pointer, use `FILE *fileP = freopen([logPath..` in above method and keep this pointer around. This way you can wrap around specific flow/nslog calls to this output file. – Ashok Nov 14 '13 at 16:14
  • Also, if you don't see the whole output (some part is missing) you can add a function `void flush_std_err() { fflush(stderr); }` and hook on `at_exit` somewhere in main `at_exit(flush_std_err)` – AlexDenisov Oct 14 '15 at 13:46
  • 3
    Could you explain why the stream is from stderr – Danyun Liu Sep 20 '16 at 02:50
  • 3
    why using stderr instead of stdout in freopen? – Wingzero Sep 20 '16 at 03:34
12

Recently i have faced similar requirement and this is how i have done it.

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Override point for customization after application launch.

    [self redirectConsoleLogToDocumentFolder];


    return YES;
}

- (void) redirectConsoleLogToDocumentFolder
{
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
                                                         NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *logPath = [documentsDirectory stringByAppendingPathComponent:@"console.txt"];
    freopen([logPath fileSystemRepresentation],"a+",stderr);
} 

And Now if you want to this console to user

-(void)displayLog{

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths firstObject];
    NSString *logPath = [documentsDirectory stringByAppendingPathComponent:@"console.txt"];

    NSError *err = nil;
    NSString *fileContents = [NSString stringWithContentsOfFile:logPath
                                                       encoding:NSUTF8StringEncoding
                                                          error:&err];
    if (fileContents == nil) {
        NSLog(@"Error reading %@: %@", logPath, err);
    } else {
        self.textView.text = fileContents;
    }

}
user1227928
  • 831
  • 1
  • 12
  • 27
3

You may be interested in CocoaLumberjack. It is a very flexible logging framework for both Mac OS X and iOS. One logging statement can be sent not only to the console but to a file simultaneously. Plus it is actually faster then NSLog. I use it in a project that has common code for both OS X and iOS.

dbainbridge
  • 1,190
  • 11
  • 18
  • Thanks dbainbridge. I am using the same framework for my apps. – Unicorn Feb 14 '12 at 08:42
  • Does CocoaLumberjack redirect NSLog? I searched it's code base no sign of freopen.I guess not. – over_optimistic Apr 30 '14 at 14:30
  • It would be better to replace your NSLogs with the proper DDLog* statements with CocoaLumberjack. CocoaLumberjack also uses GCD so it is much faster then simply replacing NSLog with a different method. – dbainbridge May 01 '14 at 21:22
-9

NSLog is made to log into the console. You need to define your own function MyLog or whatever, and replace all occurrences of NSLog into MyLog.

Yuji
  • 34,103
  • 3
  • 70
  • 88
  • I did define my function and replace NSLog with my defined function. But still I do see logs in Cosole application as well as in log file which I created. I liked the idea of defining own function for logging. – Unicorn Mar 14 '11 at 09:15
  • Confirm that the file creation worked. I have tested this code and it works. The linked code writes the file to root which I changed to my local home directory. – john elemans Aug 12 '16 at 16:27