Using Timber, I wrote a FileTree logger that will write Android logs to files on the disk. I buffer the logs, flushing to disk every 2 minutes, or 100 log messages, whichever comes first. The buffer prevents each log message from triggering a IO write, so as to not overload the IO resources by immediately writing every single log message.
I'm using RxJava to handle this task. A short snippet (taken from here):
logBuffer.observeOn(Schedulers.computation())
.doOnEach((log) -> {
processed++;
if(processed % 100 == 0) {
flush();
}
})
.buffer(flush.mergeWith(Observable.interval(2, TimeUnit.MINUTES)))
.subscribeOn(Schedulers.io())
.subscribe((logs) -> {
// Flush the logs to the file
try {
File logFile = new File(logDir, "app.log");
FileWriter fw = new FileWriter(logFile, true);
for(LogMessage msg : logs) {
fw.append(msg.toString());
}
fw.flush();
flushCompleted.onNext(logFile.length());
} catch(Exception e) {
Timber.e(e, "Failed to flush logs");
}
});
I use the "flush" subject if I need to trigger a flush manually.
I plant the FileTree in Timber within the Application's onCreate():
public class App extends Application {
@Override
public void onCreate() {
super.onCreate();
Timber.plant(new Timber.DebugTree(), new FileTree(getApplicationContext()));
}
}
This is also where the subscriptions for RxJava are setup. I have two questions about this:
- If the application closes for some reason, presumably the log flush won't get triggered by a timer or by receiving enough log messages. Should I simply place a manual call to flush in every
Activity'sonDestroy()function? Is there a better way to do this? - Secondly,
RxJavacomplains that I ignore the result of ".subscribe()", presumably because I won't ever calldispose()on it. I'm not sure exactly how to handle this case. The subscriptions have the same lifecycle as the Application itself, so I'm not sure that removing it within a particularActivity'sonDestroywould make sense. Further, my app has several entry points (the main activity, a background service, and a few others) that all make use of the logging facilities, so when to unsubscribe/dispose theRxJavasubscriptions isn't clear. My intuition is that I don't need to dispose them because they will be cleared whenever the Application is removed by the operating system. Any ideas?
BaseActivity, or useApplication'sactivity state callbacks API ( https://developer.android.com/reference/android/app/Application#registerActivityLifecycleCallbacks(android.app.Application.ActivityLifecycleCallbacks) ).I should note that for the purposes of chosing a lifecycle method to flush, no method after
onPause()is guaranteed to be called by the OS. If the operating system needs to kill your app for low-memory, it's not guaranteed thatonDestroy()will be called ( rather it will just kill your process ).To quote https://developer.android.com/reference/android/app/Activity :