extracting-logs-with-logcat について
Android開発者向けの `adb logcat` ガイドです。ログのストリーミング、フィルタリング、形式設定、保存に加え、CI失敗時の取得やリリースビルドからのデバッグログ除去を扱います。
主な機能
- 01ログのストリーミングとダンプ
- 02バッファ選択とログの絞り込み
- 03形式・時刻・PIDによるフィルタリング
- 04ログファイルへの出力とローテーション
- 05R8ルールによるデバッグログ除去
活用シーン
- 01CIテスト失敗時のデバイスログ保存
- 02アプリやシステムコンポーネントのログ絞り込み
- 03構造化JSONログの取得
- 04プロセス再起動後もログを追跡
- 05リリースビルドからの `Log.d` 呼び出し除去
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ skydoves/ android-testing-skills --skill extracting-logs-with-logcatインストール後、Claude Code で/extracting-logs-with-logcatを入力して読み込まれたか確認します。
詳細
logcat でログを抽出する — デバイスログの読み取り
このスキルでは、adb logcat の全般を扱います。ストリーミングとダンプ、バッファの選択、フィルター式、フォーマットフラグ、PID と時刻によるフィルタリング、ファイルのローテーション、リリースビルドで Log.d を取り除く R8 ルールについて説明します。CI で失敗時にログを取得する方法は、../../automation/scripting-adb-for-ci/SKILL.md にあります。
このスキルを使う場面
- CI でテストが失敗し、開発者がデバイスログをアーティファクトアーカイブにダンプする必要がある場合。
- 開発者の
adb logcat | grep MyAppが SSH セッションを圧迫している場合 — logcat 側のフィルターで解決できます。 - シナリオがデフォルトの
mainではなく、system_serverまたはcrashバッファ内でのみ再現する場合。 - 開発者が、機械処理用の構造化ログ(
-v json)を必要としている場合。 Log.d("Sensitive", "...")の呼び出しがリリースビルドに残っているように見える場合 — R8 の-assumenosideeffectsルールが不足しています。- Doze/ANR によってアプリが終了した後にログが消える場合 —
--pidと PID の再解決ループが必要です。
このスキルを使わない場面
- スクリーンショットまたは動画を撮影する場合。
../../capture/capturing-screenshots-and-screenrecord/SKILL.mdを使用してください。 - デバイスから一般的なファイルを取得する場合。
../../transfer/extracting-test-artifacts/SKILL.mdを使用してください。 - リトライ、ポートフォワーディング、複数デバイスへの並列展開など、スクリプト全体を扱う場合。
../../automation/scripting-adb-for-ci/SKILL.mdを使用してください。 - ジェスチャーの実行や設定の変更を行う場合。
../../control/injecting-input-and-state/SKILL.mdを使用してください。
前提条件
adb get-stateがdeviceを返すこと。../../devices/connecting-to-devices/SKILL.mdを参照してください。-v jsonを使用する場合:デバイス API 30 以降(Android 11 以降)。曖昧さのないタイムスタンプにするには、-v UTC -v yearと組み合わせてください。- PID ごとにフィルタリングする場合:
adb shell pidof -s <pkg>には API 24 以降が必要です(toybox のpidof -s)。古いリリースではadb shell ps -A | grep <pkg>を使用してください。
3 つの基本操作
| コマンド | 動作 |
|---|---|
adb logcat | デバイスログを stdout にストリーミングします。中断するまで実行されます。 |
adb logcat -d | 「ログをダンプして終了します。」スナップショットモードで、CI に最適です。 |
adb logcat -c | 「ログバッファをクリアします。」バグを再現する前に実行してください。 |
(developer.android.com/tools/logcat からの引用そのまま。)
CI での標準的なログ取得パターン:
adb logcat -c # clear before scenario
./run-scenario.sh # reproduce
adb logcat -d > artifacts/log.txt # dump after
バッファの選択
-b <name> は、読み取るカーネル/ユーザー空間のリングバッファを選択します。バッファ(developer.android.com/tools/logcat):
| バッファ | 内容 |
|---|---|
main | デフォルトのアプリ側バッファ。システム/クラッシュの情報は含まれません。 |
system | フレームワーク/system_server のメッセージ。 |
crash | Tombstone と未処理例外の出力。 |
events | 構造化されたバイナリ形式のシステムイベントバッファ。タグ名をデコードするには -v descriptive と組み合わせます。 |
radio | 無線/テレフォニー関連のメッセージ。 |
kernel | カーネルバッファ。 |
all | すべてのバッファ。 |
default | 暗黙のセット:main、system、crash。 |
複数の -b フラグを指定する方法と、カンマ区切りのリストを指定する方法のどちらも使用できます。
adb logcat -b radio
adb logcat -b main -b radio -b events
adb logcat -b main,radio,events
events バッファには、フレームワークが計測用に出力する am_*、wm_*、input_focus、notification_* があります。人が読むためのものではありません。タグ名をデコードするには、次のようにします。
adb logcat -b events -v descriptive
優先度の段階とフィルター式
フィルター指定は、スペース区切りの tag:priority ペアです。* はすべてのタグに一致します。
| 文字 | 意味 |
|---|---|
V | 詳細 |
D | デバッグ |
I | 情報 |
W | 警告 |
E | エラー |
F | 致命的 |
S | 「サイレント(最も高い優先度で、何も出力されません)」 |
*:S を設定すると すべてのログが無効になり、それより前に指定された tag:P は、そのタグの優先度 P 以上のログを再び有効にします。「自分のアプリのログだけを表示する」ための標準的な書き方:
adb logcat ActivityManager:I MyApp:D *:S
「ActivityManager(Info 以上)と MyApp(Debug 以上)を除くすべてのログを抑制します」— developer.android.com/tools/logcat。
その他の定番:
adb logcat *:W # warnings and above, all tags
adb logcat *:E # errors and above (very common in CI)
カレントディレクトリに S という名前のファイルがある場合、zsh/bash は引用符で囲まれていない *:S を glob 展開します。スクリプトでは引用符で囲んでください。
adb logcat "ActivityManager:I MyApp:D *:S"
同じフィルターを、環境変数でホストのデフォルトとして設定できます。
export ANDROID_LOG_TAGS="ActivityManager:I MyApp:D *:S"
フォーマットフラグ — -v <format>
| フォーマット | 表示内容 |
|---|---|
brief | 「優先度、タグ、PIDを表示します」 |
long | 「すべてのメタデータフィールドを、メッセージ間に空行を挟んで表示します」 |
process | 「PIDのみ」 |
raw | 「メタデータなしの生ログメッセージ」 |
tag | 「優先度とタグのみ」 |
thread | 「優先度、PID、TIDを表示する旧形式」 |
threadtime | デフォルト。「日付、時刻、優先度、タグ、PID、TID」 |
time | 「日付、時刻、優先度、タグ、PID」 |
フォーマットの修飾子は -v と組み合わせられます(カンマで連結するか、繰り返し指定できます)。
| 修飾子 | 効果 |
|---|---|
color | 優先度ごとに色を付けます。 |
descriptive | イベントログのタグ名をデコードします。 |
epoch | 1970-01-01からの経過秒数を表示します。 |
monotonic | 前回の起動からのCPU秒数を表示します。 |
printable | バイナリコンテンツをエスケープします。 |
uid | ログを記録したプロセスのUIDまたはAndroid IDを表示します。 |
usec | マイクロ秒精度の時刻を表示します。 |
UTC | 時刻をUTCで表示します。 |
year | 表示される時刻に年を追加します。 |
zone | 現地のタイムゾーンを追加します。 |
adb logcat -v json -v UTC -v year # Android 11+; structured stream, unambiguous time
adb logcat -b all -v color -d # color dump, all buffers
デフォルト行の例(developer.android.com/tools/logcat):
I/ActivityManager( 585): Starting activity: Intent { action=android.intent.action.MAIN ... }
brief のスキーマは <priority>/<tag>(<PID>): <message> です。threadtime では日付、時刻、TIDが追加されます。
時刻とPIDによるフィルタリング
-T '<date> <time>'— 指定したタイムスタンプ以降で最初のエントリーから開始します。形式はthreadtimeと同じです。例:'01-01 12:00:00.000'。-T <count>— バッファーから最後のN件のエントリーを出力してから、ストリーミングを続けます。-t <count>— 最後のN件のエントリーを出力して、その後終了します(つまり、-tは自動終了する-Tです)。--pid <pid>— 単一のプロセスからのエントリーのみを出力します。pidofと組み合わせます。
adb logcat --pid=$(adb shell pidof -s com.example.app)
pidof -s は単一のpidを返します。-s を指定しない場合は、スペース区切りのリストが返されますが、--pid はこれを直接受け付けません。アプリが強制終了されて再起動する可能性がある場合(Doze、ANR、クラッシュ)は、PIDを再解決するループで囲みます。
while true; do
PID=$(adb shell pidof -s com.example.app 2>/dev/null)
[ -n "$PID" ] && adb logcat --pid="$PID"
sleep 1
done
バッファーのローテーションと永続化
長時間のキャプチャでは、ローテーションを設定してファイルに保存します。
| フラグ | 意味 |
|---|---|
-f <file> | 標準出力の代わりに <file> に書き込みます。注意点: adb logcat -f の場合、ファイルはホスト側に作成されます。adb shell logcat -f の場合、パスはデバイス側です。 |
-r <kbytes> | Nキロバイトごとに出力をローテーションします。-f が必要です。 |
-n <count> | ローテーションしたファイル(logfile、logfile.1、...)を最大 <count> 個保持します。 |
-G <size> | 選択した -b リングのカーネルログバッファーのサイズを変更します(例:1M、16M)。logd が稼働している間は設定が保持されます。 |
-g | バッファーサイズと現在の使用量を出力して終了します。「ログが欠落しているのはなぜ?」という疑問を解消するための、手軽な診断方法です。 |
adb logcat -G 16M # grow the main buffer
adb logcat -g # inspect sizes
adb logcat -b main -f /data/local/tmp/app.log \
-r 1024 -n 8 # 8x ~1MB rotated files (device-side)
adb shell logcat -b main -f /data/local/tmp/app.log \
-r 1024 -n 8 # same, but the device runs it
-G によるサイズ変更はそのバッファーに対してグローバルに適用され、再起動後も(再起動するまで)保持されます。メモリに制約のあるデバイスで16 MBに設定したまま忘れないでください。CIファームを共有している場合は、テストスイートの実行後に -G 256K でメモリを解放してください。
リリース時に Log.d を除去する — R8ルール
コード縮小によって除去されない限り、android.util.Log の呼び出しはリリースAPKにも残ります。proguard-rules.pro で使う標準的な方法は次のとおりです(R8/ProGuard)。
-assumenosideeffects class android.util.Log {
public static *** v(...);
public static *** d(...);
public static *** i(...);
public static boolean isLoggable(java.lang.String, int);
}
-assumenosideeffects を使うと、R8は指定したメソッドを副作用のない処理として扱うため、すべての呼び出し(および定数の場合は文字列ビルダーの引数式も)が除去されます。本番環境の診断用に w、e、wtf は残してください。Timberの場合は、BuildConfig.DEBUG で制御される DebugTree の登録を除去し、警告やエラーをCrashlyticsに転送する ReleaseTree を使用します。
Composeでは、kotlin.jvm.internal.Intrinsics に対して -assumenosideeffects を指定しないでください。コンパイラーが依存しているnullチェックが除去されます。
パターン
パターン:誤り — フィルタリングに adb logcat | grep MyApp を使う
# WRONG
adb logcat | grep MyApp
# WRONG because: the device sends every log line over the USB/TCP transport, then the host
# discards 99% via grep. On a busy device this saturates the transport and the grep can lag
# behind real time. Worse, `grep` runs without -F, so a tag that contains regex metacharacters
# (`.`, `(`, `+`) silently mismatches.
# RIGHT — filter at the logcat level
adb logcat MyApp:D *:S
デバイスが出力するのは一致する行のみで、転送経路は混雑しません。
パターン:誤り — -T <count> で終了すると想定する
# WRONG
adb logcat -T 100 > tail.txt
# Hangs forever — -T <count> prints the last 100 lines THEN CONTINUES STREAMING.
# RIGHT — `-t` is `-T` with auto-exit
adb logcat -t 100 > tail.txt
パターン:機械処理向けの構造化ログ
adb logcat -v json -v UTC -v year -d -b all > run.jsonl
各行は単一のJSONオブジェクトです。jq に渡して処理します。
jq -c 'select(.priority == "ERROR")' run.jsonl
(-v json を使うには、デバイスがAndroid 11以降である必要があります。)
パターン:失敗時のキャプチャ — クリア、実行、ダンプ
adb logcat -c
./run-scenario.sh
RC=$?
if [ "$RC" -ne 0 ]; then
adb logcat -d -v threadtime > artifacts/logcat.txt
adb logcat -d -b crash -v threadtime > artifacts/crash.txt
adb logcat -d -b events -v descriptive > artifacts/events.txt
fi
exit "$RC"
-c は実行前にバッファーをクリアするため、ダンプにはそのシナリオのログのみが含まれます。
パターン:プロセス再起動後も維持されるPIDバインドストリーム
while true; do
PID=$(adb shell pidof -s com.example.app 2>/dev/null)
if [ -n "$PID" ]; then
adb logcat --pid="$PID" -v threadtime
fi
sleep 1
done
外側のループは、Doze/ANR/クラッシュによってアプリが強制終了され、再起動された後にPIDを再取得します。
パターン:ホスト側の-fとデバイス側の-f
# host-side rotation (file lives on the developer machine)
adb logcat -b main -f ~/logs/app.log -r 1024 -n 8
# device-side rotation (file lives in /data/local/tmp on the device)
adb shell logcat -b main -f /data/local/tmp/app.log -r 1024 -n 8
adb pull /data/local/tmp/app.log ~/logs/app.log # later
必須ルール
- 必須: ホスト側の
grepではなく、logcatレベルでフィルタリングします(例:MyApp:D *:S)。デバイス上でフィルタリングすることで、転送が飽和するのを防げます。 - 必須:
*を含むフィルター文字列は、zsh/bashでは引用符で囲みます(例:"MyApp:D *:S")。カレントディレクトリにSという名前のファイルがあると、グロブ展開によってアスタリスクが処理されてしまいます。 - 必須: 「末尾を取得して終了」するには
-t <count>を使います。-T <count>は初回ダンプ後もストリーミングを続けます。 - 必須: CIでスナップショットをダンプするには
-dを使います。ストリーミングするadb logcatは、明示的に終了させる必要があります。 - 必須: 添付するログダンプを取得したいバグを再現する前に、
adb logcat -cを実行します。そうしないと、ダンプに関係のない過去の履歴が含まれます。 - 必須:
adb logcat -f <file>(ホスト側のパス)とadb shell logcat -f <file>(デバイス側のパス)を区別します。シェル形式では動作が異なります。 - 必須: 以前に機密データを含む
Log.dを呼び出していたAndroidリリースAPKを配布する場合は、android.util.Logのv/d/i(およびisLoggable)に対するR8の-assumenosideeffectsルールを追加します。w/e/wtfは残してください。 - 禁止:
kotlin.jvm.internal.Intrinsicsに-assumenosideeffectsを設定してはいけません。Kotlinコンパイラーが依存するnullチェックのセマンティクスが失われます。 - 禁止: Android 11より古いデバイスで
-v jsonに依存してはいけません。空のストリームが暗黙的に出力されます。 - 推奨: CI成果物で曖昧さのない絶対タイムスタンプを得るには、
-v threadtime,year,UTC(または-v threadtime -v year -v UTC)を重ねて指定します。 - 推奨: 長時間かけて進行するバグを記録する場合は、
mainバッファに-G 16Mを指定し、終了処理でデフォルトに戻します。
検証
-
scripts/または.github/workflows/内のスクリプトで、フィルタリングのためにadb logcatを直接grepに渡していません。すべてのフィルターはlogcatの引数として渡されています。 - CIの失敗時キャプチャのダンプには、少なくとも
main、crash、eventsバッファが含まれています。 - リリースAPKは
Log.dの呼び出しを出力しません(apkanalyzer dex packages app-release.apk | grep 'android.util.Log'を実行し、e/w/wtfのみが返ることを確認します)。 - シェルスクリプト内の
*:Sを含むフィルター文字列は引用符で囲まれています。 - すべての
--pid呼び出しでpidof -s <pkg>(単数形)を使用し、アプリが再起動する可能性がある場合はループ内でPIDを再取得しています。 - CIで長時間実行されるすべての
adb logcatセッションに実時間の上限が設定されています(timeout 600s adb logcat ...)。これにより、セッションが停止してディスクを使い果たすのを防げます。
参考資料
- developer.android.com/tools/logcat — バッファーの表、優先度の階層、フィルター式、
-v threadtime/json/descriptive、フォーマット修飾子、-T/-t/--pid/-r/-n/-G/-gフラグ、行フォーマットのスキーマ。 - developer.android.com/tools/adb — ホスト側の簡略表記である
adb logcatとadb shell logcat。 - developer.android.com/studio/build/shrink-code — R8 の
-assumenosideeffectsとandroid.util.Logの削除ルール。 - developer.android.com/reference/android/util/Log —
Log.v/d/i/w/e/wtf/isLoggableのシグネチャ。 - 調査メモ
tasks/research/A3-adb-observability-automation.md— logcat の全機能、-fのホスト側とデバイス側の挙動の違い、R8 の keep ルールの根拠、失敗時に CI でキャプチャするパターン。 - 関連スキル:
../../architecture/understanding-adb-architecture/SKILL.md— サーバー/デーモン、トランスポート。 - 関連スキル:
../../devices/connecting-to-devices/SKILL.md— デバイスの状態、wait-for-device。 - 関連スキル:
../../devices/connecting-over-wifi/SKILL.md—adb pair/adb connect。 - 関連スキル:
../../apps/installing-and-managing-apps/SKILL.md— ログをキャプチャする APK のインストール。 - 関連スキル:
../../tests/running-instrumented-tests-via-adb/SKILL.md—am instrument -w -rと logcat の連携。 - 関連スキル:
../../control/injecting-input-and-state/SKILL.md— logcat に結果が表示されるジェスチャーの実行。 - 関連スキル:
../../capture/capturing-screenshots-and-screenrecord/SKILL.md— スクリーンショットの併用キャプチャ。 - 関連スキル:
../../transfer/extracting-test-artifacts/SKILL.md— ローテーションされたログファイルをデバイスから取得する方法。 - 関連スキル:
../../automation/scripting-adb-for-ci/SKILL.md— CI で失敗時にキャプチャする方法、リトライ、クリーンアップの全パターン。 - セット間の関連資料:
../../../instrumentation/runner/running-instrumented-tests-with-androidjunit4/SKILL.md— logcat に混在するインストルメンテーションログ。 - セット間の関連資料:
../../../instrumentation/scenarios/launching-activities-with-activityscenario/SKILL.md—eventsバッファーに表示される Activity のライフサイクルイベント。 - セット間の関連資料:
../../../fundamentals/strategies/applying-testing-strategies/SKILL.md— ログキャプチャが適切な可観測性レイヤーとなるケース。