前言 html
转载请声明,转自【http://www.javashuo.com/article/p-tygjefwy-g.html】,谢谢!java
从事Android开发的这些年中,常常碰到这样一个现象:同一款app中,每每有好几种风格迥异的log处理方式,有时候会让维护者晕头转向。同时笔者也常常碰带一些模棱两可的问题:Log等级分好几种,到底什么状况下用哪一个等级的log?什么状况下可使用log,log怎么用,为何要这么用?Android的log这么多,要怎么样高效地查看log?带着这些问题,笔者根据平时的开发经验、公司的log规范文档、网络中的相关资料,对log使用作了必定的整理。对于最基本的使用和log介绍,本文不作赘述,但愿本文能帮助一部分人,也但愿大牛们给出更牛的意见和建议,助我成长!android
本文主要内容以下:算法
1、Log等级划分shell
一、经常使用Log等级express
Android系统为开发者提供了良好的日志工具android.util.Log,经常使用的方法有以下5个,将log的输出等级也依次指定了5个级别:apache
(1)Log.v:这里的v表明Verbose啰嗦的意思,对应的log等级为VERVOSE。采用该等级的log,任何消息都会输出。网络
(2)Log.d:这里的d表明Debug调试的意思,对应的log等级为DEBUG。采用该等级的log,除了VERBOSE级别的log外,剩余的4个等级的log都会被输出。架构
(3)Log.i:这里的i表明information,为通常提示性的消息,对应的log等级为INFO。采用该等级的log,不会输出VERBOSE和DEBUG信息,只会输出剩余3个等级的信息。app
(4)Log.w:w表明warning警告信息,通常用于系统提示开发者须要优化android代码等场景,对应的等级为WARN。该级别log,只会输出WARN和ERROR的信息。
(5)Log.e:e表明error错误信息,通常用于输出异常和报错信息。该级别的log,只会输出该级别信息。通常Android系统在输出crassh等致命信息的时候,都会采用该级别的log。
二、相关源码(基于android-26,下同)
源码android.util.Log.java对Log的级别作了比较明确的说明,也依次给出了使用方法,相关源码片断以下所示:
1 /** 2 * API for sending log output. 3 * 4 * <p>Generally, use the Log.v() Log.d() Log.i() Log.w() and Log.e() 5 * methods. 6 * 7 * <p>The order in terms of verbosity, from least to most is 8 * ERROR, WARN, INFO, DEBUG, VERBOSE. Verbose should never be compiled 9 * into an application except during development. Debug logs are compiled 10 * in but stripped at runtime. Error, warning and info logs are always kept. 11 * 12 * <p><b>Tip:</b> A good convention is to declare a <code>TAG</code> constant 13 * in your class: 14 * 15 * <pre>private static final String TAG = "MyActivity";</pre> 16 * 17 * and use that in subsequent calls to the log methods. 18 * </p> 19 * 20 * <p><b>Tip:</b> Don't forget that when you make a call like 21 * <pre>Log.v(TAG, "index=" + i);</pre> 22 * that when you're building the string to pass into Log.d, the compiler uses a 23 * StringBuilder and at least three allocations occur: the StringBuilder 24 * itself, the buffer, and the String object. Realistically, there is also 25 * another buffer allocation and copy, and even more pressure on the gc. 26 * That means that if your log message is filtered out, you might be doing 27 * significant work and incurring significant overhead. 28 */ 29 public final class Log { 30 31 /** 32 * Priority constant for the println method; use Log.v. 33 */ 34 public static final int VERBOSE = 2; 35 36 /** 37 * Priority constant for the println method; use Log.d. 38 */ 39 public static final int DEBUG = 3; 40 41 /** 42 * Priority constant for the println method; use Log.i. 43 */ 44 public static final int INFO = 4; 45 46 /** 47 * Priority constant for the println method; use Log.w. 48 */ 49 public static final int WARN = 5; 50 51 /** 52 * Priority constant for the println method; use Log.e. 53 */ 54 public static final int ERROR = 6; 55 56 /** 57 * Priority constant for the println method. 58 */ 59 public static final int ASSERT = 7; 60 61 ...... 62 }
三、源码解读
除了在注释中明确的解释说明外,咱们也能够留意一下额外的信息
(1)Log.java类被final所修饰,不能被继承,没有子类,同时它也没有继承别的类,没有父类。该类的逻辑关系比较简单,容易阅读,读者有机会能够阅读源码,必定会有更深刻的理解。
(2)能够看到,Log的输出等级还包括了ASSERT,用于输出的函数还包括Log.wtf(...)等,源码中也提到,通常只用上述五种级别log,对于ASSERT和Log.wtf()等就很少说,了解一下便可,平时开发也没必要要使用。
(3)Log的级别依次为2~7,有一个比较奇特的现象就是,没有0和1,不是从0或1开始排等级的,至于缘由,读者若是感兴趣能够研究一下。
(4)类名前的注释中也提到,传入log的字符串会耗用系统开销。因此我们不能没有节制地使用Log,要讲究技巧和使用规范。
......
更多的信息,读者能够多多发掘!
2、Log使用规范
不一样的公司,对Log的使用有不一样的要求和规范,如下笔者就工做中碰到的规范来举例说明Log的使用规范(固然,从上节中的源码注释中,也能看出一些端倪来):
一、在app中,通常不容许使用VERBOSE级别的log,对于INFO、WARN级别的log,容许极少许打印重要信息。这是工做中的要求,系统源码中其实对这三个等级用得也很多,例如,系统打印通常Exception信息时,就是用的WARN级别log
二、只有在出现极严重错误的时候,才容许使用ERROR级别,通常的信息要是用DEBUG级别(在后面讲Log.isLoggable()的时候,会讲到用DEBUG级别的好处)。当系统报Fatal Exception的时候,就是用的ERROR级别的log。
三、用户的隐私信息禁止打印,好比:IMEI、手机号、密码、银行卡号等。在国外,一些法律也对Log内容作了严格的要求。
四、Log中不要打印太多具体实现的细节,这样会致使经过log就能猜到架构的设计和代码的实现。
五、Log中不能暴露核心算法或机制细节,好比核心算法相关信息、应用和框架间函数的调用流程等。
六、禁止在循环打印log。在循环条件、频繁操做、频繁调用的接口、ACTION_MOVE事件、重复打印等地方,必定要控制好log的使用。在单位时间内,不一样性质的应用对log的数目有必定的要求,对每条log的大小也有必定的限制。由于大量或者频繁的log,对app的性能有必定的影响。即使是有log开关控制日志的输出与否,字符串的拼接也是会耗掉一些性能和资源的。
七、打印捕捉到的异常堆栈必须谨慎,如不须要打印堆栈就能定位问题,就尽可能不要打印堆栈,若确实须要堆栈,在同一堆栈,尽可能控制打印频度。
八、对于Android源码中自带的log,尽可能不要修改。在Event Log中,就严禁修改源码自带的log。
九、Log中的TAG,通常以所划分的功能模块命名,log信息也最好用类名,方法名拼接为前缀。这样作的目的就是在查看log的时候,方便定位,对分析问题颇有帮助。
上述不只包含使用规范,也包含了部分log使用小技巧。这些规范中有些会根据不一样公司,不一样严格程度而有所不一样,而有些则须要统一遵照其规范的,读者能够根据具体状况斟酌。
3、Android Studio中查看log
Android Studio为开发者提供了良好的log查看工具,开发者能够经过以下方式打开log视图:View > Tool Windows > Logcat,或者用默认的快捷键 Alt+6 打开/隐藏 Logcat视图。下面简单介绍一下该工具的使用。
一、Logcat中选择筛选条件
以下截图中,标注了Android Studio中使用Logcat视图的经常使用功能,开发者能够根据实际状况选择过滤条件。
二、Log信息颜色设置
查看log的时候,有一个小技巧,为了便于查看不一样等级的log,Android Studio对不一样等级的log信息设置了不一样的颜色。开发者也能够根据本身的爱好,自行设置颜色或者其余属性,这样,在查看log的时候,就容易对log等级进行区分,查看的时候就比较有层次感。设置路径为:File > Settings > Editor > Colors & Fonts > Android Logcat。以下截图所示:
设置完成后,用以下代码进行测试
1 private void showLog(){
2 Log.v(TAG,"Hello,I am VERBOSE"); 3 Log.d(TAG,"Hello,I am DEBUG"); 4 Log.i(TAG,"Hello,I am INFORMATION"); 5 Log.w(TAG,"Hello,I am WARNNING"); 6 Log.e(TAG,"Hello,I am ERROR"); 7 }
logcat视图中打印的log信息以下:
虽然开发者能够根据本身的爱好设置log的颜色等属性,可是笔者仍是建议读者尽可能遵照约定俗称的约定,好比,ERROR级别的log,就每每被设置为红色。
三、Logcat中的log信息说明
以下截图为笔者打印的某条log,对其中各个字段的进行了说明
4、写一份便于使用的Log辅助类
Log的基本使用技能很容易掌握,可是要能灵活地使用在项目中,仍然有不少技巧须要掌握。
一、开发者常碰到的场景
在具体的开发中,开发者每每会遇到以下的情形:
(1)调试的时候,每每会打印很多的log,用于辅助分析问题,可是要发布给用户使用的版本时,这些log必需要关闭掉。
(2)开发者每每会在代码中设置一个变量,好比 boolean isDebug等,来控制日志的打印/关闭。可是每次发布版本的时候,都须要手动去修改这个值,操做不便,甚至容易忘记。
(3)发布给用户使用的user版本,log被关闭了,出现bug须要分析的时候,log信息太少,每每又让开发者感到“巧妇难为无米之炊”,不利于分析问题。
(4)拿到log信息后,又每每不容易找到这条信息和哪一个功能有关,从哪一个类,哪一个方法中打印出来的。
(5)有些log须要在user版本中关闭,但有些log须要一直保留,这两类log的处理,又须要区别对待。
······
诸如此类的情形,想必开发者们都在不断地经历着。
二、辅助工具类代码
有经验的开发者通常都会写一个Log的辅助类来尽可能规避这些麻烦,笔者在开发中也总结了一套代码,以下代码所示:
1 package com.example.demos; 2 3 import android.os.Build; 4 import android.util.Log; 5 6 public class Logger { 7 private static final String TAG = "FunctionName";//功能模块名,好比你开发的是相机功能,这里能够命名为“Camera”,在查看log的时候,能够查看到该功能所有log 8 private static final boolean isLogAnyTime = true;//任何状况下都容许打印的log,不管当前手机固件版本为“user”、“userdebug”仍是“eng”模式 9 10 /** 11 * 用于根据是否容许打印log来决定是否打印DEBUG等级的log 12 * 13 * @param moduleTag //输出该log处所在的类名 14 * @param methodName //输出该log处所在的方法名 15 * @param msg //须要输出的信息 16 */ 17 public static void d(String moduleTag, String methodName, String msg) { 18 if (isTagLoggable(TAG, Log.DEBUG)) { 19 Log.d(TAG, createLogPrefix(moduleTag, methodName, msg)); 20 } 21 } 22 23 /** 24 * 在代码层面,任何状况下都会打印DEBUG等级的日志(在手机系统中也能够设置容许log打印的等级,这种状况另当别论) 25 */ 26 public static void alwaysShowD(String moduleTag, String methodName, String msg) { 27 Log.d(TAG, createLogPrefix(moduleTag, methodName, msg)); 28 } 29 30 public static void e(String moduleTag, String methodName, String msg) { 31 if (isTagLoggable(TAG, Log.ERROR)) { 32 Log.e(TAG, createLogPrefix(moduleTag, methodName, msg)); 33 } 34 } 35 36 public static void alwaysShowE(String moduleTag, String methodName, String msg) { 37 Log.e(TAG, createLogPrefix(moduleTag, methodName, msg)); 38 } 39 40 /** 41 * 用于打印方法的调用栈,即该函数一层一层的调用关系列表 42 */ 43 public static void printStackTraceInfo() { 44 if (isTagLoggable(TAG, Log.DEBUG)) { 45 Log.d(TAG, Log.getStackTraceString(new Throwable())); 46 } 47 } 48 49 /** 50 * 获取捕捉到的Exception信息,并转化为字符串 51 */ 52 public static void printExceptionInfo(Exception pEx) { 53 String _exStr = pEx.toString() + "\n"; 54 StackTraceElement[] stackTraceElements = pEx.getStackTrace(); 55 if (stackTraceElements == null) { 56 Log.w(TAG, _exStr); 57 } 58 for (StackTraceElement se : stackTraceElements) { 59 _exStr += ("at " + se.getClassName() + "." + se.getMethodName() + "(" + se.getFileName() + ":" + se.getLineNumber() + ")\n"); 60 } 61 Log.w(TAG, _exStr); 62 } 63 64 /** 65 * 判断当前log是否容许输出 66 * 67 * @param tag 官方:the tag to check 68 * @param level 官方:the level to check 69 * @return true 表示容许输出,false表示不容许输出 70 */ 71 private static boolean isTagLoggable(String tag, int level) { 72 return Log.isLoggable(tag, level) || isDebugMode() || isLogAnyTime; 73 } 74 75 /** 76 * 将各个参数按照必定的格式组合,便于log查看 77 * 78 * @param moduleTag 传入所在的类名 79 * @param methodName 传入所在的方法名 80 * @param msg 要输出的信息 81 * @return 组合后的字符串 82 */ 83 private static String createLogPrefix(String moduleTag, String methodName, String msg) { 84 StringBuffer buffer = new StringBuffer(); 85 buffer.append("[").append(moduleTag).append("]").append(methodName).append(":").append(msg); 86 return buffer.toString(); 87 } 88 89 /** 90 * 手机的系统通常有“user”、“userdebug”、“eng”版本,“user”版本是最终发给用户使用的版本, 91 * 而另外两种为工程师调试的版本,能够对手机作更多的操做,好比root,remount等。每每开发者 92 * 用于调试的大部分log,在发给用户使用时(机user版本),必需要关闭掉。 93 * 94 * @return true 表示当前手机系统版本为“eng”或者“userdebug”版本 95 * false表示“user”版本 96 */ 97 private static boolean isDebugMode() { 98 return "eng".equals(Build.TYPE) || "userdebug".equals(Build.TYPE); 99 } 100 }
注:这套代码是根据公司的log使用规范来实现的,笔者当前从事手机系统app的开发,上述的处理办法也相对偏向系统app方面,可是对于纯第三方app开发者而言,也是实用的。
三、辅助类的使用和说明。
(1)打印基本log
根据代码中的注释,想必对于这些方法的使用和含义,是很容易理解的。下面简单演示一下使用的例子
在须要打印log的地方调用 Logger.d(className,methodName,msg);便可,以下演示了输出后的log
这里提一个小技巧:对于className的获取,能够采用以下的方法(这里的TAG,就是传入的Logger.d(...)中的类名了):
public class HandleDemoActivity extends AppCompatActivity { private static final String TAG = HandleDemoActivity.class.getSimpleName(); ...... }
类名.class.getSimpleName()返回的结果就是"HandleDemoActivity",这样作最大的好处就是,若是类名有变化,这个值也会随着改变,若是采用硬编码写死这个变量,灵活性会比较差。
(2)打印函数调用栈printStackTraceInfo
如下截图展现了函数的调用栈,对于分析某个方法被调用的轨迹很是有用。第二行printStackTraceInfo()方法是最终捕捉调用栈的地方,能够清晰看到其调用轨迹。
(3)打印异常信息printExceptionInfo(Exception pEx)
该方法主要用打印捕获的Exception信息,以下截图一清晰展现地展现了异常缘由,发生的地方,已经调用栈等信息。sdk也自带了e.printStackTrace()方法,由系统本身打印(截图二)。可是其打印信息被拆分为多条信息打印,在按某个tag进行搜索时,只能搜索到其中含有该tag的信息,而不能总体显示,自定义的方法就克服了这一点,便于总体查看。固然,读者能够根据本身爱好来选择是否用sdk自带的函数。
截图一:自定义的异常打印
截图二:sdk自带的异常打印
(4)使用Log.isLoggable(tagName, level)
本小结中第1点第(3)条中有提到,调试版本中的log,在user版本中被关闭,这极大地妨碍了对bug的分析。因此在判断是否容许打印log的条件isTagLoggable(...)中,添加了一个“或”条件,Log.isLoggable(tag, level),就很好地解决了user版本中不能打印部分log的问题。
1)基本使用
加上这条件后,在user版本系统中,只要在命令框中执行以下命令便可:
adb shell setprop log.tag.tagName level
命令中的tagName为辅助类中的TAG值,即FunctionName,level是指但愿输出的log等级下限,好比,若是level为D,则除VERBOSE外,其余等级更高log都会输出;level为E,就只有ERROR等级log会输出。针对该辅助类的具体命令为:
adb shell setprop log.tag.FunctionName D
输入该命令后,凡是以“FunctionName”为tag名,等级在DEBUG及以上的log,就都会输出了。要想恢复到不可打印的状态,只要重启手机便可。
2)相关源码
1 /** 2 * Checks to see whether or not a log for the specified tag is loggable at the specified level. 3 * 4 * The default level of any tag is set to INFO. This means that any level above and including 5 * INFO will be logged. Before you make any calls to a logging method you should check to see 6 * if your tag should be logged. You can change the default level by setting a system property: 7 * 'setprop log.tag.<YOUR_LOG_TAG> <LEVEL>' 8 * Where level is either VERBOSE, DEBUG, INFO, WARN, ERROR, ASSERT, or SUPPRESS. SUPPRESS will 9 * turn off all logging for your tag. You can also create a local.prop file that with the 10 * following in it: 11 * 'log.tag.<YOUR_LOG_TAG>=<LEVEL>' 12 * and place that in /data/local.prop. 13 * 14 * @param tag The tag to check. 15 * @param level The level to check. 16 * @return Whether or not that this is allowed to be logged. 17 * @throws IllegalArgumentException is thrown if the tag.length() > 23 18 * for Nougat (7.0) releases (API <= 23) and prior, there is no 19 * tag limit of concern after this API level. 20 */ 21 public static native boolean isLoggable(String tag, int level);
3)源码解读
依据以上源码及注释,笔者提取了部分信息:
4)测试代码
以下为一个测试函数
1 private void testIsLoggable() { 2 boolean b1 = Log.isLoggable(TAG, Log.VERBOSE); 3 boolean b2 = Log.isLoggable(TAG, Log.DEBUG); 4 boolean b3 = Log.isLoggable(TAG, Log.INFO); 5 boolean b4 = Log.isLoggable(TAG, Log.WARN); 6 boolean b5 = Log.isLoggable(TAG, Log.ERROR); 7 Log.e(TAG, "" + b1 + ";" + b2 + ";" + b3 + ";" + b4 + ";" + b5); 8 Log.v(TAG,"VERBOSE log can be print"); 9 Log.d(TAG,"DEBUG log can be print"); 10 Log.i(TAG,"INFO log can be print"); 11 Log.w(TAG,"WARN log can be print"); 12 Log.e(TAG,"ERROR log can be print"); 13 }
5)测试结果
a)不执行任何命令,测试结果为:
证实了tag默认level为INFO的结论,可是Log.v() - Log.e() 均能打印出log。
b)执行命令
adb shell setprop log.tag.HandleDemoActivity I
测试结果为:
不知道读者有没有发现,尽管默认level为INFO,此处命令设置的值也为INFO,但此时Log.v()和Log.d()的msg都没有再输出了。
c) 执行命令
adb shell setprop log.tag.HandleDemoActivity W
测试结果为:
d)结论
这里我们也能够看到,Log.isLoggable(TAG,Log.DEBUG)的值默认是false。我们在第二节讲Log的使用规范时的第2点中提到过,通常信息的打印用DEBUG级别log,再结合前面给出的Log辅助类,在这里能够感觉到一些好处了,固然读者也能够根据本身的理解,利用这些条件设计本身驾轻就熟的使用方法来。
以上的测试结果,咱们还能够获得一个结论:adb shell setprop log.tag.tagName level 不只会改变Log.isLoggable(tag,level)的返回值,也会影响到Log.v() - Log.e() 是否打印。读者必定要注意这些细微的差异,笔者刚开始的时候,也忽视过,也曾蒙圈过-_-!
6)推荐阅读
https://blog.csdn.net/qqxiaoqiang1573/article/details/72867776
5、log的获取
设计好了log的输入策略,就能够获取log了。笔者接触到的获取log的方式主要有以下几种
一、开发工具中获取。
好比上文中提到的Android Studio自带的Logcat视图,一样eclipse中也有该视图,都比较好用。这种方法主要被开发者使用,测试人员通常不会使用IDE中的相似工具。
二、adb自带工具 logcat
该命令功能也比较强大,使用起来很是方便,不须要额外的IDE,电脑上配置好adb,链接上手机,在命令框中输入命令便可。该工具的命令也很多,功能也比较强大,惋惜,笔者对这个功能用得很少,主要使用IDE自带工具和手机的Mobile Log。
推荐阅读:https://blog.csdn.net/liao277218962/article/details/50129009
三、手机自带抓log功能
通常手机也都自带了抓取log的工具,不一样的品牌和机型,抓取系统log的方式和log的形式也不尽相同,下面以某比亚的某款机型为例来讲明。
(1)在拨号盘中输入暗码(能够在网上搜,不一样品牌暗码各不一样,同一手机中抓取log的种类也多样)就会进入到log工具界面,以下所示:
能够看到,能够抓取的log种类很是多,我们这里只打开MobileLog。开发者能够根据实际状况选择开启须要的log,笔者目前为止,只用到过MoboleLog,-_-
(2)在使用以前,先点击“清空”按钮清理掉以前的log文件, 以避免无关log太多,影响查看有用信息。
(3)点击“开始”按钮,系统就开始抓取log了。
(4)开始操做手机,复现bug等,这段期间产生的log会被捕获到。
(5)操做完成后,点击“关闭”按钮,系统会生成日志文件,在最底部能够看到日志的存储路径,在该路径下获取便可。
6、查看及分析log
拿到日志文件后,就能够分析log了。在IDE的视图工具Logcat中,和adb logcat中获取的log,基本的查看基本上都会,这里很少说了。这里主要讲讲MobileLog中log分析。
一、文档结构
进入到log文件夹后,会看到以下的文件夹列表
若是开启了MobileLog,重启手机或暂停后从新开启,均会产生一个最新的日志文件夹。开发者从bug复现最近的一次log开始分析。选择某个时间段日志文件夹后点击,会看到以下界面
通常我们只关注moblie文件夹的内容(笔者目前为止也只使用过该目录下的文件)。点击进入后,会显示log文件列表,以下所示:
二、分析log文件
(1)log文件概要
文件名中包含了机型、版本信息,以及文件中log的类型。通常我们也只须要关注crash、main文件,有时候也会关注system日志文件,其使用状况以下。
(2)分析crash文件log
在crash文件中,能够清晰地看到crash发生的时间,引发crash的进程及包名等信息。这里要注意crash的时间,若是和本身复现的场景时间差得比较远(好比10分钟以上),就可能和本身要分析的问题没太大的关联度。
(3)分析main文件log
在main文件中,每每包含了大量的log信息。前面讲到的logcat视图或adb logcat捕获的log,以及不一样机型手机中不一样类型的log,其实基本结构基本相同。单条信息中也都包含了日期、时间、进程号、线程号、log等级、TAG,msg等信息。以下图所示:
在分析这些log的时候,笔者这里提几个常常用的小技巧:
笔者对MobileLog的分析技巧也在学习和摸索中,此处慢慢积累经验,慢慢总结,慢慢更新吧。
三、源码解析
在源码中有以下的代码
1 public static int v(String tag, String msg) { 2 return println_native(LOG_ID_MAIN, VERBOSE, tag, msg); 3 } 4 ...... 5 public static int d(String tag, String msg) { 6 return println_native(LOG_ID_MAIN, DEBUG, tag, msg); 7 } 8 ...... 9 public static int i(String tag, String msg) { 10 return println_native(LOG_ID_MAIN, INFO, tag, msg); 11 } 12 ...... 13 public static int w(String tag, String msg) { 14 return println_native(LOG_ID_MAIN, WARN, tag, msg); 15 } 16 ...... 17 public static int e(String tag, String msg) { 18 return println_native(LOG_ID_MAIN, ERROR, tag, msg); 19 } 20 ...... 21 /** @hide */ public static final int LOG_ID_MAIN = 0; 22 /** @hide */ public static final int LOG_ID_RADIO = 1; 23 /** @hide */ public static final int LOG_ID_EVENTS = 2; 24 /** @hide */ public static final int LOG_ID_SYSTEM = 3; 25 /** @hide */ public static final int LOG_ID_CRASH = 4; 26 27 /** @hide */ public static native int println_native(int bufID, 28 int priority, String tag, String msg);
源码中也正好给出了LOG_ID_MAIN ~ LOG_ID_CRASH 5个LOG_ID值,除了event log外,其它的也是一一对应。Log.v()~Log.e()方法的实现都调用了println_native()方法,传入其中的第一个参数bufID值也都是LOG_ID_MAIN,正好这些方法输出的log都保存在了main文件中。笔者还没有找到明确的资料来证实这其中的联系,但笔者认为,应该不是巧合,读者有兴趣能够本身再深刻研究研究。另外,咱们也能够发现,println_native()也是个native方法,经过jni在本地实现。
7、第三方工具
当前在app开发生,也出现了很多比较优秀的管理log的第三方工具,笔者使用过的有两款:log4j和腾讯的bugly,都比较好用。
我的建议:使用第三方工具,就必然要导入第三方的jar包,sdk等,无疑会增长app的负载。通常来讲,若是本身写的log辅助类可以轻松实现想要的需求,能不用仍是别用吧。固然,我的经验来看,bugly这类功能不太容易本身实现 -_-
8、结语
log的使用算是anroid开发中一个比较基础的技能了,也一个很是实用的技能,是开发中时时刻刻都要用到的。本文所讲的内容大多都算比较基础,固然也包含了一些平时容易忽视的知识点,基本上没有什么讲原理的地方。笔者在MobileLog分析等很多方面,经验也还比较浅,也在不断学习摸索中和总结中,但愿读者们能多多指教,万分感谢!
附录
在文章的最后附上android.util.Log.java的源码,有须要的,能够点开研读研读,在此中秋佳节来临之际,也祝愿全部读者中秋快乐。
1 /* 2 * Copyright (C) 2006 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.util; 18 19 import android.os.DeadSystemException; 20 21 import com.android.internal.os.RuntimeInit; 22 import com.android.internal.util.FastPrintWriter; 23 import com.android.internal.util.LineBreakBufferedWriter; 24 25 import java.io.PrintWriter; 26 import java.io.StringWriter; 27 import java.io.Writer; 28 import java.net.UnknownHostException; 29 30 /** 31 * API for sending log output. 32 * 33 * <p>Generally, use the Log.v() Log.d() Log.i() Log.w() and Log.e() 34 * methods. 35 * 36 * <p>The order in terms of verbosity, from least to most is 37 * ERROR, WARN, INFO, DEBUG, VERBOSE. Verbose should never be compiled 38 * into an application except during development. Debug logs are compiled 39 * in but stripped at runtime. Error, warning and info logs are always kept. 40 * 41 * <p><b>Tip:</b> A good convention is to declare a <code>TAG</code> constant 42 * in your class: 43 * 44 * <pre>private static final String TAG = "MyActivity";</pre> 45 * 46 * and use that in subsequent calls to the log methods. 47 * </p> 48 * 49 * <p><b>Tip:</b> Don't forget that when you make a call like 50 * <pre>Log.v(TAG, "index=" + i);</pre> 51 * that when you're building the string to pass into Log.d, the compiler uses a 52 * StringBuilder and at least three allocations occur: the StringBuilder 53 * itself, the buffer, and the String object. Realistically, there is also 54 * another buffer allocation and copy, and even more pressure on the gc. 55 * That means that if your log message is filtered out, you might be doing 56 * significant work and incurring significant overhead. 57 */ 58 public final class Log { 59 60 /** 61 * Priority constant for the println method; use Log.v. 62 */ 63 public static final int VERBOSE = 2; 64 65 /** 66 * Priority constant for the println method; use Log.d. 67 */ 68 public static final int DEBUG = 3; 69 70 /** 71 * Priority constant for the println method; use Log.i. 72 */ 73 public static final int INFO = 4; 74 75 /** 76 * Priority constant for the println method; use Log.w. 77 */ 78 public static final int WARN = 5; 79 80 /** 81 * Priority constant for the println method; use Log.e. 82 */ 83 public static final int ERROR = 6; 84 85 /** 86 * Priority constant for the println method. 87 */ 88 public static final int ASSERT = 7; 89 90 /** 91 * Exception class used to capture a stack trace in {@link #wtf}. 92 */ 93 private static class TerribleFailure extends Exception { 94 TerribleFailure(String msg, Throwable cause) { super(msg, cause); } 95 } 96 97 /** 98 * Interface to handle terrible failures from {@link #wtf}. 99 * 100 * @hide 101 */ 102 public interface TerribleFailureHandler { 103 void onTerribleFailure(String tag, TerribleFailure what, boolean system); 104 } 105 106 private static TerribleFailureHandler sWtfHandler = new TerribleFailureHandler() { 107 public void onTerribleFailure(String tag, TerribleFailure what, boolean system) { 108 RuntimeInit.wtf(tag, what, system); 109 } 110 }; 111 112 private Log() { 113 } 114 115 /** 116 * Send a {@link #VERBOSE} log message. 117 * @param tag Used to identify the source of a log message. It usually identifies 118 * the class or activity where the log call occurs. 119 * @param msg The message you would like logged. 120 */ 121 public static int v(String tag, String msg) { 122 return println_native(LOG_ID_MAIN, VERBOSE, tag, msg); 123 } 124 125 /** 126 * Send a {@link #VERBOSE} log message and log the exception. 127 * @param tag Used to identify the source of a log message. It usually identifies 128 * the class or activity where the log call occurs. 129 * @param msg The message you would like logged. 130 * @param tr An exception to log 131 */ 132 public static int v(String tag, String msg, Throwable tr) { 133 return printlns(LOG_ID_MAIN, VERBOSE, tag, msg, tr); 134 } 135 136 /** 137 * Send a {@link #DEBUG} log message. 138 * @param tag Used to identify the source of a log message. It usually identifies 139 * the class or activity where the log call occurs. 140 * @param msg The message you would like logged. 141 */ 142 public static int d(String tag, String msg) { 143 return println_native(LOG_ID_MAIN, DEBUG, tag, msg); 144 } 145 146 /** 147 * Send a {@link #DEBUG} log message and log the exception. 148 * @param tag Used to identify the source of a log message. It usually identifies 149 * the class or activity where the log call occurs. 150 * @param msg The message you would like logged. 151 * @param tr An exception to log 152 */ 153 public static int d(String tag, String msg, Throwable tr) { 154 return printlns(LOG_ID_MAIN, DEBUG, tag, msg, tr); 155 } 156 157 /** 158 * Send an {@link #INFO} log message. 159 * @param tag Used to identify the source of a log message. It usually identifies 160 * the class or activity where the log call occurs. 161 * @param msg The message you would like logged. 162 */ 163 public static int i(String tag, String msg) { 164 return println_native(LOG_ID_MAIN, INFO, tag, msg); 165 } 166 167 /** 168 * Send a {@link #INFO} log message and log the exception. 169 * @param tag Used to identify the source of a log message. It usually identifies 170 * the class or activity where the log call occurs. 171 * @param msg The message you would like logged. 172 * @param tr An exception to log 173 */ 174 public static int i(String tag, String msg, Throwable tr) { 175 return printlns(LOG_ID_MAIN, INFO, tag, msg, tr); 176 } 177 178 /** 179 * Send a {@link #WARN} log message. 180 * @param tag Used to identify the source of a log message. It usually identifies 181 * the class or activity where the log call occurs. 182 * @param msg The message you would like logged. 183 */ 184 public static int w(String tag, String msg) { 185 return println_native(LOG_ID_MAIN, WARN, tag, msg); 186 } 187 188 /** 189 * Send a {@link #WARN} log message and log the exception. 190 * @param tag Used to identify the source of a log message. It usually identifies 191 * the class or activity where the log call occurs. 192 * @param msg The message you would like logged. 193 * @param tr An exception to log 194 */ 195 public static int w(String tag, String msg, Throwable tr) { 196 return printlns(LOG_ID_MAIN, WARN, tag, msg, tr); 197 } 198 199 /** 200 * Checks to see whether or not a log for the specified tag is loggable at the specified level. 201 * 202 * The default level of any tag is set to INFO. This means that any level above and including 203 * INFO will be logged. Before you make any calls to a logging method you should check to see 204 * if your tag should be logged. You can change the default level by setting a system property: 205 * 'setprop log.tag.<YOUR_LOG_TAG> <LEVEL>' 206 * Where level is either VERBOSE, DEBUG, INFO, WARN, ERROR, ASSERT, or SUPPRESS. SUPPRESS will 207 * turn off all logging for your tag. You can also create a local.prop file that with the 208 * following in it: 209 * 'log.tag.<YOUR_LOG_TAG>=<LEVEL>' 210 * and place that in /data/local.prop. 211 * 212 * @param tag The tag to check. 213 * @param level The level to check. 214 * @return Whether or not that this is allowed to be logged. 215 * @throws IllegalArgumentException is thrown if the tag.length() > 23 216 * for Nougat (7.0) releases (API <= 23) and prior, there is no 217 * tag limit of concern after this API level. 218 */ 219 public static native boolean isLoggable(String tag, int level); 220 221 /* 222 * Send a {@link #WARN} log message and log the exception. 223 * @param tag Used to identify the source of a log message. It usually identifies 224 * the class or activity where the log call occurs. 225 * @param tr An exception to log 226 */ 227 public static int w(String tag, Throwable tr) { 228 return printlns(LOG_ID_MAIN, WARN, tag, "", tr); 229 } 230 231 /** 232 * Send an {@link #ERROR} log message. 233 * @param tag Used to identify the source of a log message. It usually identifies 234 * the class or activity where the log call occurs. 235 * @param msg The message you would like logged. 236 */ 237 public static int e(String tag, String msg) { 238 return println_native(LOG_ID_MAIN, ERROR, tag, msg); 239 } 240 241 /** 242 * Send a {@link #ERROR} log message and log the exception. 243 * @param tag Used to identify the source of a log message. It usually identifies 244 * the class or activity where the log call occurs. 245 * @param msg The message you would like logged. 246 * @param tr An exception to log 247 */ 248 public static int e(String tag, String msg, Throwable tr) { 249 return printlns(LOG_ID_MAIN, ERROR, tag, msg, tr); 250 } 251 252 /** 253 * What a Terrible Failure: Report a condition that should never happen. 254 * The error will always be logged at level ASSERT with the call stack. 255 * Depending on system configuration, a report may be added to the 256 * {@link android.os.DropBoxManager} and/or the process may be terminated 257 * immediately with an error dialog. 258 * @param tag Used to identify the source of a log message. 259 * @param msg The message you would like logged. 260 */ 261 public static int wtf(String tag, String msg) { 262 return wtf(LOG_ID_MAIN, tag, msg, null, false, false); 263 } 264 265 /** 266 * Like {@link #wtf(String, String)}, but also writes to the log the full 267 * call stack. 268 * @hide 269 */ 270 public static int wtfStack(String tag, String msg) { 271 return wtf(LOG_ID_MAIN, tag, msg, null, true, false); 272 } 273 274 /** 275 * What a Terrible Failure: Report an exception that should never happen. 276 * Similar to {@link #wtf(String, String)}, with an exception to log. 277 * @param tag Used to identify the source of a log message. 278 * @param tr An exception to log. 279 */ 280 public static int wtf(String tag, Throwable tr) { 281 return wtf(LOG_ID_MAIN, tag, tr.getMessage(), tr, false, false); 282 } 283 284 /** 285 * What a Terrible Failure: Report an exception that should never happen. 286 * Similar to {@link #wtf(String, Throwable)}, with a message as well. 287 * @param tag Used to identify the source of a log message. 288 * @param msg The message you would like logged. 289 * @param tr An exception to log. May be null. 290 */ 291 public static int wtf(String tag, String msg, Throwable tr) { 292 return wtf(LOG_ID_MAIN, tag, msg, tr, false, false); 293 } 294 295 static int wtf(int logId, String tag, String msg, Throwable tr, boolean localStack, 296 boolean system) { 297 TerribleFailure what = new TerribleFailure(msg, tr); 298 // Only mark this as ERROR, do not use ASSERT since that should be 299 // reserved for cases where the system is guaranteed to abort. 300 // The onTerribleFailure call does not always cause a crash. 301 int bytes = printlns(logId, ERROR, tag, msg, localStack ? what : tr); 302 sWtfHandler.onTerribleFailure(tag, what, system); 303 return bytes; 304 } 305 306 static void wtfQuiet(int logId, String tag, String msg, boolean system) { 307 TerribleFailure what = new TerribleFailure(msg, null); 308 sWtfHandler.onTerribleFailure(tag, what, system); 309 } 310 311 /** 312 * Sets the terrible failure handler, for testing. 313 * 314 * @return the old handler 315 * 316 * @hide 317 */ 318 public static TerribleFailureHandler setWtfHandler(TerribleFailureHandler handler) { 319 if (handler == null) { 320 throw new NullPointerException("handler == null"); 321 } 322 TerribleFailureHandler oldHandler = sWtfHandler; 323 sWtfHandler = handler; 324 return oldHandler; 325 } 326 327 /** 328 * Handy function to get a loggable stack trace from a Throwable 329 * @param tr An exception to log 330 */ 331 public static String getStackTraceString(Throwable tr) { 332 if (tr == null) { 333 return ""; 334 } 335 336 // This is to reduce the amount of log spew that apps do in the non-error 337 // condition of the network being unavailable. 338 Throwable t = tr; 339 while (t != null) { 340 if (t instanceof UnknownHostException) { 341 return ""; 342 } 343 t = t.getCause(); 344 } 345 346 StringWriter sw = new StringWriter(); 347 PrintWriter pw = new FastPrintWriter(sw, false, 256); 348 tr.printStackTrace(pw); 349 pw.flush(); 350 return sw.toString(); 351 } 352 353 /** 354 * Low-level logging call. 355 * @param priority The priority/type of this log message 356 * @param tag Used to identify the source of a log message. It usually identifies 357 * the class or activity where the log call occurs. 358 * @param msg The message you would like logged. 359 * @return The number of bytes written. 360 */ 361 public static int println(int priority, String tag, String msg) { 362 return println_native(LOG_ID_MAIN, priority, tag, msg); 363 } 364 365 /** @hide */ public static final int LOG_ID_MAIN = 0; 366 /** @hide */ public static final int LOG_ID_RADIO = 1; 367 /** @hide */ public static final int LOG_ID_EVENTS = 2; 368 /** @hide */ public static final int LOG_ID_SYSTEM = 3; 369 /** @hide */ public static final int LOG_ID_CRASH = 4; 370 371 /** @hide */ public static native int println_native(int bufID, 372 int priority, String tag, String msg); 373 374 /** 375 * Return the maximum payload the log daemon accepts without truncation. 376 * @return LOGGER_ENTRY_MAX_PAYLOAD. 377 */ 378 private static native int logger_entry_max_payload_native(); 379 380 /** 381 * Helper function for long messages. Uses the LineBreakBufferedWriter to break 382 * up long messages and stacktraces along newlines, but tries to write in large 383 * chunks. This is to avoid truncation. 384 * @hide 385 */ 386 public static int printlns(int bufID, int priority, String tag, String msg, 387 Throwable tr) { 388 ImmediateLogWriter logWriter = new ImmediateLogWriter(bufID, priority, tag); 389 // Acceptable buffer size. Get the native buffer size, subtract two zero terminators, 390 // and the length of the tag. 391 // Note: we implicitly accept possible truncation for Modified-UTF8 differences. It 392 // is too expensive to compute that ahead of time. 393 int bufferSize = NoPreloadHolder.LOGGER_ENTRY_MAX_PAYLOAD // Base. 394 - 2 // Two terminators. 395 - (tag != null ? tag.length() : 0) // Tag length. 396 - 32; // Some slack. 397 // At least assume you can print *some* characters (tag is not too large). 398 bufferSize = Math.max(bufferSize, 100); 399 400 LineBreakBufferedWriter lbbw = new LineBreakBufferedWriter(logWriter, bufferSize); 401 402 lbbw.println(msg); 403 404 if (tr != null) { 405 // This is to reduce the amount of log spew that apps do in the non-error 406 // condition of the network being unavailable. 407 Throwable t = tr; 408 while (t != null) { 409 if (t instanceof UnknownHostException) { 410 break; 411 } 412 if (t instanceof DeadSystemException) { 413 lbbw.println("DeadSystemException: The system died; " 414 + "earlier logs will point to the root cause"); 415 break; 416 } 417 t = t.getCause(); 418 } 419 if (t == null) { 420 tr.printStackTrace(lbbw); 421 } 422 } 423 424 lbbw.flush(); 425 426 return logWriter.getWritten(); 427 } 428 429 /** 430 * NoPreloadHelper class. Caches the LOGGER_ENTRY_MAX_PAYLOAD value to avoid 431 * a JNI call during logging. 432 */ 433 static class NoPreloadHolder { 434 public final static int LOGGER_ENTRY_MAX_PAYLOAD = 435 logger_entry_max_payload_native(); 436 } 437 438 /** 439 * Helper class to write to the logcat. Different from LogWriter, this writes 440 * the whole given buffer and does not break along newlines. 441 */ 442 private static class ImmediateLogWriter extends Writer { 443 444 private int bufID; 445 private int priority; 446 private String tag; 447 448 private int written = 0; 449 450 /** 451 * Create a writer that immediately writes to the log, using the given 452 * parameters. 453 */ 454 public ImmediateLogWriter(int bufID, int priority, String tag) { 455 this.bufID = bufID; 456 this.priority = priority; 457 this.tag = tag; 458 } 459 460 public int getWritten() { 461 return written; 462 } 463 464 @Override 465 public void write(char[] cbuf, int off, int len) { 466 // Note: using String here has a bit of overhead as a Java object is created, 467 // but using the char[] directly is not easier, as it needs to be translated 468 // to a C char[] for logging. 469 written += println_native(bufID, priority, tag, new String(cbuf, off, len)); 470 } 471 472 @Override 473 public void flush() { 474 // Ignored. 475 } 476 477 @Override 478 public void close() { 479 // Ignored. 480 } 481 } 482 }