支持手机和控制台log的小工具-ZeusLog

0x00 解决问题

  1. 网络请求查看比较麻烦,需要fiddler/charles代理,再格式化json。手机端可以像打印日志一样打印json格式化后的log ,其中【log内容区透传操作,不影响操作app,侧边栏控制内容区过长的滑动】
  2. 系统Log比较挫,限制多多。优化系统控制台log打印

项目地址 https://github.com/xsfelvis/ZeusLog

0x01 ZeusLog

主要分为两大块,移动端Log和控制台Log,先上图

移动端

主要支持

  • 显示当前Actvity的名称
  • 显示所需打印网络请求的内容,内容部分透传点击事件,不影响app使用
  • 右边有一个长条控制区域,可以滑动联动内容区域,便于阅读长的网络请求

具体如下

【黄色区域】 当前Actvity名称

【红色区域】 log日志开启或者关闭,关闭后右边控制区域也随之消失,只有当前的Activity名称,如下

【橙色区域】 网络请求格式化显示区域,该部分透传所有点击事件,从而使整个App使用不受影响

【绿色区域】 自定义sideBar,滑动该区域控制橙色区域长文本滚动阅读

技术点:

没有采用window去实现,原因很简单,兼容性不好,现在各大厂商对自己的windows权限管理都很紧,而且正好尝试一下自定义view+事件分发,有了想法,一时技痒,就撸一个呗,当然你也可以有其他更好的思路,也可以跟我交流。

主要采用了自定义viewgroup+自定义viewSideDragBar【绿色部分】,通过橙色部分显示内容,透传点击事件从而不影响用户对app操作,SideDragBar控制内容区域的滚动,从而不影响内容区长文本的阅读。

几个关键点:

  1. 附着到当前Activity屏幕

获取屏幕的content区域内容,然后将我们的自定义viewgroup add进去

 ViewGroup rootView = (ViewGroup) activity.findViewById(Window.ID_ANDROID_CONTENT);

首先我们需要知道android中window的位置,下面摘了一张图

  1. 透传文本区域的点击事件

我们希望屏幕日志只是给用户用来查看,而不要影响用户对app的操作,但是也要对长文本支持,这样采用重写ScrollView包裹一个textView的方法来解决这个问题,这个ScrollView的拦截事件方法均被重写


    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        return false;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return false;
    }

这样这个scrollview就可以透传了,


  <xsf.zeuslibrary.zeusMobile.ScrollViewSV
            android:id="@+id/svContent"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="9">

            <TextView
                android:textColor="@color/white"
                android:id="@+id/tvShowInfo"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="日志显示 \n"
                android:textSize="12dp"/>
        </xsf.zeuslibrary.zeusMobile.ScrollViewSV>
  1. 长日志如何滑动查看

这里思考了一会,最后给出的解决方案就是在边栏加一个自定义view SideDragBar【绿色部分】

通过重写dispatchTouchEvent,记录滑动变化,然后使用控制传入的scrollview滚动


    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        final int action = event.getAction();
        //记录当前点击位置
        int x = (int) event.getX();
        int y = (int) event.getY();

        switch (action) {
            case MotionEvent.ACTION_DOWN:
                lastX = x;
                lastY = y;
            case MotionEvent.ACTION_MOVE:
                int offsetX = x-lastX;
                int offsetY = y-lastY;
                sv.smoothScrollBy(0,offsetY);
                break;
            case MotionEvent.ACTION_UP:
                break;
        }
        return true;
    }

总之就是通过重写scrollview使得控制滚动,从而解决这个问题

4.格式化json数据

关于这个,刚开始在网上找了一下,很多都是直接for循环一个一个字符的去解析,感觉实在不够优雅,而且效率也很低,其实JSONObjectJSONArray就可以解决这个问题


 String message;
        try{
            if(msg.startsWith("{")){
                JSONObject jsonObject = new JSONObject(msg);
                message = jsonObject.toString(ZeusLog.JSON_INDENT);
            }else if(msg.startsWith("[")){
                JSONArray jsonArray = new JSONArray(msg);
                message = jsonArray.toString(ZeusLog.JSON_INDENT);
            }else {
                message = msg;
            }

        }catch (JSONException e){
            message = msg;
        }

控制台

控制台有一些比较全的Log库,如orhanobut/logger、JakeWharton/timber等,感觉这些库写的都很重,当然功能很全,个人不太不喜欢他们打印log格式化方式,而且也想看看具体原理还有也不想增加无需求的功能,如xml格式化等,最终自己搓了一个支持基本log+多参数+json格式,具体如下:

  • 支持显示行号
  • 支持显示Log所在函数名称
  • 支持无Tag快捷打印
  • 支持在Android Studio开发IDE中,点击函数名称,跳转至Log所在位置
  • 支持JSON字符串解析打印
  • 支持无限长字符串打印,无Logcat4000字符限制
  • 支持变长参数,任意个数打印参数
  • 支持设置全局Tag

