Android开发学习之Log的使用

Log(android.util.log)是Android Studio中的日志工具类,熟练使用log会对你以后的Android开发之旅有很大的帮助。
* log类有五个方法,分别是(级别由低到高):根据首字母对应VERBOSE,DEBUG,INFO, WARN,ERROR

1.Log.v():打印一些最为繁琐、意义不大的日志信息
2.Log.d():打印一些调试信息(logd+tab)
3.Log.i():打印一些比较重要的数据,可帮助你分析用户行为数据(logi+tab)
4.Log.w():打印一些警告信息,提示程序该处可能存在的风险(logw+tab)
5.Log.e():打印程序中的错误信息(loge+tab)
(这五个方法都可以进行重载)

注意:不同的打印方法在使用时都是某个方法带上(String tag, String msg)参数,tag表示的是打印信息的标签,msg表示的是需要打印的信息。

 

Android Studio中查看log

        Android Studio为开发者提供了良好的log查看工具,开发者可以通过如下方式打开log视图:View > Tool Windows > Logcat,或者用默认的快捷键 Alt+6 打开/隐藏 Logcat视图。下面简单介绍一下该工具的使用。

    1、Logcat中选择筛选条件  

        如下截图中,标注了Android Studio中使用Logcat视图的常用功能,开发者可以根据实际情况选择过滤条件。

    2、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,就往往被设置为红色。

     3、Logcat中的log信息说明

        如下截图为笔者打印的某条log,对其中各个字段的进行了说明

        

 

四、写一份便于使用的Log辅助类

    Log的基本使用技能很容易掌握,但是要能灵活地使用在项目中,仍然有很多技巧需要掌握。

    1、开发者常碰到的场景

    在具体的开发中,开发者往往会遇到如下的情形:

    (1)调试的时候,往往会打印不少的log,用于辅助分析问题,但是要发布给用户使用的版本时,这些log必须要关闭掉。

    (2)开发者往往会在代码中设置一个变量,比如 boolean isDebug等,来控制日志的打印/关闭。但是每次发布版本的时候,都需要手动去修改这个值,操作不便,甚至容易忘记。

    (3)发布给用户使用的user版本,log被关闭了,出现bug需要分析的时候,log信息太少,往往又让开发者感到“巧妇难为无米之炊”,不利于分析问题。

    (4)拿到log信息后,又往往不容易找到这条信息和哪个功能有关,从哪个类,哪个方法中打印出来的。

    (5)有些log需要在user版本中关闭,但有些log需要一直保留,这两类log的处理,又需要区别对待。

    ······

    诸如此类的情形,想必开发者们都在不断地经历着。

    2、辅助工具类代码

        有经验的开发者一般都会写一个Log的辅助类来尽量规避这些麻烦,笔者在开发中也总结了一套代码,如下代码所示:

 View Code

注:这套代码是根据公司的log使用规范来实现的,笔者当前从事手机系统app的开发,上述的处理办法也相对偏向系统app方面,但是对于纯第三方app开发者而言,也是实用的。

    3、辅助类的使用和说明。

    (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)相关源码

 View Code

     3)源码解读

       依据以上源码及注释,笔者提取了部分信息:

  • 该方法是一个native方法,通过jni在底层实现。
  • 在没有设置运行打印的tag的等级的时候,默认等级为INFO,即此时isLoggable(tag,level)中的leve如果为VERBOSE或DEBUG,isLoggable()会返回false,其它等级level则会返回true。
  • 此处可以设置的level等级更多,在前面提到的5个等级之后,还有ASSERT、SUPPRESS,设置为SUPPRESS可以关闭所有log。
  • TAG字符串长度不可太长,超过23个后,后面的会被截断。
  • 注释中也提供了修改系统文件的方法,来设置允许打印的log的等级。

     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

 

