mcpField
E
extracting-logs-with-logcat

extracting-logs-with-logcat

github.com/skydoves開発33988 回インストール22 週間前に更新
リポジトリ

extracting-logs-with-logcat について

Android開発者向けの `adb logcat` ガイドです。ログのストリーミング、フィルタリング、形式設定、保存に加え、CI失敗時の取得やリリースビルドからのデバッグログ除去を扱います。

主な機能

  1. 01ログのストリーミングとダンプ
  2. 02バッファ選択とログの絞り込み
  3. 03形式・時刻・PIDによるフィルタリング
  4. 04ログファイルへの出力とローテーション
  5. 05R8ルールによるデバッグログ除去

活用シーン

  1. 01CIテスト失敗時のデバイスログ保存
  2. 02アプリやシステムコンポーネントのログ絞り込み
  3. 03構造化JSONログの取得
  4. 04プロセス再起動後もログを追跡
  5. 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 のメッセージ。
crashTombstone と未処理例外の出力。
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イベントログのタグ名をデコードします。
epoch1970-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 — ログキャプチャが適切な可観測性レイヤーとなるケース。

コメント