9

I have been working on a framework for several applications which implements multi-level logging. It is mainly used in applications used in-house to test communications with other devices, yet is also used in some applications that we will be distributing.

Is there any way to catch an uncaught exception and promptly execute code to save the exception to the log file? At the moment, the Log class simply writes to file every so often, alternating between two files in case of write failure, etc. This works alright, but it would be great if it could see that an unhandled exception occurs, write any unwritten log entries to file, note that an exception happened and log its details, and then allow the app to crash.

If there is some way of catching unhandled exceptions app-wide, I would think it would be something like:

appDidReceiveUnhandledException:(NSException *)exception
{
    //write log to disk
    //append exception info to log file
    //rethrow exception
}

If anyone can give me insight or suggestions on whether or not this is possible, it would be greatly appreciated.

mbuc91
  • 1,270
  • 11
  • 17
  • 1
    `@try { } @catch(NSException *ex) { }` to the rescue. Or maybe some method swizzling on `NSException`? –  Mar 11 '13 at 21:27
  • 1
    See [NSSetUncaughtExceptionHandler](https://developer.apple.com/library/ios/#documentation/Cocoa/Reference/Foundation/Miscellaneous/Foundation_Functions/Reference/reference.html) (though it's not terribly reliable). – Hot Licks Mar 11 '13 at 21:33
  • See [here](http://stackoverflow.com/a/12268397/581994). – Hot Licks Mar 11 '13 at 21:35

3 Answers3

18

You can add an application-wide unhandled exception handler in your AppDelegate:

void HandleExceptions(NSException *exception) {
    NSLog(@"The app has encountered an unhandled exception: %@", [exception debugDescription]);
    // Save application data on crash
}

Reference it in the AppDelegate didFinishLaunchingWithOptions as:

NSSetUncaughtExceptionHandler(&HandleExceptions);
Richard Brown
  • 11,346
  • 4
  • 32
  • 43
3

You can set your own exception handler by calling NSSetUncaughtExceptionHandler()

You can call it you you app delegate

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

#ifdef DEBUG
    NSSetUncaughtExceptionHandler(&uncaughtExceptionHandler);
#endif

...

}

As you can see you need to pass a function pointer to a function like this one:

void uncaughtExceptionHandler(NSException *exception)
{
    NSLog(@"App Crash:\n%@", exception);
    NSLog(@"Stack Trace:\n%@", [exception callStackSymbols]);
}
Luca Bernardi
  • 4,191
  • 2
  • 31
  • 39
1

You have to set your exception handler, this is best done in your app delegate in applicationDidFinishLaunching, ie:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    NSSetUncaughtExceptionHandler(&mExceptionHandler);
}

- (void)mExceptionHandler(NSException *exception) {
    NSLog(@"Exception %@", exception);
}
Zaid Daghestani
  • 8,555
  • 3
  • 34
  • 44