When writing Cocoa the common function to write simple text messages in a non-GUI manner is NSLog(), rather than something like fprintf(). Mainly this is because NSLog adds the "%@" format specifier that (usually) works nicely when printing Cocoa objects. This is especially useful with caveman-style debugging, where instead of using a debugger you just add a ton of print statements and then pore over the results to see what went wrong. That can be ugly but most developers have resorted to it at some point.

Sometimes, though, you don't want those messages to just go to console and (eventually) disappear. It can be useful to make them go somewhere for later reference. The trick here is realizing that NSLog sends its messages to Unix's standard error for the process, which in a GUI app gets sent to the system console. Standard error is malleable, though, and can point wherever you want it to point. So:

// Set permissions for our NSLog file
umask(022);
// Send stderr to our file
FILE *newStderr = freopen("/tmp/redirect.log", "a", stderr);

NSLog(@"This goes to the file");

The effect of the call to freopen() is to open the file at the specified path, and make the already-existing stderr stream use it. Voila! In this case I'm using a mode of "a" so that I'll append to the file if it exists; if you'd prefer to overwrite it, change that to "w". Note that since we're redirecting standard error, anything else that uses stderr will also be redirected.

Even better, you can temporarily redirect NSLog, yet later restore normal NSLog/stderr operation if desired. This involves saving a reference to stderr before redirecting it, and then later restoring stderr to its original state.

#include <unistd.h>

// Set permissions for our NSLog file
umask(022);

// Save stderr so it can be restored.
int stderrSave = dup(STDERR_FILENO);

// Send stderr to our file
FILE *newStderr = freopen("/tmp/redirect.log", "a", stderr);

NSLog(@"This goes to the file");

// Flush before restoring stderr
fflush(stderr);

// Now restore stderr, so new output goes to console.
dup2(stderrSave, STDERR_FILENO);
close(stderrSave);

// This NSLog will go to the console.
NSLog(@"This goes to the console");