基本tag

无tag显示当前类名

格式化输出json

多参数log

几个关键点

  1. 显示行号、函数名

这个需要使用到Thread.currentThread().getStackTrace()返回的是一个StackTraceElement数组,内容为调用函数堆栈,并且以调用层级关系保存。android中对应返回值数组是19个,而且最终都是调用

dalvik.system.NativeStart.main(Native Method)

有兴趣的可以断点看下,其实日志打印工具类实现都离不开它的应用,然后获取对应index的StackTraceElement你就可以获取当前行号,类名,部分代码如下

StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
        StackTraceElement targetElement = stackTrace[stackTraceIndex];
        String className = targetElement.getClassName();
        String[] classNameInfo = className.split("\\.");
        if (classNameInfo.length > 0) {
            className = classNameInfo[classNameInfo.length - 1] + SUFFIX;
        }
        if (className.contains("$")) {
            className = className.split("\\$")[0] + SUFFIX;
        }
        String methodName = targetElement.getMethodName();
        int lineNumber = targetElement.getLineNumber();
        if (lineNumber < 0) {
            lineNumber = 0;
        }
  1. log长字符的限制

先介绍下Android中Log的实现结构


可以看出大致过程 App通过util.log.产生日志->JVM->JNI(Native C)调用->log_write的sys_call()->logger驱动->dispatch分发给订阅者,android打印受限问题就出在这个Logger驱动上


#define LOGGER_ENTRY_MAX_LEN        (4*1024)  
#define LOGGER_ENTRY_MAX_PAYLOAD    \\  
    (LOGGER_ENTRY_MAX_LEN - sizeof(struct logger_entry))

可以看出,系统的显示单条Log长度是有限制的4*1024字符长度,那么我们打印日志的时候可以对症下药,在写自己的log日志时可以对msg做下处理,采取分段打印


   public static void printDefault(int type, String tag, String msg) {
        int index = 0;
        int length = msg.length();
        int countOfSub = length / MAX_LENGTH;
        if (countOfSub > 0) {
            for (int i = 0; i < countOfSub; i++) {
                String sub = msg.substring(index, index + MAX_LENGTH);
                printSub(type, tag, sub);
                index += MAX_LENGTH;
            }
            printSub(type, tag, msg.substring(index, length));
        } else {
            printSub(type, tag, msg);
        }
    }

0x02 How to Use

compile 'com.xsf:zeusLog:1.0.0'

移动端Log使用很简单,需要在你想打印的地方,调用如下API即可

ZeusMobileView.startZeus(MainActivity.this).setJsonStr(JSON_LONG);

控制台Log 需要先初始化安全等级

ZeusLog.init(BuildConfig.DEBUG); 表示仅仅在debug包下打印日志,如果不初始化也可使用,但是需要注意release包保护

然后如同使用系统API一样使用即可

不带tag

  • ZeusLog.v(LOG_MSG);
  • ZeusLog.d(LOG_MSG);
  • ZeusLog.i(LOG_MSG);
  • ZeusLog.w(LOG_MSG);
  • ZeusLog.e(LOG_MSG);
  • ZeusLog.a(LOG_MSG);

带tag

  • ZeusLog.v(TAG, LOG_MSG);
  • ZeusLog.d(TAG, LOG_MSG);
  • ZeusLog.i(TAG, LOG_MSG);
  • ZeusLog.w(TAG, LOG_MSG);
  • ZeusLog.e(TAG, LOG_MSG);
  • ZeusLog.a(TAG, LOG_MSG);

json格式化

  • ZeusLog.printJsonStr(JSON);

多个参数

  • ZeusLog.v(TAG, LOG_MSG, “params1”, “params2”, this);
  • ZeusLog.d(TAG, LOG_MSG, “params1”, “params2”, this);
  • ZeusLog.i(TAG, LOG_MSG, “params1”, “params2”, this);
  • ZeusLog.w(TAG, LOG_MSG, “params1”, “params2”, this);
  • ZeusLog.e(TAG, LOG_MSG, “params1”, “params2”, this);
  • ZeusLog.a(TAG, LOG_MSG, “params1”, “params2”, this);

最后感谢你宝贵的时间阅读,如果你喜欢的话可以点赞收藏,也可以关注我的账号,大家一起交流技术。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值