← Writing

Redirecting NSLog Output to a File on Demand for iPhone Debugging

NSLog can be a great tool for debugging when developing iPhone applications. It outputs a formatted message to StdErr, which is the console view in Xcode by default. However, that doesn’t help much when you want to collect information in a file on a real device over several hours of real world usage. Sure, you can write separate routines to log information to a file yourself, but why muddy your code with all that extra code, when, instead, you can simply redirect existing NSLog output to a file?

Whenever you want to redirect NSLog output to a file, you can do so by using a simple freopen statement to redirect StdErr to a file, like this:

NSString *cachesDirectory = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *logPath = [cachesDirectory stringByAppendingPathComponent:@"application.log"];
freopen([logPath cStringUsingEncoding:NSASCIIStringEncoding], "a+", stderr);

The above statements will cause all output for StdErr, including all NSLog output, to be redirected to a file called “application.log”. The file will be created if it doesn’t already exist, and the output will be appended to the file contents, if one already exists. If you use “w+” as the second parameter to the freopen call, the file will be truncated, its contents erased, each time this statement is called. Notice also that the file is created in <Application_Home>/Library/Caches folder, rather than the <Application_Home>/Documents folder. Caches folder is not backed up by iTunes in the later versions of iOS, and I think is more suitable for log files.

Please note that redirecting StdErr to a file will slow down your application, depending on how much and how often you output logging information. If you want to be able to redirect StdErr back to the console, you can do so by first saving the original StdErr like this:

savedStdErr = dup(STDERR_FILENO);

And then, later when you want to revert output, you can do so like this:

fflush(stderr);
dup2(savedStdErr, STDERR_FILENO);
close(savedStdErr)

You can download a sample project implementing the above ideas here.

You can retrieve the saved file easily when you connect the device to your Mac via USB. Open Xcode, click on menu item Window -> Organizer, and click on the name of your device when the Organizer window opens. You will see a list of all applications on the device on the lower right side, titled ‘Applications’. Click on the disclosure triangle to the left of your application’s name, and you should see a row titled ‘Application Data’ and if you click on the arrow to the right, you can download all of the application data on your Mac.

Xcode Organizer window showing the Application Data download arrow for retrieving files from a connected iPhone

Comments10 archived

Imported from Disqus when the comment system was retired. The conversation lives here as a static archive — no replies possible.

  1. mklement

    Very helpful - thanks!

  2. Swetha

    How do i show output to console window as well as in a file simultaneously?

    1. azizuysal

      @Swetha: You need to write your own logger class that outputs logging messages to a text file. It can simultaneously use NSLog to output to console as well.

    2. stavash

      You can use if (!isatty(STDERR_FILENO)) {...} and conditionally defer stderr output to file only when device is not connected to the debugger.

  3. Apophenia Overload

    Any suggestions as to how to remotely send the log file to a server as a bug report or user feedback?

  4. Apophenia Overload

    And a followup question: Is it true that doing this causes considerable slowdown, because writing to the file system is slow?

  5. coopertatiana

    Works fine, thanks!

  6. Anton Smirnov

    Helpful, thanks!
    The only question is can output data be appended the moment it's fprintf() is invoked (not when file is closed/flushed)?

  7. Petr

    Nice soution, thank you! Will Apple reject my app for use of freopen? Did you publish any apps with this code? (I’m thinking about suppressing all output of my app with redirecting to /dev/null)

  8. charlesr1971

    This is really fantastic. Thank you for showing me how to redirect back to the console. This was the missing piece of the puzzle.

    I am calling the line:

    freopen([logPath cStringUsingEncoding:NSASCIIStringEncoding], "w+", stderr);

    So that the file data is erased each time & starts a fresh logging cycle. I have done this, as a safeguard to prevent a large log file from building up.

    Am I right in saying that, if I call this function:

    - (void)redirectStdErrToFile;

    Inside:

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions;

    It should only log things to file, every application launch cycle?

    I will set a macro that defines whether to turn off this functionality & return to console, rather than using a switch button.

    However, l do like your switch button idea. But it will only be useful to me, if my app testers are able to send me the log file from within the device.

    So, my question is:

    Is there any way to send the log file to device -> settings -> general -> about -> diagnostics & usage -> diagnostics & usage data

    Then, my testers can copy the text & e-mail to me.

    My testers do not have XCode & therefore cannot open up XCode -> Organiser -> App -> Application Data -> log file

    By, the way I am saving my log file to the 'documents' folder & not the cache.