Android通用框架封装二 Log日志框架封装

1 前言

我们在实际开发中日志这个功能时必不可少的,有些是用原生的android.util.Log,或者使用网上开源的第三方框架例如Logger等,不可否认,第三方框架相对于原生的功能增强了不少。但是这里,我想在原生的基础上封装一个非常简单并且实用的日志框架。请看下面的介绍

2 Log日志框架的主要功能

要封装这个日志框架,首先要知道我们需要什么,要把这个框架做成什么样子。经过思考,我打算把日志框架做成如下样子
1 日志记录功能,能记录各种日志,包括各种级别VERBOSE,DEBUG, INFO, WARN, ERROR,如果调整级别,将相应的隐藏日志的打印
2 日志提供打印到文件中的功能,每个进程打印一个文件,可以为特定的日志指定单独打印到一个文件中
3 根据APP是Debug还是Release 状态自动的开启关闭日志打印功能
4 提供日志打开关闭的开关
5 日志文件可上传至指定的服务器
6 日志打印到文件中格式必须记录 那个文件(那个类)那个方法,多少行等信息

为什么要记录那个文件,那个方法,多少行呢,因为当外场问题发生时,我们往往无法拿到客户的手机,进行问题的复现,这个时候定位问题只能靠日志了,因此我们的日志要尽量记录有用的信息

基于此,我们思考一下,我们的日志功能该怎么设计呢?请看下面

3 Log日志框架的设计与实现

首先,我们设计的框架API使用起来要方便,最好有一个类来统一进行管理,这里我就交给LogManager了。我们的API也尽量设计成静态方法,因为Log的使用是在是太多,如果不设置成静态,势必要产生大量的对象,也不利于管理,也消耗内存。另外,我们将API的实现和API分开。基于此,我们可以设计如下的类图:
这里写图片描述
说明如下:
1 LogManager之所以设计成Map< String,LogImpl >类型,是打算在日志打印到文件中是,同一个进程也可以打印多个日志文件,例如,对于一些重要的,复杂的逻辑流程,我们可以将它单独打印到某个文件中,没有必要和主进程的日志夹杂在一起,例如笔者的项目中用到了SIP协议,这其中的一些协议交互逻辑,如果混在主进程则不好定位,放在单独的一个文件中就很好定位。
2 LogConstant的主要作用就是来存放使用的常量的,例如,目前的截图如下:
这里写图片描述

3 LogImpl主要为日志打印的实现,目前耦合性稍微重了一点,后续视情况拆分
4 LogImpl中isOpen,isWriteFile分别用来区分是否打开日志开关,是否打印到文件中,相关核心代码如下:

    /**
     * 日志开关是否打开
     */
    private boolean isOpen;
    /**
     * 是否写入文件
     */
    private boolean isWriteFile;
    /**
     * 打印日志
     * @param level
     * @param tag
     * @param msg
     * @return
     */
    public int print(int level,String tag, String msg){

        //如果是非调试状态,直接不打印,返回
        if (!AndroidUtil.isDebug(RuntimeEnv.appContext) && !isOpen){
            return 0;
        }

        Log.d(LogConstant.TAG,"log print --> level:" + level);
        //大于等于该级别才会打印
        if (level >= mLevel){
            if (isWriteFile){
                //打印到文件 只有设置了为true才会打印到文件
                printToFile(level,tag,msg);
            }
            //打印到控制台
            printToConsole(level,tag, msg);
        }
        return 0;
    }

5 printToFile(),printToConsole()分别为打印到文件中,打印到Android Studio 终端中。printToConsole()直接调用android.util.Log实现打印。

6 日志打印到文件头中,打印头的处理。我这里的格式如下:

2017-08-28 19:45:14.894 D 15655|15655[SkinManager.java->loadSkin() 94][SkinManager]path --> /storage/emulated/0/red.skin

日志头为:
2017-08-28 19:45:14.894 D 15655|15655[SkinManager.java->loadSkin() 94]
其中格式为:时间 + 级别 + 进程ID | 线程ID + [文件名(类名) ->方法名 行数] + [TAG] + message
响应的代码如下:

/**
     * 打印到日志文件中
     * @param level
     * @param tag
     * @param msg
     */
    private void printToFile(int level, String tag, String msg){
        // 时间格式 2017-8-26 23.22.23.445
        SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
        String time = sf.format(new Date());
        String preTAG = V;

        switch (level) {
            case LogConstant.VERBOSE:
                preTAG = V;
                break;
            case LogConstant.DEBUG:
                preTAG = D;
                break;
            case LogConstant.INFO:
                preTAG = I;
                break;
            case LogConstant.WARN:
                preTAG = W;
                break;
            case LogConstant.ERROR:
                preTAG = E;
                break;
            case LogConstant.ASSERT:
                preTAG = A;
                break;
            default:
                break;
        }
        //打印进程ID 线程ID 当前类 当前方法
        String message = time + " " + preTAG + " "
                + ""+ android.os.Process.myPid() + "|" +""+ android.os.Process.myTid()
                + "[" + RuntimeEnv.getCurrentFileName() + "->" + RuntimeEnv.getCurrentMethodName()+"]"
                + "[" + tag +"]" + msg;
        printMessage(message);
    }

RuntimeEnv.java中

    /***
     * 获取当前运行的类的方法 和行数
     * @return
     */
    public static String getCurrentMethodName() {
        StackTraceElement element = getCallLogManagerStackTrace();
        if (element != null){
            String methodName = element.getMethodName();
            int lineNumber = element.getLineNumber();
            return methodName + "() " + lineNumber;
        }
        return null;
    }

    /**
     * 获取当前运行的Class
     * @return
     */
    public static String getCurrentClassName() {
        StackTraceElement element = getCallLogManagerStackTrace();
        if (element != null){
            String clazz = element.getClassName();
            //去最后一个即 类的简名
            if (clazz.contains(".")){
                String strArray[] = clazz.split("\\.");
                clazz = strArray[strArray.length -1];
            }
            return clazz;
        }
        return null;
    }

    /**
     * 获取当前运行的Class
     * @return
     */
    public static String getCurrentFileName() {
        StackTraceElement element = getCallLogManagerStackTrace();
        if (element != null){
            String fileName = element.getFileName();
            return fileName;
        }
        return null;
    }

基于此,一个简单的日志框架就搭建完成了,后续可以根据项目实际情况添加功能,或者进行优化。详细的代码可以参考我的github
https://github.com/qiyei2015/EssayJoke 中SDK 下的log目录

下面贴一个打印到文件中的截图:
这里写图片描述

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值