How logcat filtering actually works
adb logcat's filter syntax confuses most people because it works backwards
from how most filter systems do. Most filters are "show nothing by default, add what you
want." Logcat is "show everything by default, then quiet down what you don't want." Once
that flip clicks, the rest of the syntax falls into place.
Priority levels and the cascade
Every log entry has a priority. Lowest to highest: V (verbose),
D (debug), I (info), W (warn), E
(error), F (fatal). There's also S for silent. S
isn't a real log priority. It's a meta-value meaning "show nothing for this tag."
When you write a filter like MyTag:D, you're saying "show MyTag at
DEBUG or higher." The priority cascades upward. D includes D, I,
W, E, F. W includes W, E, F. S includes nothing. So
*:E shows only errors and fatals across every tag, which is typically what
you want when hunting for crashes.
The "silence everything else" pattern
Here's the first real gotcha. You might run adb logcat MyTag:V expecting to
see only MyTag's logs. You'll see all logs, plus MyTag at verbose. That's
because the default priority is verbose. Your MyTag:V filter just
also enables MyTag explicitly, which it already was.
The fix is to add *:S at the end: adb logcat MyTag:V *:S. The
* is the "all other tags" wildcard. Setting it to S silences
everything except what you've explicitly enabled. Now you see only MyTag's logs, at
verbose or higher.
Order matters. *:S goes at the end. Earlier filters override later
ones for the same tag, but * is the catchall, and it gets evaluated last
after all the specific tag filters have been applied.
The -s shortcut
The silence-everything-else pattern is so common that logcat has a shortcut for it:
adb logcat -s MyTag MyOtherTag. The -s flag means "silent
default, then enable all subsequent tags at verbose." That command is equivalent to
adb logcat MyTag:V MyOtherTag:V *:S. The Build Mode above generates the
explicit form because it's easier to read. -s is shorter to type but harder
to scan at a glance.
Filtering by app process with pidof
Often what you actually want is "show only my app's logs, regardless of which tag they use." The way to do that is filter by process ID:
adb logcat --pid=$(adb shell pidof com.example.app)
The $(adb shell pidof X) part is bash command substitution. It runs the
inner command, captures the PID, and substitutes it into the outer command. The whole
thing evaluates to something like adb logcat --pid=12345.
The reason to resolve the PID dynamically rather than hard-coding it is that PIDs change every time the app process restarts. Hard-coded PIDs go stale within minutes. Resolving on every run means the same command keeps working across debug sessions without editing.
On Windows PowerShell the equivalent is (adb shell pidof com.example.app):
parentheses, no dollar sign. On Windows cmd, dynamic substitution is awkward. You have
to run adb shell pidof com.example.app separately and then paste the PID
into the second command. The Build Mode above adjusts the syntax depending on which
shell you select.
Why threadtime matters
logcat's default output format is brief:
D/MyTag(12345): some message. Concise, but missing two crucial fields.
There's no timestamp and no thread ID. For any non-trivial debugging, switch to
threadtime:
05-08 14:23:15.243 12345 12350 D MyTag: some message
Now you have the date, time (with milliseconds), PID, TID (thread ID), priority, tag, and message. Thread IDs are essential for diagnosing concurrency issues. Without them, you can't tell if "X happened after Y" was a single-thread sequence or a race condition between two threads. Default to threadtime. Switch to brief or tag only when you specifically want less noise.
Buffers
Android maintains separate log buffers. By default logcat reads only the
main buffer. There are others worth knowing:
main: the default. App and system logs fromandroid.util.Logcalls.system: Android framework internal messages, separate from app code.crash: app crashes, fatal exceptions, ANRs. Reading from this buffer is faster than grepping main for "AndroidRuntime: FATAL EXCEPTION."events: structured system events. Used by some monitoring tools, but less useful for typical debugging.radio: telephony and radio stack. Almost never useful unless you work on the Android telephony layer specifically.
Read multiple buffers by repeating -b:
adb logcat -b main -b crash.
Things that will trip you up
Tag matching is case-sensitive. OkHttp:V works. okhttp:V just
silently shows nothing, and you spend ten minutes thinking your filter is broken before
realising the SDK uses Pascal case. Hosts in URLs are case-insensitive on the JVM, but
logcat tags are not. Copy-paste the tag from the library's source rather than typing it.
There's no regex support in the built-in filter syntax. The tag filter is exact match.
For pattern matching across the message body, pipe through grep:
adb logcat -v threadtime | grep -i "exception". That's fine for interactive
use but inconvenient for scripted log capture, where you may want to use
adb logcat -d (dump) and process the output with whatever pattern matching
your tooling supports.
The log buffer can be clogged with hours of stale output if you don't clear it first.
Run adb logcat -c before reproducing your bug. Otherwise you're scrolling
through unrelated lines looking for the moment your repro fired. The clear is fast and
non-destructive. You're not losing anything you'd want.
Multiple connected devices need -s, but in that position -s
means something different from the one earlier in this article.
adb -s <serial> logcat ... targets a specific device by its serial
number (from adb devices). The -s after logcat
means "silent default." Same letter, different meaning, different position. Yes, this
is annoying.
The last one is more advice than gotcha. Don't run adb logcat without any
filters on a noisy device. A modern Android phone produces hundreds of log lines per
second from system services running in the background. Bluetooth scanning, location
updates, framework chatter, the works. Without a filter, ten seconds of "the part you
actually care about" gets buried in ten thousand lines of noise.
Patterns I actually reach for
Hunting a crash: adb logcat -b crash *:E. The dedicated crash buffer is
much faster to scan than grepping the main buffer for "FATAL EXCEPTION," and the
*:E filter trims anything below ERROR. Good first stop when something's
gone wrong and you don't yet have a hypothesis.
Verifying a feature works:
adb logcat MyFeatureTag:V *:S -v threadtime. Just the tag I'm working on,
at verbose, with timestamps. The threadtime format is what lets me verify ordering
between events. Without it, you can't tell which event happened first when two of them
fire in the same second.
Debugging a third-party SDK like OkHttp or Firebase Analytics:
adb logcat OkHttp:V FA:V *:S. Multiple tags allow-listed, everything else
silenced. The tag names are case-sensitive (again) so check the SDK's documentation or
source for the exact spelling.
Filing a bug report: adb logcat -d -v threadtime > bug-report.txt. The
-d flag dumps the current buffer and exits, so you get a finite file
rather than a live stream. Pair it with adb bugreport if you need the full
system snapshot for an OEM-specific bug.
Watching my app's process holistically:
adb logcat --pid=$(adb shell pidof com.example.app) -v threadtime. Every
log line from my process regardless of which tag it uses. Best for verifying that an
end-to-end flow does what you expect. You see your code logging, the framework
logging, third-party libraries logging, all interleaved in real time.