android 本地日志打印

提示
博主:章飞_906285288
博客地址:http://blog.csdn.net/qq_29924041
转载请注明出处

前言

  离上一篇博客大概有快四个月没有更新了,当然这段时间其实是遇到了一些事情,也是因为可能自己也比较懒散了点吧,不过渐渐的也就没有时间,没有精力去做一些总结相关的工作了,而且一直工作也比较忙了,给自己找了一些借口,一直想着坚持坚持,但是还是一次一次的放弃了。。。养成一个习惯只要21天,放弃只要一天。好了,废话不多说,直奔主题了

  现在越来越有感觉,基于安卓的智能设备也多如牛毛了,而且现在越来越多的原来做手机开发的人也渐渐转入到了系统应用开发中,两者嘛说一样,其实还是有一定区别的,说有很大区别吧,但是又都是有一个根。所以可以认为大概相似,因为在做系统应用开发的时候,可能考虑的业务深度会稍微深那么一点点。而且现在很多智能设备厂商都是对android系统进行了很多裁剪的操作,也就是动不动砍掉一个Phone模块啊,不编这个模块,改那个模块,并不是像手机方案商那么专业,所以很多时候可能有些问题是不是自己改出来的都不知道,所以系统日志的打印其实就相对比较重要了,做过mtk平台的都知道,mtk在debug版本的时候,是可以打开mtklog的,直接将日志一行一行的打印到本地,如果出现了一些crash,直接在sdcard的某个目录下面,其实就可以找到事发现场,包括framwork,kernel相关的日志都可以。对于问题的分析更加透彻

  现在基于android系统的系统的设备,比如手环,手表,车机,智能猫眼等等一系列的设备。如何打印日志,如何定位问题,甚至当设备已经在用户手中的时候出现的crash日志该如何收集,可能有人说可以用bugly,还有很多第三方的bug收集平台,这肯定是没问题的,但是那些貌似其实都是crash情况,只能定位问题点,但是无法出现问题出现的代码业务逻辑场景。所以根据我以前做过的一些相关工作,简单的写了一个日志收集的demo。姑且叫做Logger吧。


Logger 系统/本地收集日志

本地/系统打印策略

  根据以前使用过的一些日志打印工具,有的使用了Log4j这些开源的库打印的,有的直接使用IO流直接往文件里面写的,现在也有个很牛逼的Logger库等。这些都是可以做到本地打印的功能,但是不要忘了,android原生的logcat命令。本学徒的这个demo其实最主要其实就是基于logcat命令来使用的。 打印需要有一定策略的,比如,存储路径,比如写入的文件大小,写入的文件的数量等。是否是debug模式下。等等都是需要限定的吧。
  一个文件1G,你还有心思看么。一次性打印了100个文件,你还有心思看么???

直接上代码了,先看配置Sysconfig:

 mSysConfig = new SysAppLoggerConfiguration.SysAppLoggerConfigurationBuilder().tag(LoggerConstant.TEST_TAG).elogLevel(EnLogLevel.ALL).fileSuffix(LoggerConstant.SUFFIX).isExternalSdcard(true).logPath("my/sys").setDebug(false).logThreadPrioperty(EnLogThreadPrioperty.NORM_PRIPERTY).logTagPrioperty(EnLogTagPrioperty.NORM_PRIPERTY).singleLogFileSize(1024 * 500 * 2).logfileNums(10).filterParameters(LoggerConstant.params).build();

demo下的所有配置参数,当然每个参数其实都已经有自己的默认值,可以选择性的填写。有参数如下:
1:tag:作为整个应用的总的tag。用于logcat过滤的时候使用
2:elogLevel:选择打印的是哪一个等级,如V,D,I,等等,logcat下打印日志的等级
3:isExternalSdcard:是否在外置sd卡上打印,默认内置
4:logPath存储的路径
5:setDebug是否需要是deubg模式,默认的话,是非debug的,如果是debug的话,只会打印debug日志
6:logThreadPrioperty打印线程的优先级,参数存在意义小
7:fileSuffix 打印日志的后缀名
8:logTagPrioperty 这个优先级,其实是用户筛选的,即限定一个最低的打印优先级,低于此数值的话,则不会打印到日志文本中,只会打印高于此优先级的日志
9:singleLogFileSize 单个文件的大小,限制文件的大小
10:filterParameters:筛选的命令,具体可以参考logcat命令集

通过参数配置的形式然后进行初始化:

 Logger mLogger = Logger.getInstance();mLogger.init(mSysConfig, null);

日志打印的开关及日志清除

mLogger.startLogger();
mLogger.stopLogger();
mLogger.clearLogCache();

打印日志的使用方式:

