In our Android App we wish to capture Log messages.
We kick off a Process
which invokes LogCat, asking for rolling logs.
LogCat starts. We get rolling logs. We get the right number of files. In fact allAll the arguments we specify are honored except for the individual file size argument, -r
. No matter what argument we specify for -r we get individual 16K files. We've tried -r 10000
and -r 250
and -r 100
and -r 64
. Always 16K files.
EDIT: -r 12
(smaller value) also ignored - get 16K files.
EDIT: As of a few months ago, LogCat
spontaneously started to honor our size argument. It must have been a bug that they fixed. Thanks all.
What are we doing wrong?
Here's the call we make:
logcat -f /storage/emulated/0/Android/data/com.example.my.app/files/MyAppLogs.txt -v time -n 20 -r 64 CookActivity:D CookingService:D destroy:W distcooked:D finishedSpeaking:D freeMealReceived:D gotFocus:D keptMenu:D settingNextWakeAlarm:D settingReTryAlarm:D speakBroadcast:D speakerDestroyed:D stopped:D timeupdate:D dalvikvm:W dalvikvm-heap:W MyImageCache:D LogCatManager:D MainActivity:I MainActivity:D *:W *:E
If you're curious:
We take the code structure and file system checks from this and other StackOverflow pages: Android unable to run logcat from application
We take the call to exec() from this page and others: http://docs.oracle.com/javase/6/docs/api/java/lang/Runtime.html#exec(java.lang.String[])
From other places we learned that we have to request the app permissions for WRITE_EXTERNAL_STORAGE and READ_LOGS
EDIT: The class we use to manage logCat:
package com.example.my.app.main;
import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader;
import android.app.Activity; import android.os.Environment; import android.os.SystemClock; import android.util.Log;
/**
* Manages starting the external logCat process and shutting it down.
*
* When this is asked to start an instance of logCat for this application and one is already running,
* a second one is not started.
*
* When this application is shut down gracefully, it tries to shut down its instance of logCat.
*
*/
public class LogCatManager {
private static final String TAG = "LogCatManager";//The process for logCat - set up initially, tear down when we're done. Process logSaveProcess = null; // logcat -f /storage/emulated/0/Android/data/com.example.my.app/files/MyAppLogs.txt -v time -n 20 -r 64 CookActivity:D CookingService:D destroy:W distcooked:D finishedSpeaking:D freeMealReceived:D gotFocus:D keptMenu:D settingNextWakeAlarm:D settingReTryAlarm:D speakBroadcast:D speakerDestroyed:D stopped:D timeupdate:D dalvikvm:W dalvikvm-heap:W MyImageCache:D LogCatManager:D MainActivity:I MainActivity:D *:W *:E //The index to which the output file name string should be assigned. static final int logCatOutputFileIndex = 2 ; //The index to which the number-of-Kbytes-in-each-logfile threshhold should be assigned. static final int logCatLogKSizeIndex = 8; //The index to which the number-of-logfiles should be assigned static final int logCatLogNLogsIndex = 6; String logCatTokenArray[] = { "logcat" ,"-f" ,"--THE_OUTPUT_FILE_NAME_GOES_HERE--" ,"-v" ,"time" ,"-n" ,"--THE_NUMBER_OF_LOGFILES_GOES_HERE--" ,"-r" ,"--THE_MAX_LOGFILE_SIZE_GOES_HERE--" /*\ * entries after this are search criteria - logCat entries matching these are included in the saved file \*/ ,"CookActivity:D" ,"CookingService:D" ,"destroy:W" ,"distcooked:D" ,"finishedSpeaking:D" ,"freeMealReceived:D" ,"gotFocus:D" ,"keptMenu:D" ,"settingNextWakeAlarm:D" ,"settingReTryAlarm:D" ,"speakBroadcast:D" ,"speakerDestroyed:D" ,"stopped:D" ,"timeupdate:D" ,"dalvikvm:W" ,"dalvikvm-heap:W" ,"MyImageCache:D" ,"LogCatManager:D" ,"MainActivity:I" ,"MainActivity:D" /*\ * these mean 'anything with W or E, include'. \*/ ,"*:W" ,"*:E" }; /** Turns on and off saving logCat data to a file. **/ public void setLogSaving(Activity act, boolean shouldStart) { try { if (shouldStart) { if (logSaveProcess == null) { Log.i(TAG, "logCat starting LogSaving"); startLogSaving(act); } else { Log.e(TAG,"logCat asked to start LogSaving - but saving already happening (process reference not null)! request ignored"); } } else { if (logSaveProcess == null) { Log.e(TAG,"could not simply destroy logCat process - have no reference"); } else { Log.i(TAG,"have reference to logCat process object - stopping LogSaving"); logSaveProcess.destroy(); logSaveProcess = null; } } } catch(Exception e) { Log.e(TAG,"exception in setLogSaving("+ shouldStart + ") - " + e.toString()); } } //10-18-2014 current experience with current software and logCat arguments // has RM being able to record about 140 minutes of log data in a megabyte. //The settings below attempt to give us more than 10 hours on average before rollover. public final static String SAVINGLOGFILENAME = "MyAppLogs.txt" ; public final static String SAVINGLOGMAXSIZE = "12" ; //... "64", "100", "250", "10000" (10Meg) too big. public final static String SAVINGLOGMAXNLOGS = "20" ; //10 has also been honored... // Took structure and file system checks from this and other StackOverflow pages: // https://stackoverflow.com/questions/6219345/android-unable-to-run-logcat-from-application?rq=1 // // Took call to exec() from this and others: // http://docs.oracle.com/javase/6/docs/api/java/lang/Runtime.html#exec(java.lang.String[]) // // ** make sure your app has the WRITE_EXTERNAL_STORAGE and READ_LOGS permissions ** // private void startLogSaving(Activity act) { String filename = null; String directory = null; String fullPath = null; String externalStorageState = null; // The directory will depend on if we have external storage available to us or not try { filename = SAVINGLOGFILENAME ; externalStorageState = Environment.getExternalStorageState(); if (externalStorageState.equals(Environment.MEDIA_MOUNTED)) { if(android.os.Build.VERSION.SDK_INT <= 7) { directory = Environment.getExternalStorageDirectory().getAbsolutePath(); } else { directory = act.getExternalFilesDir(null).getAbsolutePath(); } } else { directory = act.getFilesDir().getAbsolutePath(); } fullPath = directory + File.separator + filename; // Finish token array by contributing the output file name. logCatTokenArray[ logCatOutputFileIndex ] = fullPath ; // ...and the max size. logCatTokenArray[ logCatLogKSizeIndex ] = SAVINGLOGMAXSIZE ; // ...and the max number of log files (rotating). logCatTokenArray[ logCatLogNLogsIndex ] = SAVINGLOGMAXNLOGS ; Log.w(TAG, "About to start LogCat - " + strJoin(logCatTokenArray, " ") ); logSaveProcess = Runtime.getRuntime().exec( logCatTokenArray ); } catch (Exception e) { Log.e(TAG, "startLogSaving, exception: " + e.getMessage()); } } // from https://stackoverflow.com/questions/1978933/a-quick-and-easy-way-to-join-array-elements-with-a-separator-the-oposite-of-spl private static String strJoin(String[] aArr, String sSep) { StringBuilder sbStr = new StringBuilder(); for (int i = 0, il = aArr.length; i < il; i++) { if (i > 0) sbStr.append(sSep); sbStr.append(aArr[i]); } return sbStr.toString(); }
}