Logcat and crashes
When an app crashes, freezes or does something odd, the reason is almost always written in the phone's log. This lesson shows how to read that log, cut it down to the one app you care about, and find the line in a crash that points at the bug.
What the log is
Android keeps a running log in memory. The system and every app write short messages to it as they work. Logcat is the tool that reads it, and you run it through ADB:
adb logcatLines pour in, newest at the bottom, until you press Ctrl + C. A quiet phone writes many lines a second; that is normal.
Read one line
11-09 14:03:20.871 6120 6120 I ActivityManager: Start proc 6120:com.example.notes/u0a190 for …
└──── time ──────┘ PID TID │ └──── tag ────┘ └──────────────── message ─────────────────┘
priority| Part | In the example | What it is |
|---|---|---|
| Date and time | 11-09 14:03:20.871 | When the line was written, on the phone's clock |
| PID | 6120 | The process ID: a number for the running program that wrote it |
| TID | 6120 | The thread ID: which thread inside that process. Equal to the PID for the app's main thread |
| Priority | I | How important the line is |
| Tag | ActivityManager | A short name for the part of the code that wrote it, chosen by its developer |
| Message | Start proc … | The text itself |
This layout is called threadtime, and it is what adb logcat prints by default on current Android. DroidDeck asks for it by name with adb logcat -v threadtime.
Each app gets a new PID every time it starts. Note it: you will filter on it later.
Priorities
Every line has one of six priorities, from chattiest to most serious:
| Letter | Priority | Typical use |
|---|---|---|
V | Verbose | Everything, in detail. Usually only in development builds |
D | Debug | Information for the developer |
I | Info | Normal events: an app started, a connection opened |
W | Warning | Something unexpected that the code recovered from |
E | Error | Something failed |
F | Fatal | Something failed so badly the process is ending |
A priority filter means "this and everything more serious". Filter at W and you see W, E and F.
Buffers
The log is kept in several separate memory areas, called buffers:
| Buffer | What goes in it |
|---|---|
main | Most app and system messages |
system | Messages from the Android system itself |
crash | Crash reports only |
events | Short coded system events, mostly for tools |
radio | Mobile network and telephony messages |
Plain adb logcat shows main, system and crash together. Pick others with -b, for example adb logcat -b crash for only crashes, or -b all.
Buffers are a fixed size. On a busy phone, old lines are pushed out within minutes, so save a log soon after something goes wrong.
Filter the log
On the command line, a filter is tag:priority. * means every tag, and S (silent) hides it:
adb logcat "*:E" # errors and fatal lines only
adb logcat ActivityManager:I "*:S" # only ActivityManager, Info and up
adb logcat -s NotesSync # only the tag NotesSync, everythingPut *:E in quotes. Without them, your shell may treat * as a file name pattern: a file in the current folder called x:E would quietly replace it.
One app only
Tags are chosen by developers and are not unique, so filtering on a tag often misses lines. The reliable way is the process ID. pidof on the phone finds it, and --pid keeps only that process's lines:
adb shell pidof com.example.notes
adb logcat --pid=6120Or both at once, in a Bash shell:
adb logcat --pid=$(adb shell pidof -s com.example.notes)The catch: when the app restarts, it gets a new PID, and you have to run it again.
The same in DroidDeck
The Logcat tab starts the stream as soon as a device is selected. Its toolbar holds the filters, and they combine: a line must pass all of them.
| Filter | What it does |
|---|---|
| V D I W E F | Shows the chosen priority and everything more serious |
| Tag | Keeps lines whose tag contains your text, ignoring case |
| Contains | Keeps lines whose message contains your text |
| Package | Keeps lines from one app, given its exact package name |
Type com.example.notes into Package and a chip shows the app's pid, or not running. DroidDeck checks again every three seconds, so when the app restarts with a new PID, the filter follows it. That solves the catch above. The details are in Read the device log.
Read a crash
When an app crashes on an uncaught Java or Kotlin error, Android writes the report at priority E under the tag AndroidRuntime, starting with FATAL EXCEPTION. Here is a typical one, trimmed:
E AndroidRuntime: FATAL EXCEPTION: main
E AndroidRuntime: Process: com.example.notes, PID: 6120
E AndroidRuntime: java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.notes/com.example.notes.EditorActivity}: java.lang.NullPointerException: …
E AndroidRuntime: at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:…)
E AndroidRuntime: at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:…)
E AndroidRuntime: …
E AndroidRuntime: Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.lang.String.length()' on a null object reference
E AndroidRuntime: at com.example.notes.EditorActivity.updateCounter(EditorActivity.java:42)
E AndroidRuntime: at com.example.notes.EditorActivity.onCreate(EditorActivity.java:18)
E AndroidRuntime: at android.app.Activity.performCreate(Activity.java:…)
E AndroidRuntime: …The block is a stack trace: the chain of method calls that was running when the error happened, most recent call first. Read it in four steps:
Which app and thread.
FATAL EXCEPTION: mainandProcess: com.example.notessay the app's main thread crashed. The main thread draws the screen, so the app closes.Find the last
Caused by:. The first exception is often a wrapper. Here the system says it could not start a screen,Unable to start activity. The real cause is further down: aNullPointerException, with a message saying code calledlength()on a text value that wasnull, meaning empty, not set.Find the first line in the app's own code under that
Caused by:. Skip lines that start withandroid.,java.orandroidx.; they belong to the system and libraries. The first line incom.example.notesisEditorActivity.updateCounter(EditorActivity.java:42): the fileEditorActivity.java, line 42, insideupdateCounter.Read the line below it to see how the code got there:
onCreatecalledupdateCounterwhile the screen was opening. So a text value was stillnullwhen the screen first appeared.
That is what a developer needs, and what a tester should paste into a bug report: the whole block from FATAL EXCEPTION to the end of the trace, plus the steps that caused it.
Two other kinds of failure look different:
- App not responding. An app that freezes for several seconds gets an
ANR in com.example.noteserror from the tagActivityManager. ANR means Application Not Responding: the main thread was busy and the screen stopped answering. - Native crashes, in code written in C or C++, show
Flines such asFatal signal 11 (SIGSEGV)from the taglibc, followed by a block ofF DEBUGlines.
Save a log
adb logcat -d > notes-crash.txt # what is in the buffers now, then stop
adb logcat -b crash -d # recent crashes only
adb logcat > notes-live.txt # keep saving until Ctrl+C
adb logcat -c # erase the buffers on the phone-d dumps what is there and exits, so it is the one to run straight after a crash. In DroidDeck, Export current view to ~/Downloads saves the lines your filters show, to a droiddeck-logcat-…log file. Set the priority to V and empty the filters first to save everything.
WARNING
adb logcat -c, and DroidDeck's Clear (logcat -c), erase the log on the phone itself, not only on your screen. Lines from before that moment are gone for every tool. Save first.
A clean start helps when you reproduce a bug: save, clear, reproduce, save again. The second file then holds only what matters.
Stay safe
- Logs can hold personal data. Apps sometimes log email addresses, account names, file names, locations, even tokens. Read a log before you attach it to a public bug report, and trim it to the app and the minutes that matter.
- Never log secrets in your own apps. Anything an app writes to logcat can be read by anyone with ADB access to the phone.
- Save before you clear. Clearing cannot be undone.
Check yourself
Question 1 of 6
In logcat you select the priority W. Which lines do you see?
What you learned
- Each logcat line has a time, PID, TID, priority, tag and message.
- Priorities run
V D I W E F; a filter shows that level and above. Buffers keep different kinds of message, and old lines roll out. - Filter one app by its process ID; DroidDeck's Package filter follows the app across restarts.
- In a crash, find the last
Caused by:, then the first line in the app's own code: file and line number. - Save with
adb logcat -d; clearing erases the phone's log for good.
Next steps
- Lesson 5: Permissions and safety
- Read the device log in DroidDeck
- Manage apps: find an app's package name