final LogUtils mLogutils = Logger.getInstance().getLogger();
mLogutils.i(EnLogTagPrioperty.NORM_PRIPERTY.getValue(),TAG, "logger.i--------");
mLogutils.v(10,TAG, "logger.v--------");
mLogutils.d(8,TAG, "logger.d---------");
mLogutils.e(3,TAG, "logger.e---------");

使用方式很简单,其实主要其实就是在每个类中得到LogUtils,然后通过LogUtils直接进行打印。
最最重要的权限千万别忘了啊,不多谈权限:

<uses-permission android:name="android.permission.INTERNET"/><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/><uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/><uses-permission android:name="android.permission.READ_LOGS" />

原理分析,在这个demo中核心代码分析:

  Log.i(LoggerConstant.TEST_TAG,"SysEngineTask run");ShellUtils.execCommand(LoggerConstant.CLEAR_LOGCAT_CACHE, false);try {Log.i(LoggerConstant.TEST_TAG,mConfig.getBaseLogCommands());proc = Runtime.getRuntime().exec(mConfig.getBaseLogCommands());mBufferedReader = new BufferedReader(new InputStreamReader(proc.getInputStream()), 2048);} catch (IOException e) {e.printStackTrace();}if (null == mBufferedOutputStream) {Log.i(LoggerConstant.TEST_TAG, "mBufferOutputStream is null");return;}Log.i(LoggerConstant.TEST_TAG, "mBufferOutputStream is not null");String line = null;int writeLen = 0;int lineLen;int regexPrioperty = -1;while (currentThread().isAlive() && (!Thread.currentThread().isInterrupted())) {try {while (startFlag && (line = mBufferedReader.readLine()) != null) {if (mBufferedOutputStream != null) {lineLen = line.length();if (lineLen == 0){continue;}regexPrioperty = mRegexUtils.getPropNum(line);if (regexPrioperty != -1){if (regexPrioperty <  prioperty){continue;}}mBufferedOutputStream.write(line.getBytes());writeLen += line.getBytes().length;mBufferedOutputStream.write(LoggerConstant.SIGN_LINE_BREAK.getBytes());writeLen += LoggerConstant.SIGN_LINE_BREAK.getBytes().length;if (writeLen >= mConfig.mBuilder.getSingleLogFileSize()){mBufferedOutputStream.write(LoggerConstant.LOG_END.getBytes());Log.i(LoggerConstant.TEST_TAG,"=====||||");mBufferedOutputStream.flush();CloseUtils.closeIO(mBufferedOutputStream);writeLen = 0;mBufferedOutputStream = mFileHelper.changeNextFile();ShellUtils.execCommand(LoggerConstant.CLEAR_LOGCAT_CACHE, false);}}}} catch (IOException e) {e.printStackTrace();}}

SysEngineTask中的代码,其实原理很简单,使用的其实就是执行了logcat命令,将logcat将logcat的流定向,拿到logcat数据流之后,直接将其往一个文件中写,只是在里面加入了一些策略。

注意事项

1:如果你想把它作为系统应用的日志打印工具的话,那么那么一定要注意给你的应用添加系统权限,因为在4.0之后,
uses-permission android:name=”android.permission.READ_LOGS” 这个只能拿到自己当前应用的日志,拿不到系统的日志。
2:当然如果你想通过它拿到自己单个应用的日志,也可以使用,策略是一样的。它可以筛选出本应用下,同一个tag下的日志
3:筛选的命令需要自己选择写。我的demo里面主要是基于logcat -v time -s这个基础命令来使用的

Logger 本地收集日志的另外一种形式

有时候可能有人会觉得我只想打我自己想要的日志,我不想有那么多形式的日志都打印在本地,那样太多了。反正我就是不想要那么多形式的日志。在我的demo里面也提供了一些,使用队列和字符拼接的形式去打印。打印的策略其实与上面的形式类似吧,不过不会把系统的信息给打印出来

依然代码:

void initNatConfiguration(){mNatConnfig =new NatAppLoggerConfiguration.NatAppLoggerConfigurationBuilder().tag(LoggerConstant.TEST_TAG).elogLevel(EnLogLevel.ALL).fileSuffix(LoggerConstant.SUFFIX).isExternalSdcard(true).logPath("my/nat").setDebug(true).logThreadPrioperty(EnLogThreadPrioperty.NORM_PRIPERTY).logTagPrioperty(EnLogTagPrioperty.NORM_PRIPERTY).singleLogFileSize(1024 * 500 * 2).logfileNums(3).initCrashHander(getApplicationContext()).build();}

上面的参数与系统打印的时候的一些参数都是类似的。可以看看,参数也是可缺省的。

初始化的过程也是一样的

mLogger.init(mNatConnfig, new Logger.GetAppNameListener() {@Overridepublic String getName() {return  AppUtils.getAppName(getApplicationContext());}});