五、log的获取

      设计好了log的输入策略,就可以获取log了。笔者接触到的获取log的方式主要有如下几种 

    1、开发工具中获取。

       比如上文中提到的Android Studio自带的Logcat视图,同样eclipse中也有该视图,都比较好用。这种方法主要被开发者使用,测试人员一般不会使用IDE中的类似工具。

    2、adb自带工具 logcat

       该命令功能也比较强大,使用起来非常方便,不需要额外的IDE,电脑上配置好adb,连接上手机,在命令框中输入命令即可。该工具的命令也不少,功能也比较强大,可惜,笔者对这个功能用得不多,主要使用IDE自带工具和手机的Mobile Log。

       推荐阅读:https://blog.csdn.net/liao277218962/article/details/50129009

    3、手机自带抓log功能

      一般手机也都自带了抓取log的工具,不同的品牌和机型,抓取系统log的方式和log的形式也不尽相同,下面以某比亚的某款机型为例来说明。

      (1)在拨号盘中输入暗码(可以在网上搜,不同品牌暗码各不同,同一手机中抓取log的种类也多样)就会进入到log工具界面,如下所示:

                     

              可以看到,可以抓取的log种类非常多,咱们这里只打开MobileLog。开发者可以根据实际情况选择开启需要的log,笔者目前为止,只用到过MoboleLog,-_-

      (2)在使用之前,先点击“清空”按钮清理掉之前的log文件, 以免无关log太多,影响查看有用信息。

      (3)点击“开始”按钮,系统就开始抓取log了。

      (4)开始操作手机,复现bug等,这段期间产生的log会被捕获到。

      (5)操作完成后,点击“关闭”按钮,系统会生成日志文件,在最底部可以看到日志的存储路径,在该路径下获取即可。

                     

 

六、查看及分析log

     拿到日志文件后,就可以分析log了。在IDE的视图工具Logcat中,和adb logcat中获取的log,基本的查看基本上都会,这里不多说了。这里主要讲讲MobileLog中log分析。

 1、文档结构

      进入到log文件夹后,会看到如下的文件夹列表

     

      如果开启了MobileLog,重启手机或暂停后重新开启,均会产生一个最新的日志文件夹。开发者从bug复现最近的一次log开始分析。选择某个时间段日志文件夹后点击,会看到如下界面

     

     一般咱们只关注moblie文件夹的内容(笔者目前为止也只使用过该目录下的文件)。点击进入后,会显示log文件列表,如下所示:

   

  2、分析log文件

    (1)log文件概要

     文件名中包含了机型、版本信息,以及文件中log的类型。一般咱们也只需要关注crash、main文件,有时候也会关注system日志文件,其使用情况如下。

  • crash文件中收集了系统中crash的log,首先分析这个文件,看是否有和自己项目相关的crash信息。
  • main文件,咱们前文中讲到的添加的log,允许打印的,都会被收集到该文件中。
  • system文件,收集系统的log,系统框架中自带的log会体现在该文件中,偶尔有需要使用。
  • 其他文件使用得不多,笔者暂时还没有碰到要使用剩余这几个文件的场景。

    (2)分析crash文件log

       在crash文件中,可以清晰地看到crash发生的时间,引起crash的进程及包名等信息。这里要注意crash的时间,如果和自己复现的场景时间差得比较远(比如10分钟以上),就可能和自己要分析的问题没太大的关联度。

     

    (3)分析main文件log

        在main文件中,往往包含了大量的log信息。前面讲到的logcat视图或adb logcat捕获的log,以及不同机型手机中不同类型的log,其实基本结构基本相同。单条信息中也都包含了日期、时间、进程号、线程号、log等级、TAG,msg等信息。如下图所示:

        

      在分析这些log的时候,笔者这里提几个经常用的小技巧:

  • 选一个好用的文本编辑器。笔者和周围的同事基本上用的都是Notepad++,对查找信息非常有帮助,对于该工具的使用技巧,读者可以自己网上搜索一下。
  • 结合自己添加log的时候的设计,可以快速根据功能模块、类名、方法名等关键信息,筛选出关联度高的信息来。
  • 每一个app一般对应一个进程号,如果进程号中途变化了,说明中途该app发生了crash,可以在进程号变化点附近查找bug原因。
  • 最重要一点,对这类的log,就是要大量看。有时候看一遍可能找不出问题,就要反复看,找各种关键字搜索,必要时,甚至要逐行逐行看。就像要提升写作技能,首先必须大量去写一样,这是不二法门。

  笔者对MobileLog的分析技巧也在学习和摸索中,此处慢慢积累经验,慢慢总结,慢慢更新吧。

  3、源码解析

      在源码中有如下的代码

 View Code

       源码中也正好给出了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在本地实现。

 

七、第三方工具

     当前在app开发生,也出现了不少比较优秀的管理log的第三方工具,笔者使用过的有两款:log4j和腾讯的bugly,都比较好用。

       个人建议:使用第三方工具,就必然要导入第三方的jar包,sdk等,无疑会增加app的负载。一般来说,如果自己写的log辅助类能够轻松实现想要的需求,能不用还是别用吧。当然,个人经验来看,bugly这类功能不太容易自己实现 -_-


————————————————

posted @ 2020-05-10 22:06  nuoruo  阅读(3576)  评论(0编辑  收藏  举报