I'm planning to implement a logging mechanism for security-related messages in Android. I want to be able to use it in the Android source code like the Log class, e.g. SecuLog.log(String msg);
It shall in the following ways differ from the normal Log
No levels like DEBUG, INFO, etc...
Output should directly go into a File on the device. There must not be the need of redirecting Logcat output for example.
As multiple processes shall be able to log security-related messages I failed with just implementing a LoggingClass in com.android.util with a static PrintWriter.
static {
try {
writer = new PrintWriter("data/secu.log");
} catch (FileNotFoundException e) {
Log.e(TAG, "Exception initializing SecuLog.", e);
}
}
This did not work, because Android is designed to run multiple dalvik-VMs that all try to access the given file. So i need some kind of non-blocking File I/O.
Is there a way to reuse any logging mechanism from Android without the need to explicitly redirect logcat output?
How else can I achieve a simple file logging mechanism, that can be called from every other process? Should I implement a logging Service? Does this service has to be a bound service or a started service? Do I have to use AIDL?
After following the comments to my question I chose the following solution:
created multiple log files
one file for each process
used processId as suffix for log files
designed a log viewing app, that puts all logs togheter
For my logging class I used the following code:
static {
try {
File file = new File("data/secu" + android.os.Process.myPid() + ".log");
file.createNewFile();
file.setReadable(true, false);
file.setExecutable(true, false);
writer = new PrintWriter(file);
} catch (IOException e) {
Log.e(TAG, "Exception initializing SecuLog.", e);
}
}
This post is quite old by now. But I recently did this work and want to share it.
Suggestions are welcome.
Multiple libs are available for this purpose, but if you want to do it yourself, here you go.
fun log(tag: String?, message: String) {
try {
val direct = File("${Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)}/$DIRECTORY_NAME")
if (!direct.exists()) {
direct.mkdir()
}
val fileNameTimeStamp = "${SimpleDateFormat("dd-MM-yyyy", Locale.getDefault()).format(Date())}"
val logTimeStamp = SimpleDateFormat("E MMM dd yyyy 'at' hh:mm:ss:SSS aaa", Locale.getDefault()).format(Date())
val fileName = "$fileNameTimeStamp.html"
val file = File("${Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)}/$DIRECTORY_NAME" + File.separator + fileName)
file.createNewFile()
if (file.exists()) {
val fileOutputStream = FileOutputStream(file, true)
//Here I have added a html tag to beautify/highlight the output in file.
fileOutputStream.write("<p style=\"background:lightgray;\"><strong style=\"background:lightblue;\">  $logTimeStamp :  </strong>  $message</p>".toByteArray())
fileOutputStream.close()
}
} catch (e: Exception) {
Log.e(TAG, "Error while logging into file : $e")
}
}
The purpose of keeping it html file is to open it browser and could highlight different items. Becuase log searching and debugging is very boring and beautifying could reduce the mental stress.
Output file looks like:
Related
Is there a way to see Android logs that were logged before connecting to the Android Studio?
I have an app that tracks GPS location. The issue is that it terminates after some time and I get the Android system message that says "Application Terminated"
I want to see what went wrong and where. When I connect Android studio later, it shows logs that happened from time it is connected.
I want the logs from past.
There are couple of things you can try to resolve:
1) I generally write logs when I have such cases to test.
public static void writeToFile(String msg) {
Log.d("MyApp", msg);
try {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
FileOutputStream dio = null;
File file = new File(Environment.getExternalStorageDirectory(), "MyApp_File_Logs.txt");
if (file != null) {
try {
dio = new FileOutputStream(file, true);
dio.write((msg+"\n").getBytes());
dio.close();
dio = null;
} catch (Exception e) {
e.printStackTrace();
}
}
}
} catch (Exception e) {
Log.e("MyApp", "Exception in writeToFile(): " + e.getMessage());
}
}
So instead of Log.d , Use this Utility method. You can add time in the message to be more precise.
Done forget to give write external storage permission to you app
2) Once you see crash , go to command prompt
and use the below commands:
adb shell
logcat -b crash
You might see something in this, if you connect within a minute or so
3) Increase the "Logger Buffer Size " in your developer options , but in this case you will see delay in getting logs when you connect your device to adb and open logcat.
you can add a log module into your app,let log module output your app's log into local file,like logger
I have an android application that collects data from a sensor via Bluetooth.
When trying to save the data to a .csv-file on the device, the data.csv-file gets created but no text is saved in the file.
The function in question:
private void writeData(boolean writeError) {
try {
File traceFile = new File(Environment.getExternalStorageDirectory(), writeError ? "error.csv" : "data.csv");
if (!traceFile.exists())
traceFile.createNewFile();
BufferedWriter writer = new BufferedWriter(new FileWriter(traceFile, true /*append*/));
writer.write("Test string");
} catch (Exception e) {
e.printStackTrace();
}
}
No error is thrown and I've made sure that each part of the code gets executed. Any ideas as to why this doesn't work?
Solution by Hurundi V. Bakshi
Added
writer.flush();
after
writer.write("Test string");
Documentation on flush():
Flushes this writer. Implementations of this method should ensure that all buffered characters are written to the target.
I am currently writing an android app that logs the accelerometer. (its a test app at the moment so i can prototype an algorithm.
To write out a list of SensorEventStore's (which is just a way of storing the data from a SensorEvent) to the SD card from a 30 minute recording, locks up the GUI for about 20 - 30 seconds while writing the file.
I am using the following code to write out the file to the SD card.
#Override
public void onMessage(Messages message, Object param[]) {
if(message == IDigest.Messages.SaveData) {
File folder = (File) param[0];
File accFileAll = new File(folder, startTime + "_all.acc");
FileWriter accFileWriterAll;
try {
accFileWriterAll = new FileWriter(accFileAll);
} catch (IOException e) {
accFileWriterAll = null;
}
for(Iterator<SensorEventStore> i=eventList.iterator(); i.hasNext();) {
SensorEventStore e = i.next();
if(accFileWriterAll != null) {
try {
accFileWriterAll.write(
String.format(
"%d,%d,%f,%f,%f\r\n",
e.timestamp,
e.accuracy,
e.values[0],
e.values[1],
e.values[2]
)
);
accFileWriterAll.flush();
} catch (IOException ex) {
}
}
}
new SingleMediaScanner(RunBuddyApplication.Context, accFileAll);
}
}
Can anyone give me any pointers to make this not lock up the UI, or not have to take the amount of time it currently takes to write out the file.
Firstly you should try to do this in the background. The AsyncTask is fairly well suited for the task.
Other than that, you should remove the flush() statement, and probperly close() your file writer. The flush causes the data to be written to disk in rather small portions, which is really slow. If you leave the filewriter to its own flushing, it will determine a buffer size on its own. When you properly close the FileWriter, the remaining data should be written to disk as well.
Also, you could take a look at "Try with resources" for your filewriter, but that is optional.
I'm trying to set up a log handler to output the Android log to file to external storage. The code below creates the log file, but no output is sent to the file, so something is obviously wrong with how the handler is configured. Or, perhaps this arrangement cannot be expected to work at all?
The function is called in onCreate() from the main activity.
private void logToFile(String path) {
try {
// Get package name
String packageName = MainActivity.class.getPackage().getName();
String logfileName = path + "/" + packageName + ".log";
Logger logger = Logger.getLogger(packageName);
logger.setLevel(Level.FINE);
FileHandler fileTxt = new FileHandler(logfileName);
SimpleFormatter formatterTxt = new SimpleFormatter();
fileTxt.setFormatter(formatterTxt);
logger.addHandler(fileTxt);
Toast.makeText(this, "Logging to " + logfileName, Toast.LENGTH_LONG).show();
} catch (IOException e) {
Log.d(TAG, e.getMessage());
}
Log.i(TAG, "logging to filesystem enabled");
}
To write to the logger declared above (and, thus, the attached handler which writes to a file), the following should be used instead of Log.i(TAG, "message")
private static final Logger logger = Logger.getLogger(TAG);
public void someFunction() {
logger.info("message")
}
These log messages will also appear in logCat/debugger, with the supplied TAG.
P.S. Java logging makes my head hurt...
I was frustrated at having to use Logger instead of standard Logcat Log.d(), Log.e(), etc. so I started using this Frankenstein's monster solution of reading from Logcat into a LogRecord and saving that using FileHandler.
This means you can limit the log file size easily, and retain your detailed Android logs.
But this isn't going to give you continuous output to file. If you don't mind pressing a button or calling it once a session though, then it shouldn't really matter since Logcat is constantly updated anyway.
(I strongly recommend calling from a non-UI thread.)
FileHandler fh=null;
String name;
if ( 0 == Environment.getExternalStorageState().compareTo(Environment.MEDIA_MOUNTED))
name = Environment.getExternalStorageDirectory().getAbsolutePath();
else
name = Environment.getDataDirectory().getAbsolutePath();
name += "/yourapp/yourapp";
try {
fh = new FileHandler(name, 1024*1024, 7, true); //Limit to 7 x 1MB files.
fh.setFormatter(new SimpleFormatter());
//Try to read Logcat.
try {
//Dumps the entire logcat to std output.
Process processD = Runtime.getRuntime().exec("logcat -v long -d");
BufferedReader bufferedReaderD = new BufferedReader(new InputStreamReader(processD.getInputStream()));
String lineD;
while ((lineD = bufferedReaderD.readLine()) != null){
//Send to the file handler.
fh.publish(new LogRecord(Level.ALL, lineD));
}
//Clear the logcat storage. Don't feel like rewriting old records.
Process processC = Runtime.getRuntime().exec("logcat -c");
} catch (IOException e) {
Log.e(TAG, "Could not get Logcat logs.");
e.printStackTrace();
}
} catch (Exception e) {
Log.e("MyLog", "FileHandler exception", e);
} finally {
if (fh != null)
fh.close();
}
Following suggestions on this site, I have adopted SimpleXML from org.simpleframework.xml.
I use this code to deserialize my class from a file on disk:
try {
myPoints = serial.read(Points.class, new File(getFilesDir(), "points.xml"));
Log.i(TAG, "Number of Points: " + myPoints.getSize());
} catch (FileNotFoundException e) {
Log.d(TAG, "No data found!");
} catch (Exception e) {
Log.d(TAG, "Uncaught exception: ", e.getMessage());
}
In the event the contents of file "points.xml" is not legal xml (in my case it's an empty file), serial.read breaks (an exception occurs in Persister.class, sorry I don't have simplexml sources...).
Should I check for xml consistency beforehand?
Can anybody help?
No need to validate before-hand since you won't be able to fix the problem. Just make sure it fails gracefully (as your code appears to be doing).
You may, however, want to see if the file is empty or not in the case of a deserialization error. An empty file is likely not a problem where as a malformed XML file is!