logcat
會改變裝置串流系統紀錄:手機上發生的一切的實況轉播。
- 指令
- adb shell logcat
- 指令
- 系統與紀錄
- 風險
- 會改變裝置
語法
logcat [-v <format>]旗標與參數
| token | 意義 |
|---|---|
| -v <format> | 選擇行格式。值得用的是 `threadtime`(日期、時間、pid、tid、等級、tag),`brief` 是較簡短的 `I/Tag( 1234):` 形式。 |
| -b <buffer> | 要讀哪一個環形緩衝。`default` 一次涵蓋 main、system 與 crash,`all` 拿走 shell 能打開的全部,而這個版本不認識的名稱會被指名拒絕,不是被忽略。 |
| -d | 把緩衝裡目前有的東西倒出來就結束,不要串流。這是串流沒有的界限,也是任何自動化該有的預設。 |
| -t <count> | 印最後 N 行就結束——它隱含 `-d`。在緩衝滿了的手機上比整份倒出來便宜,也是事情已經出錯之後、抓上下文最實際的方法。 |
| -m <count> | 印滿 N 行就停。它不能和 `-t` 一起用,因為 `-t` 設的是起點而不是上限;指令會把話講清楚然後結束,不會去猜你是哪個意思。 |
| -c | 清空緩衝。破壞性而且不需要特權:那些行就這樣沒了,裝置上沒有任何東西留著副本,而指令不會問你。 |
| -G <size> | 調整緩衝大小,寫法是一個數字加一個倍率單位——只給數字會被拒絕。新的大小在纜線被拔掉之後依然存在,這讓它成為這一頁上最持久的東西。 |
範例
logcat -v threadtime欄位最齊的格式,也是檢視器應該解析的那個。
logcat -v brief較舊的簡短形式,在別人貼上來的紀錄裡還是常看到。
logcat -t 200 -v threadtime最後兩百行、欄位齊全,然後結束——在開一個之後還得想辦法停下來的串流之前,先試這個有界限的形式。
logcat -b crash -d -v threadtime只看 crash 這個緩衝,倒一次。殺掉 app 的那個致命例外就記在這裡,而且沒有周圍那幾千行。
輸出怎麼讀
輸出範例
07-26 13:37:01.234 1234 1250 I ActivityManager: Start proc 3062:com.example/u0a234
07-26 13:37:02.010 3062 3062 E MyApp : java.lang.IllegalStateException: not attached用 `threadtime` 時是日期、時間、行程 id、執行緒 id、單一字母的等級、tag,然後在一個冒號加空白之後是訊息。tag 在那個冒號之前可以含空白。不是每一行都是紀錄:`--------- beginning of main` 與空行是紀錄自己的分隔訊息,該顯示出來而不是試著解析。
輸出欄位
| 欄位 | 意義 |
|---|---|
| MM-DD HH:MM:SS.mmm | 裝置自己的本地時間。時區不會印出來。 |
| pid tid | 行程 id 與執行緒 id。兩個數字一樣代表主執行緒。 |
| V D I W E F | Verbose、Debug、Info、Warn、Error、Fatal,嚴重程度依序遞增。 |
| tag | 寫紀錄的人自己選的字串。這是把紀錄縮小範圍最實用的手段。 |
| --------- beginning of … | 緩衝的邊界,不是一筆紀錄。 |
| --------- switch to … | 同一種分隔訊息的第二種形式,當後面某一行來自一個已經開始過的緩衝時會印。同時讀好幾個緩衝時,這兩種標記出每一個來源切換的位置。 |
需要什麼權限
不用 root、不用 `adb root`,開發者選項裡除了 USB 偵錯也沒有東西要打開——而且和這份參考裡多數頁不同,這是刻意給的授權而不是預設值。`adbd` 在放掉權限之前,把 `AID_LOG` 放進它給 `shell` 使用者的附加群組清單裡,這就是為什麼一條線讀得到每個 app 的紀錄,而同一支手機上的 app 從 Jelly Bean 起就只讀得到自己的。指令自己的說明也從另一邊講了同一件事:只有 root、log 與 system 使用者可以看屬於其他 uid 的紀錄。這個授權不涵蓋所有東西:`security` 這個緩衝不會為 shell 打開,而且它是唯一一個「明確指名時會回報被拒絕」的緩衝,其他的只會安靜地被跳過。讀取不需要特權;但這個指令另外三件事並不是唯讀的,值得用對待寫入的態度對待。清空一個緩衝會毀掉你一分鐘後可能會想要的證據。調整緩衝大小,會在纜線拔掉之後繼續有效。寫進檔案會把位元組放到裝置自己的儲存空間上。這三件也都不需要 root,所以除了專心以外,沒有任何東西能阻止一個飛來的旗標丟掉你連線過來要收的那份紀錄。Android Logcat Viewer 三個都不用,它只串流然後在瀏覽器裡篩選,把緩衝原封不動地留在原地。
失敗訊息對照
| 訊息 | 意義 |
|---|---|
| logcat: Unexpected EOF! | 讀取端跟不上寫入端,或者記錄用的常駐程式不見了。這個訊息後面還有好幾行,會同時說出兩個原因和解法——把緩衝調大——因為在忙碌的手機上這是吞吐量問題而不是當機。任何消費這個串流的東西都必須跟得上,或者把要的量框住;沒有第三條路。 |
| logcat: Unable to open log device 'security'. | 那個緩衝存在,但 shell 讀不到。它也是唯一一個「不可用時會被回報」的緩衝:隱含選到而缺席的緩衝會被安靜跳過,只有你自己傳的名稱才會產生這一行。你請求的其他部分不受影響。 |
| logcat: Failed to wait for logd.ready to become true. logd not running? | 記錄用的常駐程式在被問之後一秒內沒有登記上去。開機初期以及常駐程式剛被重啟時,這是預期中的;紀錄本身沒有遺失,而等裝置開完機再問一次就是全部的解法。 |
| logcat: Unknown buffer 'radio1' listed for -b. | 那個緩衝名稱不是這個版本認識的。可用的集合在各版本與各廠商之間並不固定,所以請用 `logcat --help` 去問裝置,不要把另一支手機上的清單帶過來——另外要注意,指令是拒絕整次呼叫,而不是去讀它認得的那幾個緩衝。 |
| logcat: Invalid filter expression in logcat args. | 一個 tag 加等級的篩選式解析不了。等級是單一字母、tag 在冒號處結束,所以一個本身含冒號的 tag 根本沒辦法用這種方式篩,只能等行都到了之後再比對。什麼都沒讀到;指令在打開緩衝之前就結束了。 |
會改變什麼,以及怎麼回頭
讀取紀錄不會改變任何東西,但有兩個旗標會:`-c` 清掉緩衝區,那些行就沒了,裝置上不會留任何備份;`-G <大小>` 調整緩衝區大小,這個設定在拔掉線之後仍然存在。兩者都沒有指令能還原——`-G` 只能把原本的大小寫回去,所以改之前先記下來;`-c` 則要在清除前先用 `-d` 存一份。單純的 `logcat`、`-d`、`-t`、`-v` 都是唯讀的。
版本與廠商注意事項
- 它會一直流到有東西把它停下來,而在忙碌的手機上那是每分鐘好幾千行。消費它的東西需要一個上限,否則它會一直長到機器抗議。
- 很長的一行可能被傳輸切成兩半,所以讀它的東西必須跨越封包邊界保留半行,不然大約每一百行就會壞掉一行。
- 有哪些緩衝、這個版本認得哪些篩選式,都會變。不要假設,用 `logcat --help` 去問裝置。
- 讀得到其他 app 的紀錄,唯一的原因是 `adbd` 在放掉權限之前把 `log` 群組給了 shell。這沒有執行期權限可以申請,也不需要 `adb root`,這也是為什麼 app 重現不了一條線看得到的東西。
- 清空、調整大小與寫進檔案,全都是躲在一個讀取指令頁面上的寫入,而且三個都不會先問。一個才剛開始就結束的串流,在怪裝置之前,先檢查有沒有一個飛來的清空旗標。
這個我們有 UI
Android Logcat Viewer 會透過 USB 執行這個指令並解析回應——只要 Chromium 系瀏覽器,不必安裝 SDK,也不會上傳任何東西。
相關指令
和 logcat 會在同一次作業裡出現的指令。
| 指令 | 做什麼 |
|---|---|
| dumpsys dropbox | 平台用來存放當機、ANR 與 tombstone 的環形緩衝——這是不用 root 讀 ANR 追蹤的方法。 |
| dumpsys activity activities | 現在哪個 app 在前景,以及它背後的 task 與 stack 結構。 |
| run-as | 以某個 app 自己的 uid 執行指令——這是不用 root 讀取可除錯版本私有檔案的方法。 |
其他系統與紀錄指令
這些行為是在撰寫 Web ADB 工具背後的解析程式時,對照 AOSP、toybox 與核心原始碼查證的。真的會因 Android 版本或廠牌而異的地方,這一頁會直接說明,而不是給你一個在半數手機上都是錯的數字。