注意,因为在拼接的时候,需要用到appName,没办法,简单的使用这样方式来从外界进行获取操作。

启动打印的线程

mLogger.startLogger();

使用的过程:

  final LogUtils mLogutils = Logger.getInstance().getLogger();mLogutils.i(EnLogTagPrioperty.NORM_PRIPERTY.getValue(),TAG, "logger.i--------");mLogutils.v(10,TAG, "logger.v--------");mLogutils.d(8,TAG, "logger.d---------");mLogutils.e(3,TAG, "logger.e---------");

主要的业务逻辑重点分析:
1:队列创建

@Overridepublic void prepare(BaseConfiguration mBaseConfiguration) {mconfig = mBaseConfiguration;if (mBaseConfiguration instanceof SysAppLoggerConfiguration){//如果是系统app的时候该怎么去初始化一些参数configType = 1;mTask = new SysEngineTask(this);}else if (mBaseConfiguration instanceof NatAppLoggerConfiguration){//如果是非系统应用的时候该怎么去初始化一些参数configType = 2;logMsgsQue = new LinkedBlockingQueue<>(1000); //创建里面有1000个容量的队列Log.i(LoggerConstant.TEST_TAG,"initLogMsgQueue");mTask = new NatEngineTask(this);}else {throw new RuntimeException("参数类型不匹配");}}

2:队列使用offer数据

 private void printToTextFile(LogMsg msg){if (null != mBlockQueue){try {Log.i(LoggerConstant.TEST_TAG,"put object:"+msg.getLoggerMsg());mBlockQueue.put(msg);} catch (InterruptedException e) {e.printStackTrace();}}}

3:打印数据的拼接过程

//时间 + 进程ID + 进程名字 + TAG类型 +/tag 类型 + msgpublic synchronized String getLoggerMsg(){mStringBuffer = new StringBuffer();String date = new SimpleDateFormat(LoggerConstant.DATE_FORAT).format(new Date());int pid = AppUtils.getProcessId();int uid = AppUtils.getProcessUid();String type = null;if (level == EnLogLevel.DEBUG){type = "D";}else if (level == EnLogLevel.ERROR){type = "E";}else if (level == EnLogLevel.FATAL){type = "F";}else if ((level == EnLogLevel.INFO)){type = "I";}else if (level == EnLogLevel.WARN){type = "W";}else if (level == EnLogLevel.TRACE){type = "T";}else if (level == EnLogLevel.ALL){type = "V";}else {type = "V";}mStringBuffer.append(date).append(" ").append(uid).append("-").append(pid).append("/").append(appName).append(" ").append(type).append("/").append(tag).append(":").append(logmsg);return mStringBuffer.toString();}

4:将队列中的数据打印到本地的过程

@Overridepublic void run() {while (currentThread().isAlive() && (!Thread.currentThread().isInterrupted())){int writeLen = 0;while (startFlag){try {msg = mBlockQueue.take();if (msg.getPrioperty() < prioperty){continue;}writeLen = writeLen + msg.getLoggerMsg().getBytes().length;if (mBufferOut != null){Log.i(LoggerConstant.TEST_TAG,"loggerMsg:"+msg.getLoggerMsg());mBufferOut.write(msg.getLoggerMsg().getBytes());writeLen += LoggerConstant.SIGN_LINE_BREAK.getBytes().length;mBufferOut.write(LoggerConstant.SIGN_LINE_BREAK.getBytes());if (writeLen >= mConfig.mBuilder.getSingleLogFileSize()){Log.i(LoggerConstant.TEST_TAG,"writelen is Equal LogFilesize:"+mConfig.mBuilder.getSingleLogFileSize());mBufferOut.write(LoggerConstant.LOG_END.getBytes());mBufferOut.flush();CloseUtils.closeIO(mBufferOut);writeLen = 0;mBufferOut = mFileHelper.changeNextFile();}}} catch (InterruptedException e) {e.printStackTrace();}catch (IOException e) {e.printStackTrace();}}}}

从上面的大概代码的过程可以看到,先创建了一个队列,在执行打印的时候生成的msg,然后将msg放入到queue中。单独有一个线程来从queue中取出数据用来打印到本地。只是这种打印过程放入了一些策略性的东西而已

注意注意

权限问题不可避免:注册

 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/><uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/><uses-permission android:name="android.permission.READ_LOGS" />

注册完了在6.0的时候,还是需要进行申请的啊。

 @Overridepublic void requestReadAndWriteSdCardPermission() {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){int checkSefPermiss = mContext.checkSelfPermission(WRITE_EXTERNAL_STORAGE);if (checkSefPermiss == PackageManager.PERMISSION_DENIED){((AppCompatActivity)mContext).requestPermissions(new String[]{WRITE_EXTERNAL_STORAGE},readAndWriteSdCardPermissionRequestCode);}}}

以上部分大概就是整个封装出来的日志的大概的主旨所在以及思想。有兴趣的话可以阅读下我传上去的代码部分。
https://gitee.com/906285288/Android_LoggerUtils-_Library

欢迎持续访问博客

喜欢的朋友们,点个关注撒,谢谢

android 系统/本地日志打印相关推荐

  1. Android 系统(56)---Android 系统开机日志

    <Android内核开发>系列的第八篇文章,本文主要关注如何分析Android系统的启动log,学会通过搜索重要的TAG标签,找到Android启动过程中的重要节点. 要学会分析系统的启动 ...

  2. Android日志打印类LogUtils,能够定位到类名,方法名以及出现错误的行数并保存日志文件...

    关注finddreams,一起分享,一起进步!http://blog.csdn.net/finddreams/article/details/45569089    在开发中,我们常常用打印log的方 ...

  3. 深入浅出 - Android系统移植与平台开发(五)- 定制手机模拟器ROM

    作者:唐老师,华清远见嵌入式学院讲师. 一. 修改化定制Android4.0系统 Android系统启动时,先加载Linux内核,在Linux的framebuffer驱动里可以定制开机界面,Linux ...

  4. Android系统调试指令

    循环执行脚本: while true;do procrank | grep com.topway.livetv; sleep 1; done 网络设置: 抓包:tcpdump-i eth1 -s0 - ...

  5. 【Android 系统开发】 Android 系统启动流程简介

    作者 : 万境绝尘 (octopus_truth@163.com) 转载请注明出处 : http://blog.csdn.net/shulianghan/article/details/3889548 ...

  6. android 系统(6)---Android ADB 命令大全

    原文链接:https://github.com/mzlogin/awesome-adb ADB,即 Android Debug Bridge,它是 Android 开发/测试人员不可替代的强大工具,也 ...

  7. Android系统Surface机制的SurfaceFlinger服务简要介绍和学习计划

    前面我们从Android应用程序与SurfaceFlinger服务的关系出发,从侧面简单学习了SurfaceFlinger服务.有了这些预备知识之后,我们就可以从正面来分析SurfaceFlinger ...

  8. android系统的简单定制

    android入门自学总结: android系统添加linux驱动_Kael.dong的博客-CSDN博客_安卓系统安装linux驱动 android系统的日志系统分层与logcat使用_Kael.d ...

  9. android系统知识,Android系统基础知识百科

    Android系统基础知识百科 2015-12-16 17:58:31 954浏览 Android系统作为一款功能强大的移动计算平台,在保持开放性的同时,必须提供强健的安全保障.Android的系统安 ...

最新文章

  1. 农村信用社招聘考试计算机,农村信用社招聘考试题:计算机(一)
  2. 【精】【火】关于CSDN博客与博主的第二个博客之间的区别与联系
  3. python发动机曲轴连杆动力学计算
  4. 【2021-01-14】navicat使用ssh tunnel、密钥方式连接数据库的问题。错误:Unable to load key - unrecognised cipher name
  5. Java 算法 理财计划
  6. 源码分析Dubbo前置篇-寻找注册中心、服务提供者、服务消费者功能入口
  7. 游戏开发之C++IO流(C++基础)
  8. Hbuilder x安装教程(前端html5开发)
  9. 计算机屏幕显示不能全屏,电脑屏幕小软件显示不全
  10. C语言捕捉键盘,按键信息
  11. Js实现实时显示系统时间(获取当前时间并显示)
  12. 基于java+springboot+mybatis+vue+elementui的农机机械设备租赁平台
  13. 人脸识别技术发展及实用方案设计
  14. C51编程语言bit和sbit,你知道bit和sbit有什么区别吗
  15. php warning require,Thinkphp 网站打开提示Warning: require(): open_basedir restriction in effect另一种解决方法...
  16. MT管理器 – 超强的手机文件管理器
  17. 物联网毕业设计-选题推荐
  18. 防盗链技术及破解方法详细解析
  19. 2017中国各省份GDP排名(预计)
  20. CentOS 7下 VNC 服务的配置和开启、常见问题

热门文章

  1. 【作用域、自由变量】
  2. 【教程】在Radmixture运行无标杆成分名的任意祖源计算器的方法
  3. 语音识别karas实现
  4. 淘宝网首页登录失败原因分析及解决…
  5. 一文搞懂EMAS Serverless小程序开发|电子书免费下载
  6. 360路由器c301的官方固件和openwrt固件
  7. javascript读写二进制
  8. android线程间通信的几种方法_Android进程间和线程间通信方式
  9. 中国传统文化讲坛之“春风拂槛”唐文化论坛成功举办
  10. UI自动化偷懒必备:AirTest封装好ADB命令