Android日誌系統(logging system)詳解
不管是做Android應用還是做Android中間層和底層,Logging系統都是必須要了解的;因為Android不像微控制器程式UCOS那麼簡單,可以很方便的單步除錯。所以,就準備用一篇blog來分析一下logging system。
概覽
Android提供了一個靈活的logging系統,允許應用程式和系統元件等整個系統記錄logging資訊,它是獨立於Linux Kernel的一個logging系統,kernel是通過”pr_info”、”printk”等儲存,通過“dmesg”或“cat /proc/kmsg”獲取。不過,Android logging 系統也是將資訊存在核心快取區。其結構如下:
Logging system由如下幾部分組成:
- 實現loging資訊儲存的kernel驅動和快取區
- C,C++和Java 類新增與讀取log
- 一個單獨瀏覽log資訊的程式(logcat)
- 能夠檢視和過濾來自主機的log資訊(通過Android Studio 或者 DDMS)
其在kernel中為系統的不同部分提供了四個不同log快取區,可以通過/dev/log檢視這些不同的裝置節點,如下:
1 2 3 4 |
/dev/log/mian : 主應用程式log,除了下三個外,其他使用者空間log將寫入此節點,包括System.out.print及System.erro.print等 /dev/log/events : 系統事件資訊,二進位制log |
log中的每條資訊主要由四部分組成,如下:
- Tag
- 時間戳
- log資訊level(或者event的優先順序)
- log資訊
Android logger
logging的kernel driver部分被稱作”logger”,其為系統日誌提供支援,程式碼路徑: kernel/drivers/staging/android/logger.c,此檔案對4種logging快取區加以支援。
驅動
Log的讀寫是通過正常Linux檔案讀寫方式完成的,write path被很好的優化過,所以能很快的open()、write()及close(),這樣就避免了logging在系統中有太多的開銷,影響速度。
Reading
在使用者空間,一個正常的read操作通常讀取從log讀取一個條目,每read一次返回一個log條目或者阻塞等待下一個log條目。裝置可以開啟非阻塞模式。每一個read請求應該至少請求LOGGER_ENTRY_MAX_LEN (4096)長度的資料。
Writing
當系統寫資料到log時,driver將為每一個log條目儲存pid(程序ID),tgid(執行緒組ID),timestamp(時間戳),這些資訊將出現在使用者空間的level,tag和message中。
Ioctl
Ioctl函式支援如下cmd:
1 2 3 4 5 6 |
- LOGGER_GET_LOG_BUF_SIZE : log條目快取區的大小 - LOGGER_GET_LOG_LEN : log資料的長度 - LOGGER_GET_NEXT_ENTRY_LEN: 下一log條目的大小 - LOGGER_FLUSH_LOG : 清除log資料 - LOGGER_GET_VERSION : 獲得logger版本 - LOGGER_GET_VERSION : 設定logger版本 |
裝置節點
當一個使用者空間執行的程式用合適的主裝置號和次裝置號開啟裝置節點後,裝置節點就處於活動狀態,這些裝置節點如下:
1 2 3 4 5 6 |
[email protected]_32:/ # ls -al dev/log ls -al dev/log crw-rw-rw- root log 10, 61 1970-01-09 02:14 events crw-rw-rw- root log 10, 62 1970-01-09 02:14 main crw-rw-rw- root log 10, 60 1970-01-09 02:14 radio crw-rw-rw- root log 10, 59 1970-01-09 02:14 system |
系統和應用程式logging
所有的log資訊在Java類中定義並做相應處理,最終一個格式化的訊息通過C/C++庫傳遞到核心驅動程式,然後再將訊息儲存在適當的緩衝區中。
App log
App通過匯入android.util.Log包來引入Log類,然後通過log方法寫不同優先順序的相關資訊到log。Java類定義傳遞到log方法的tag為字串常量,log方法通過這些字串來獲知資訊的重要性,這樣,當我們用log檢視工具(如logcat)時,就可以過濾tag或者優先順序來獲取我們想要的資訊。如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
[email protected]_32:/ # logcat logcat --------- beginning of system I/Vold ( 265): Vold 2.1 (the revenge) firing up D/Vold ( 265): Volume sdcard1 state changing -1 (Initializing) -> 0 (No-Media) D/Vold ( 265): Volume uicc0 state changing -1 (Initializing) -> 0 (No-Media) D/Vold ( 265): Volume usbotg state changing -1 (Initializing) -> 0 (No-Media) D/Vold ( 265): Volume uicc1 state changing -1 (Initializing) -> 0 (No-Media) I/Cryptfs ( 265): Check if PFE is activated on Boot E/Cryptfs ( 265): Bad magic for real block device /dev/block/bootdevice/by-name/userdata E/Cryptfs ( 265): Error getting crypt footer and key I/irsc_util( 316): irsc tool created:0xb70ff688 I/irsc_util( 316): Starting irsc tool I/irsc_util( 316): Trying to open sec config file |
Event log
Event logs是在android.util.EventLog.class中建立二進位制log資訊。Log條目由二進位制tag程式碼和二進位制引數構成。Event logs 檔案儲存在system/etc/event-log-tags中,通過cat system/etc/event-log-tags能檢視其資訊。如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
[email protected]_32:/ # cat system/etc/event-log-tags cat system/etc/event-log-tags 42 answer (to life the universe etc|3) 314 pi 1003 auditd (avc|3) 2718 e 2719 configuration_changed (config mask|1|5) 2720 sync (id|3),(event|1|5),(source|1|5),(account|1|5) 2721 cpu (total|1|6),(user|1|6),(system|1|6),(iowait|1|6),(irq|1|6),(softirq|1|6) 2722 battery_level (level|1|6),(voltage|1|1),(temperature|1|1) 2723 battery_status (status|1|5),(health|1|5),(present|1|5),(plugged|1|5),(technology|3) 2724 power_sleep_requested (wakeLocksCleared|1|1) 2725 power_screen_broadcast_send (wakelockCount|1|1) 2726 power_screen_broadcast_done (on|1|5),(broadcastDuration|2|3),(wakelockCount|1|1) 2727 power_screen_broadcast_stop (which|1|5),(wakelockCount|1|1) 2728 power_screen_state (offOrOn|1|5),(becauseOfUser|1|5),(totalTouchDownTime|2|3),(touchCycles|1|1) 2729 power_partial_wake_state (releasedorAcquired|1|5),(tag|3) 2730 battery_discharge (duration|2|3),(minLevel|1|6),(maxLevel|1|6) 2740 location_controller |
System log
framework層的許多類通過使用system log 來與app的log資訊區分開來。System log在android.util.Slog.clash中實現。
log命令列工具
log命令列工具能用來給任意程式穿件log條目,此工具是內建與toolbox的多功能程式。在adb shell中輸入log則會提示其用法,如下:
1 2 3 4 5 6 |
C:\Users\Administrator>adb shell [email protected]_32:/ # log log USAGE: log [-p priorityChar] [-t tag] message priorityChar should be one of: v,d,i,w,e |
toolbox: 具有管理記憶體、備份和資料清除功能的一個系統檔案,用來對手機效能進行設定,需要root許可權,能被軟體呼叫。
logwrapper
logwrapper工具是用來捕捉stdout資訊的,當需要從本地應用捕捉stdout資訊到log時,它將十分有用。原始碼路徑:system/core/logwrapper/logwrapper.c;用法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
[email protected]_32:/ # logwrapper logwrapper Usage: logwrapper [-a] [-d] [-k] BINARY [ARGS ...] Forks and executes BINARY ARGS, redirecting stdout and stderr to the Android logging system. Tag is set to BINARY, priority is always LOG_INFO. -a: Causes logwrapper to do abbreviated logging. This logs up to the first 4K and last 4K of the command being run, and logs the output when the command exits -d: Causes logwrapper to SIGSEGV when BINARY terminates fault address is set to the status of wait() -k: Causes logwrapper to log to the kernel log instead of the Android system log |
Logcat命令
我們可以通過logcat命令檢視log,這個命令檔案在檔案系統的system/bin目錄下,所以我們可以到檔案系統中執行logcat,或者直接adb logcat,都能檢視log。adb用法可以檢視adb.html(需翻牆,等什麼時候有空以中文形式移到blog來)。
- 每一個有tag和優先順序的log資訊
- 可以通過tag和log等級過濾log資訊
- 可以通過系統屬性指定程式將stdout和stderr內容寫入日誌
在啟動階段預設開啟Logcat
Android logging和kernel logging是完全不同的兩種日誌系統,另補充一點,kernel日誌支援直接在使用者空間向/dev/kmsg寫入log條目。groups.google.com中介紹瞭如何在啟動階段launch
Logcat,如下:
1 2 3 4 |
it can be launched via init.rc as below.. service logcat /system/bin/logcat -f /dev/kmsg oneshot |
不推薦這樣做,這樣會增加列印開銷,使系統卡頓