UPDATE: Please see "accepted" solution below
When my app creates an unhandled exception, rather than simply terminating, I'd like to first give the user an opportunity to send a log file. I realize that doing more work after getting a random exception is risky but, hey, the worst is the app finishes crashing and the log file doesn't get sent. This is turning out to be trickier than I expected :)
What works: (1) trapping the uncaught exception, (2) extracting log info and writing to a file.
What doesn't work yet: (3) starting an activity to send email. Ultimately, I'll have yet another activity to ask the user's permission. If I get the email activity working, I don't expect much trouble for the other.
The crux of the problem is that the unhandled exception is caught in my Application class. Since that isn't an Activity, it's not obvious how to start an activity with Intent.ACTION_SEND. That is, normally to start an activity one calls startActivity and resumes with onActivityResult. These methods are supported by Activity but not by Application.
Any suggestions on how to do this?
Here are some code snips as a starting guide:
public class MyApplication extends Application
{
defaultUncaughtHandler = Thread.getDefaultUncaughtExceptionHandler();
public void onCreate ()
{
Thread.setDefaultUncaughtExceptionHandler (new Thread.UncaughtExceptionHandler()
{
@Override
public void uncaughtException (Thread thread, Throwable e)
{
handleUncaughtException (thread, e);
}
});
}
private void handleUncaughtException (Thread thread, Throwable e)
{
String fullFileName = extractLogToFile(); // code not shown
// The following shows what I'd like, though it won't work like this.
Intent intent = new Intent (Intent.ACTION_SEND);
intent.setType ("plain/text");
intent.putExtra (Intent.EXTRA_EMAIL, new String[] {"me@mydomain.com"});
intent.putExtra (Intent.EXTRA_SUBJECT, "log file");
intent.putExtra (Intent.EXTRA_STREAM, Uri.parse ("file://" + fullFileName));
startActivityForResult (intent, ACTIVITY_REQUEST_SEND_LOG);
}
public void onActivityResult (int requestCode, int resultCode, Intent data)
{
if (requestCode == ACTIVITY_REQUEST_SEND_LOG)
System.exit(1);
}
}
Here's the complete solution (almost: I omitted the UI layout and button handling) - derived from a lot of experimentation and various posts from others related to issues that came up along the way.
There are a number of things you need to do:
Now, here are the details:
(1 & 2) Handle uncaughtException, start send log activity:
(3) Extract log (I put this an my SendLog Activity):
(4) Start an email app (also in my SendLog Activity):
(3 & 4) Here's what SendLog looks like (you'll have to add the UI, though):
(5) Manifest:
(6) Setup Proguard:
In project.properties, change the config line. You must specify "optimize" or Proguard will not remove Log.v() and Log.d() calls.
In proguard-project.txt, add the following. This tell Proguard to assume Log.v and Log.d have no side effects (even though they do since they write to the logs) and thus can be removed during optimization:
That's it! If you have any suggestions for improvements to this, please let me know and I may update this.
Today there are many crash reprting tools that do this easily.
crashlytics - A crash reporting tool, free of charge but gives you basic reports Advantages : Free
Gryphonet - A more advanced reporting tool, requires some kind of fee. Advantages : Easy recreation of crashes, ANR's, slowness...
If you are a private developer I would suggest Crashlytics, but if it's a big organization, I would go for Gryphonet.
Good Luck!
Nicely explained. But one observation here, instead of writing into file using File Writer and Streaming, I made use of the logcat -f option directly. Here is the code
This helped me in flushing the latest buffer info. Using File streaming gave me one issue that it was not flushing the latest logs from buffer. But anyway, this was really helpful guide. Thank you.
@PeriHartman's answer works well when the UI thread throws uncaught exception. I made some improvements for when the uncaught exception is thrown by a non UI thread.
Try using ACRA instead - it handles sending the stack trace as well as tons of other useful debug information to your backend, or to Google Docs document you've set up.
https://github.com/ACRA/acra