Use this simple script to find exceptions in Android logs
Logcat logs can provide a great insight into Android device behavior and running processes, however, in certain occasions the size of the output can be enormous. Let’s build a useful script that could help us with scanning the log and highlight any errors/crashes. It will perform the following tasks:
- Capture logs from a device
- Analyze output line by line
- Extract any exceptions into a separate file
1. Capturing the logs
This task is fairly simple. We want to get the logs from a device to a file stored on a computer so it can analyze further in future.
adb logcat -v time -d > ./logcat_captured.txt
-v time -> will add timestamp to logs
-d -> will dump current content of logs (without it logs would be constantly streamed to file)
> ./logcat_captured.txt -> this will stream logs to a file in our current working directory
2. Analyzing logs
Once the logs are captured its time to analyze its content. This will be performed the same way Google search engine works, by using keywords.
a. Scanning through lines
First, let’s assess the size of the file and assign this value to a variable:
log_size=$(cat ./logcat_captured.txt | wc -l)
Now when the size is known we can start scanning through its content (size = number of lines is required to know when to stop). Simple ‘while’ is sufficient enough:
start_log=1 while [ $start_log -le $log_size ] ; do
At the beginning lets assign the content of the line to variables. With each iteration of the loop, one line will be assigned, its content verified against keywords (exception types) and based on the outcome the choice will be made if it’s a beginning of exception stacktrace or not.
$start_log -> this variable will be incremented with each iteration.
current_line=$(head -$start_log ./logcat_captured.txt | tail -1)
Now let verify the content of the currently selected log line against the keywords. I have picked the most common exceptions (but surely not all, feel free to expand the list):
is_filenotfound=$(echo "$current_line" | grep -i "FileNotFoundException" | wc -l) is_handlingexception=$(echo "$current_line" | grep -i "Handling exception" | wc -l) is_nullpointer=$(echo "$current_line" | grep -i "NullPointerException" | wc -l) is_runtimeexception=$(echo "$current_line" | grep -i "RuntimeException" | wc -l) is_securityexception=$(echo "$current_line" | grep -i "SecurityException" | wc -l) is_illegalstateexception=$(echo "$current_line" | grep -i "IllegalStateException" | wc -l) is_transactiontolarge=$(echo "$current_line" | grep -i "TransactionTooLargeException" | wc -l) is_classcast=$(echo "$current_line" | grep -i "ClassCastException" | wc -l) is_remote=$(echo "$current_line" | grep -i "RemoteException" | wc -l) is_deadobject=$(echo "$current_line" | grep -i "DeadObjectException" | wc -l) is_connectionleaked=$(echo "$current_line" | grep -i "ServiceConnectionLeaked" | wc -l)
b. Error found
Now let put a condition that verifies the outcome of the key-word check (the more checks the longer it gets):
if [[ $is_fatal -gt 0 || $is_filenotfound -gt 0 || $is_handlingexception -gt 0 || $is_nullpointer -gt 0 || $is_runtimeexception -gt 0 || $is_securityexception -gt 0 || $is_illegalstateexception -gt 0 || $is_transactiontolarge -gt 0 || $is_classcast -gt 0 || $is_remote -gt 0 || $is_deadobject -gt 0 || $is_connectionleaked -gt 0 ]] ; then
For our convenience let print a message in case an error was found:
echo "An error was found at line: $start_log"
We will give extracted error stacktraces timestamp name, so let create a new variable and assign current time value (in seconds):
This way each of the files will be unique (we avoid overwriting the same file). Error files will be kept in a separate folder for clarity.
mkdir -p ./stacktrace
3. Extracting error logs
As log check is being triggered by a keyword that is a part of an exception the ‘current’ line is, in fact, a first line of stacktrace, therefore its content will be redirected to file:
echo "$current_log_line" > ./stacktrace/"$stacktrace_name"_stacktrace.txt
Now we will need another loop to redirect rest of the error exception to the same file:
error_search=1 while [ $still_search -eq 1 ] ; do
$error_search -> this will be our condition check, it will ensure that the loop breaks when next line is no longer part of error output.
We need to increment current line and check it for the ‘special’ condition:
let start_log=start_log+1 current_line=$(head -$start_log ./logcat_captured.txt | tail -1)
This time, however, we are looking for a different keyword – ‘at ‘ (which should be stacktrace marker). Let’s create a new variable and assess if the keyword is there:
is_stack=$(echo “$current_log_line” | grep -i “at ” | wc -l)
Simple condition to verify if the criteria are met. If a result is a positive line will be added to error stacktrace if not the loop will be stopped:
if [ $is_stack -eq 1 ] ; then # Condition met, line is being added to stacktrace echo "$current_log_line" >> ./stacktrace/"$stacktrace_name"_stacktrace.txt else # Condition not met, loop will be broken as a result of changing condition variable still_search=0 fi
Now the only thing left are finishing touches 🙂
done fi let start_log=start_log+1 done