15

I am attempting to find a logging framework for a Cocoa application, written in ObjC.

What I've attempted so far:

  1. Use NSLog, but then realise that it is very hard to configure and redirect. I suppose I could hack it and write a macro that obtains the information I want, such as thread ID, current function, current line, current file, current time, the message, etcetera, and then uses NSLog, however...
  2. NSLog ultimately uses NSLogv, which ultimately uses asl, so I thought "fantastic", I tried using asl instead, with the default client and context (If I was doing this properly, I would've had to create a new client for each thread), however, unless I create a macro, this is also very verbose, and I noticed that the logs sent via asl got broadcast system wide, whereas NSLog only logged to stderr, and I want them to both go to the same log!
  3. I then noticed that errors, are formatted in a different way (different datestamp, etc), so there is now a third logging context.

What l logging framework setup can I use to have ALL messages logged through that framework in a convenient fashion so that if there is a problem with an application, a developer can get the log files, and figure out what went wrong?

I don't want to simply redirect stderr, I want to have a structured log output that contains all of the logs. I don't want some logs going to standard output, I don't want any logs sent to a syslogd, I just want all the logs written to a single file, that reliably identifies all the pertinent information about that log message (such as thread ID, message, the function that called the logger, etcetera), in format that is easy to view and visualise.

What I want is to redirect all current logs to the new destination.

Does anyone have any suggestions?

EDIT:

Effectively, what I want to do, in ObjC terms is:

  1. Do "method swizzling" on the NSLog function. Is this possible? Is it possible to (re)configure the use of the Apple System Logger to override any prior configuration of the service within the same application?
  2. Determine all the places where I have to catch unhandled exceptions. THis includes, but possibly isn't limited to: Unhandled Cocoa Exceptions. Unhandled ObjC exceptions. Unhandled C++ exceptions. Unix Signals.
  3. Catch and log the stack for errors such as those raised by CoreGraphics. (The ones that simply log a message saying "Add a breakpoint using your debugger!!!").
Arafangion
  • 11,517
  • 1
  • 40
  • 72

3 Answers3

11

You can intercept NSLog() messages (but not ASL in general) using _NSSetLogCStringFunction(). It’s documented here for WebObjects 4 for Windows, but it exists in current Mac OS and iOS releases too. However, it’s a private function that may go away at any time, so you shouldn’t rely on it in released code.

If you want to be able to safely do this for non-debug builds, I suggest duplicating this enhancement request on Radar.

Jens Ayton
  • 14,532
  • 3
  • 33
  • 47
9

You can use the Foundation function NSSetUncaughtExceptionHandler to set a callback function that will handle all uncaught exceptions:

void CustomLogger(NSString *format, ...) {
   //do other awesome logging stuff here...
}

void uncaughtExceptionHandler(NSException *exception) {
   //do something useful, like this:
   CustomLogger(@"%@", [exception reason]);
}

NSSetUncaughtExceptionHandler(&uncaughtExceptionHandler);

For NSLog, you can use a macro to override it :)

#define NSLog(...) CustomLogger(__VA_ARGS__);
Jacob Relkin
  • 161,348
  • 33
  • 346
  • 320
  • Thanks for the uncaughtExceptionHandler, however I'm still seeing logs that are apparently using asl at some point. How do I redirect those, including those sent via an ordinary NSLog? – Arafangion Dec 02 '10 at 06:10
  • @Arafangion Well, as you can see, I use a macro to tell the preprocessor to substitute all occurrences of `NSLog` with `CustomLogger`, which is where you should be doing all the heavy lifting. – Jacob Relkin Dec 02 '10 at 06:18
  • That's fine for code and libraries' you've written, but what about other libraries? For example, the following line never appears in my logs, but it does appear in gdb: Thu Dec 2 17:21:12 mymachine myproject[16215] : kCGErrorIllegalArgument: _CGSFindSharedWindow: WID -1 – Arafangion Dec 02 '10 at 06:22
  • In other words, how do I override the destination of all messages sent to syslog from my application, given that I control the entry point of my application (because I have the code!) – Arafangion Dec 02 '10 at 06:24
  • Joe: It did answer a part of it, but indeed, not the main portion. I was one of those who up voted it, because it was helpful. – Arafangion Dec 13 '10 at 23:02
6

You can redirect standard error output to a file. Here is the method that redirects console output into a file in application’s Documents folder. This can be useful when you want to test your app outside your development studio, unplugged from your mac.

-(void) redirectNSLogToDocuments {
 NSArray *allPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
 NSString *documentsDirectory = [allPaths objectAtIndex:0];
 NSString *pathForLog = [documentsDirectory stringByAppendingPathComponent:@"yourFile.txt"];
 freopen([pathForLog cStringUsingEncoding:NSASCIIStringEncoding],"a+",stderr);
}

After executing this method all output generated by NSLog will be forwarded to specified file. To get your saved file open Organizer, browse application’s files and save Application Data somewhere in your file system, than simply browse to Documents folder.

BadPirate
  • 25,802
  • 10
  • 92
  • 123
Rafał Sroka
  • 39,540
  • 23
  • 113
  • 143
  • This method worked great! I used iExplorer to read the document. Here's a download link for mac: http://download.cnet.com/iExplorer/3000-2141_4-10968991.html – funroll Aug 27 '13 at 21:02