您的位置:首页 > 移动开发 > Android开发

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

2017-09-08 23:50 399 查看

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目录

下面贴一个打印到文件中的截图:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Log 日志