1. 概述
应用开发与调试中,可以使用Log增加日志打印;然后通过logcat观察&分析日志,定位问题。
2. Log调用
2.1 方法
Log的调用非常简单,通常包括两个步骤:
- 定义TAG;
- Log.e(), w(), i(), d(), v()打印日志
当然,第一步定义TAG不是必须的,但是惯用法。通常一个Activity或Service会定义这个类的一个TAG,从而和其他的组件区分开来;然后在这个组件直接Log(TAG, msg)调用。
2.2 示例
public class MainActivity extends ActionBarActivity {
private static final String TAG = "MyActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.container, new PlaceholderFragment())
.commit();
}
Log.d(TAG, "onCreate()");
}
2.3 源码
Log的源码如下,包括了前面提到的使用方法:
/**
* API for sending log output.
*
* <p>Generally, use the Log.v() Log.d() Log.i() Log.w() and Log.e()
* methods.
*
* <p>The order in terms of verbosity, from least to most is
* ERROR, WARN, INFO, DEBUG, VERBOSE. Verbose should never be compiled
* into an application except during development. Debug logs are compiled
* in but stripped at runtime. Error, warning and info logs are always kept.
*
* <p><b>Tip:</b> A good convention is to declare a <code>TAG</code> constant
* in your class:
*
* <pre>private static final String TAG = "MyActivity";</pre>
*
* and use that in subsequent calls to the log methods.
* </p>
*
* <p><b>Tip:</b> Don't forget that when you make a call like
* <pre>Log.v(TAG, "index=" + i);</pre>
* that when you're building the string to pass into Log.d, the compiler uses a
* StringBuilder and at least three allocations occur: the StringBuilder
* itself, the buffer, and the String object. Realistically, there is also
* another buffer allocation and copy, and even more pressure on the gc.
* That means that if your log message is filtered out, you might be doing
* significant work and incurring significant overhead.
*/
public final class Log {
2.4 注意事项
Log的调用应该谨慎,不能使用泛滥。一方面,这样不利于定位,大量的无价值的日志会淹没真正有价值的信息,降低定位问题的效率;另一方面,大量的日志会影响程序的运行效率。而且对于手机设备来讲,会影响电池的续航能力(如前面源码中的描述)。
3. logcat
logcat用于捕获(不限于)Log输出的日志信息,包括adb logcat和Eclipse (ADT)的logcat窗口两种方法。
3.1 adb logcat
直接通过adb logcat查看日志,示例:
flying-bird@flyingbird:~/software/android/adt-bundle-linux-x86-20140321/sdk/platform-tools$ ./adb logcat
--------- beginning of /dev/log/system
I/Vold ( 78): Vold 2.1 (the revenge) firing up
D/Vold ( 78): Volume sdcard state changing -1 (Initializing) -> 0 (No-Media)
W/DirectVolume( 78): Kernel block uevent 'PARTN' 1 pendingparts 0
D/Vold ( 78): Volume sdcard state changing 0 (No-Media) -> 2 (Pending)
W/DirectVolume( 78): Kernel block uevent 'PARTN' 1, 1 minor 1
D/Vold ( 78): Volume sdcard state changing 2 (Pending) -> 1 (Idle-Unmounted)
E/NetlinkEvent( 78): NetlinkEvent::FindParam(): Parameter 'SWITCH_NAME' not found
E/NetlinkEvent( 78): NetlinkEvent::FindParam(): Parameter 'SWITCH_STATE' not found
W/Vold ( 78): Switch /devices/virtual/switch/usb_mass_storage event missing name/state info
E/NetlinkEvent( 78): NetlinkEvent::FindParam(): Parameter 'SWITCH_NAME' not found
E/NetlinkEvent( 78): NetlinkEvent::FindParam(): Parameter 'SWITCH_STATE' not found
W/Vold ( 78): Switch /devices/virtual/switch/usb_mass_storage event missing name/state info
W/Vold ( 78): open sys/devices/platform/msm_hsusb/gadget/lun1/file fail:(No such file or directory)
D/VoldCmdListener( 78): volume list
D/VoldCmdListener( 78): share status ums
D/VoldCmdListener( 78): share status ums
D/VoldCmdListener( 78): volume mount /mnt/sdcard
E/DirectVolume( 78): getDeviceNodes i 0 j 0 1
E/Vold ( 78): /dev/block/vold/179:1 being considered for volume sdcard
D/Vold ( 78): Volume sdcard state changing 1 (Idle-Unmounted) -> 3 (Checking)
I/Vold ( 78): Filesystem check completed OK
I/Vold ( 78): Device /dev/block/vold/179:1, target /mnt/sdcard mounted @ /mnt/secure/staging
D/Vold ( 78): Volume sdcard state changing 3 (Checking) -> 4 (Mounted)
D/VoldCmdListener( 78): asec list
E/NetlinkEvent( 78): NetlinkEvent::FindParam(): Parameter 'SWITCH_NAME' not found
E/NetlinkEvent( 78): NetlinkEvent::FindParam(): Parameter 'SWITCH_STATE' not found
W/Vold ( 78): Switch /devices/virtual/switch/usb_mass_storage event missing name/state info
E/NetlinkEvent( 78): NetlinkEvent::FindParam(): Parameter 'SWITCH_NAME' not found
可以看到简单的logcat会输出大量的日志信息。手机厂商通常会预置一些app,消费者拿到手机之后,也会安装一些。由于某些app会滥用Log,因此会导致大量的日志打印出来。
为此,需要对logcat的结果进行过滤。
3.2 adb logcat --help
最简单地,我们直接查阅logcat的帮助信息:
flying-bird@flyingbird:~/software/android/adt-bundle-linux-x86-20140321/sdk/platform-tools$ ./adb logcat --help
Usage: logcat [options] [filterspecs]
options include:
-s Set default filter to silent.
Like specifying filterspec '*:s'
-f <filename> Log to file. Default to stdout
-r [<kbytes>] Rotate log every kbytes. (16 if unspecified). Requires -f
-n <count> Sets max number of rotated logs to <count>, default 4
-v <format> Sets the log print format, where <format> is one of:
brief process tag thread raw time threadtime long
-c clear (flush) the entire log and exit
-d dump the log and then exit (don't block)
-t <count> print only the most recent <count> lines (implies -d)
-g get the size of the log's ring buffer and exit
-b <buffer> request alternate ring buffer
('main' (default), 'radio', 'events')
-B output the log in binary
filterspecs are a series of
<tag>[:priority]
where <tag> is a log component tag (or * for all) and priority is:
V Verbose
D Debug
I Info
W Warn
E Error
F Fatal
S Silent (supress all output)
'*' means '*:d' and <tag> by itself means <tag>:v
If not specified on the commandline, filterspec is set from ANDROID_LOG_TAGS.
If no filterspec is found, filter defaults to '*:I'
If not specified with -v, format is set from ANDROID_PRINTF_LOG
or defaults to "brief"
flying-bird@flyingbird:~/software/android/adt-bundle-linux-x86-20140321/sdk/platform-tools$
或者从Android官网查阅详细信息。由于众所周知的原因,频繁进入developer.android.com是很费劲的,因此最好把Android SDK docs下载一份到本地,具体请参考 Ubuntu下面搭建Android应用开发环境。示例:
3.3 adb logcat使用示例
只显示特定的Activity的debug及以上的日志:
flying-bird@flyingbird:~/software/android/adt-bundle-linux-x86-20140321/sdk/platform-tools$ ./adb logcat MyActivity:d *:S
--------- beginning of /dev/log/system
--------- beginning of /dev/log/main
D/MyActivity( 1939): onCreate()
D/MyActivity( 1939): onResume()
D/MyActivity( 1939): onStop()
如果显示MyActivity的所有日志,则可以直接adb logcat MyActivity *:S
flying-bird@flyingbird:~/software/android/adt-bundle-linux-x86-20140321/sdk/platform-tools$ ./adb logcat MyActivity *:S
--------- beginning of /dev/log/system
--------- beginning of /dev/log/main
D/MyActivity( 1939): onCreate()
D/MyActivity( 1939): onResume()
D/MyActivity( 1939): onStop()
或者
flying-bird@flyingbird:~/software/android/adt-bundle-linux-x86-20140321/sdk/platform-tools$ ./adb logcat -s MyActivity
--------- beginning of /dev/log/system
--------- beginning of /dev/log/main
D/MyActivity( 1939): onCreate()
D/MyActivity( 1939): onResume()
D/MyActivity( 1939): onStop()
如果日志非常多,希望重定向到文件中,事后分析,也是可以的,只需要> mylog即可:
其他几种常用法:
a. 每条日志前面增加时间戳:-v time
flying-bird@flyingbird:~/software/android/adt-bundle-linux-x86-20140321/sdk/platform-tools$ ./adb logcat -s -v time MyActivity
--------- beginning of /dev/log/system
--------- beginning of /dev/log/main
06-05 19:13:57.089 D/MyActivity( 1939): onCreate()
06-05 19:13:57.099 D/MyActivity( 1939): onResume()
06-05 19:14:00.259 D/MyActivity( 1939): onStop()
b. 取完日志之后就退出adb进程:-d
flying-bird@flyingbird:~/software/android/adt-bundle-linux-x86-20140321/sdk/platform-tools$ ./adb logcat -s -v time -d MyActivity
--------- beginning of /dev/log/system
--------- beginning of /dev/log/main
06-05 19:13:57.089 D/MyActivity( 1939): onCreate()
06-05 19:13:57.099 D/MyActivity( 1939): onResume()
06-05 19:14:00.259 D/MyActivity( 1939): onStop()
flying-bird@flyingbird:~/software/android/adt-bundle-linux-x86-20140321/sdk/platform-tools$
c. 日志本身是缓存的,如果要清除历史日志,则使用-c,然后再用前面的命令:
flying-bird@flyingbird:~/software/android/adt-bundle-linux-x86-20140321/sdk/platform-tools$ ./adb logcat -c
flying-bird@flyingbird:~/software/android/adt-bundle-linux-x86-20140321/sdk/platform-tools$ ./adb logcat -s -v time -d MyActivity
--------- beginning of /dev/log/main
flying-bird@flyingbird:~/software/android/adt-bundle-linux-x86-20140321/sdk/platform-tools$ ./adb logcat -s -v time MyActivity
--------- beginning of /dev/log/main
06-05 19:17:07.769 D/MyActivity( 1939): onCreate()
06-05 19:17:07.769 D/MyActivity( 1939): onResume()
06-05 19:17:16.299 D/MyActivity( 1939): onStop()
3.4 Eclipse Logcat View
几种常用的过滤方法:
1. 指定某个Activity的日志:tag:MyActivity
2. 某个app:app:com.example.helloworld