Android日志系統(logging system)
背景
不管是做Android應用還是做Android中間層和底層,在做一些調試工作的時候,使用adb logcat非常關鍵。特意學習了一下安卓的log系統。
adb logcat -v time -b all
原文(有刪改)出處不詳。
參考文檔:
- http://elinux.org/Android_Logging_System (大部分內容譯自此文檔)
- http://developer.android.com/guide/developing/tools/adb.html
- http://groups.google.com/group/android-kernel/browse_thread/thread/87d929863ce7c29e/f8b0da9ed6376b2f?pli=1
概覽
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查看這些不同的設備節點,如下:
/dev/log/mian: 主應用程序log,除了下三個外,其他用戶空間log將寫入此節點,包括System.out.print及System.erro.print等/dev/log/events: 系統事件信息,二進制log信息將寫入此節點,需要程序解析/dev/log/radio: 射頻通話相關信息,tag 為"HTC_RIL" "RILJ" "RILC" "RILD" "RIL" "AT" "GSM" "STK"的log信息將寫入此節點/dev/log/system: 低等級系統信息和debugging,為了防止mian緩存區溢出,而從中分離出來
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:
-
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版本
設備節點
當一個用戶空間執行的程序用合適的主設備號和次設備號打開設備節點后,設備節點就處於活動狀態,這些設備節點如下:
root@msm8916_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
所有的log信息在Java類中定義並做相應處理,最終一個格式化的消息通過C/C++庫傳遞到內核驅動程序,然后再將消息存儲在適當的緩沖區中。
App log
App通過導入android.util.Log包來引入Log類,然后通過log方法寫不同優先級的相關信息到log。
Java類定義傳遞到log方法的tag為字符串常量,log方法通過這些字符串來獲知信息的重要性,這樣,當我們用log查看工具(如logcat)時,就可以過濾tag或者優先級來獲取我們想要的信息。如下:
root@msm8916_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能查看其信息。
如下:
root@msm8916_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則會提示其用法,如下:
C:\Users\Administrator>adb shell
root@msm8916_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;用法如下:
root@msm8916_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。
- 每一個有tag和優先級的log信息
- 可以通過tag和log等級過濾log信息
- 可以通過系統屬性指定程序將stdout和stderr內容寫入日志
在啟動階段默認打開Logcat
Android logging和kernel logging是完全不同的兩種日志系統,另補充一點,kernel日志支持直接在用戶空間向/dev/kmsg寫入log條目。groups.google.com中介紹了如何在啟動階段launch Logcat,如下:
it can be launched via init.rc as below..
service logcat /system/bin/logcat -f /dev/kmsg
oneshot
不推薦這樣做,這樣會增加打印開銷,使系統卡頓
