Logger框架解析

Logger简介

用了蛮久的Logger框架,突然想自己做一个,所以从Github上down了一份源码,研究了一下,本身原理比较简单,主要是定制个人有个人需求,技术点就那么几个。
网上使用较高的就是orhanobut的Logger框架,题外话,用了蛮多他的插件的,属于国外的大神。

引用自Logger,效果图

主要原理

StackTraceElement
关于这个StackTraceElement的原生api解释,网上有很多,我就简单说一下。
本身我们知道方法栈的概念就是当每个方法嵌套调用的时候,方法会以指针形式存到我们的方法栈,符合先进后出的原理,也就是最内层的方法是最后入栈的,出栈也就意为着程序执行这个方法。
我们核心代码是

StackTraceElement[] elements=Thread.currentThread().getStackTrace();

调用这个方法就能获取到当前方法所在方法栈信息,我们再来看看StackTraceElement这个类

StackTraceElement

主要关注这几个方法,后面我们需要用到这几个api打印一些关键信息,以及定位。
另外需要注意的是上述的数组,因为会先走底层方法

at dalvik.system.VMStack.getThreadStackTrace(Native Method)
at java.lang.Thread.getStackTrace(Thread.java:579)

所以第三条信息才是我们定义的方法(自定义方法),接下来就是被嵌套的函数,依次类推。

源码分析

因为Logger框架有许多定制的地方,我们需要来看看各个实现,先不上升到设计模式,单纯功能性分析。
按照我的习惯,我喜欢先从功能入手来看源码,于是我首先查看了Logger类,一般辅助类与主类属于从属关系,我决定以下面的顺序进行解读(缩略版的话其实可以省略这些)

Logger的类
  1. Helper类,这个类看到他的使用后就知道他是个辅助类,类似util做一些处理
  • isEmpty(CharSequence):boolean 判断字符串是否为空或者长度为0
  • equals(CharSequence,CharSequence);boolean 逐字比较字符串是否相等
  • getStackTraceString(Throwable):String 打印错误信息
  1. Setting因为嵌套在Printer里面所以首先选择解析这个
Settings

如图这些都是Settings的属性,其实Settings就跟他的名字一样用来控制Log的打印,这里的methodCount,methodOffset目前意义不详,我们到时候回过头来分析

  • showThreadInfo 就是是否需要打印线程信息
  • logAdapter 就是对系统提供的Log框架的分享
  • logLevel 枚举有两个常量FULL,NONE就是是否需要打印log的开关了

3.AndroidLogAdapter是LogAdapter的实现,无非是系统log的实现
4.接下来看Logger类,除了init方法需要注意,也就是我们阅读程序的入口了,其他只是对外提供方法调用而已,在init里面我们看到了整个框架的核心LoggerPrinter

5.LoggerPrinter是Printer的实现,此类实现了所有打印的定制,格式问题我们就不展开了,具体看下几个核心业务的实现就可以动手写我们自己的了

仔细一看所有设计到log(e,w,i,d...)的最后都调用到log方法,那么就简单了我们之需要看这个函数就行了

logTopBorder(priority, tag);
logHeaderContent(priority, tag, methodCount);
//get bytes of message with system's default charset (which is UTF-8 for Android)
byte[] bytes = message.getBytes();
int length = bytes.length;
if (length <= CHUNK_SIZE) {
  if (methodCount > 0) {
    logDivider(priority, tag);
  }
  logContent(priority, tag, message);
  logBottomBorder(priority, tag);
  return;
}
if (methodCount > 0) {
  logDivider(priority, tag);
}
for (int i = 0; i < length; i += CHUNK_SIZE) {
  int count = Math.min(length - i, CHUNK_SIZE);
  //create a new String with system's default charset (which is UTF-8 for Android)
  logContent(priority, tag, new String(bytes, i, count));
}
logBottomBorder(priority, tag);

省略了前面的各种判断,这里首先打印了头,就是那个花哨的东西,然后判断log内容是否超出限制
然后我们再进到logHeaderContent,logContent函数
这两个函数分别打印线程信息,与消息体,这里我们只需要关注线程信息就可以了

private void logHeaderContent(int logType, String tag, int methodCount) {
  StackTraceElement[] trace = Thread.currentThread().getStackTrace();
  if (settings.isShowThreadInfo()) {
    logChunk(logType, tag, HORIZONTAL_DOUBLE_LINE + " Thread: " + Thread.currentThread().getName()); 
   logDivider(logType, tag);
  } 
 String level = "";
  int stackOffset = getStackOffset(trace) + settings.getMethodOffset();
  //corresponding method count with the current stack may exceeds the stack trace. Trims the count  
if (methodCount + stackOffset > trace.length) {
    methodCount = trace.length - stackOffset - 1;
  } 
 for (int i = methodCount; i > 0; i--) {
    int stackIndex = i + stackOffset; 
   if (stackIndex >= trace.length) {
      continue;
    }
    StringBuilder builder = new StringBuilder(); 
   builder.append("║ ")
        .append(level)
        .append(getSimpleClassName(trace[stackIndex].getClassName()))
        .append(".")
        .append(trace[stackIndex].getMethodName())
        .append(" ")
        .append(" (")
        .append(trace[stackIndex].getFileName())
        .append(":")
        .append(trace[stackIndex].getLineNumber())
        .append(")");
    level += "   ";
    logChunk(logType, tag, builder.toString());
  }
}

根据前面的原理桌布来看就很容易理解了,我们首先获取队栈信息,然后打印线程信息,接着打印具体方法,这时候我们就知道Settings里面的offset的用处了,他就是用来帮助我们定位到自己的方法的,所以尽量不要去懂Settings里面的默认值,这个methodCount的含义就是总的嵌套方法数,写了这么多,其实要是不是初学者看下原理,基本自己能实现了
最后附上自己的github地址已上传Maven

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,302评论 5 470
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,232评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,337评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,977评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,920评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,194评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,638评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,319评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,455评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,379评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,426评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,106评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,696评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,786评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,996评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,467评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,043评论 2 341

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,280评论 25 707
  • 在应用程序中添加日志记录总的来说基于三个目的:监视代码中变量的变化情况,周期性的记录到文件中供其他应用进行统计分析...
    时待吾阅读 4,951评论 0 6
  • 在应用程序中添加日志记录总的来说基于三个目的:监视代码中变量的变化情况,周期性的记录到文件中供其他应用进行统计分析...
    时待吾阅读 4,913评论 1 13
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,566评论 18 139
  • 莉淇的第13篇原创 大学同学小雷因为高血压引发了脑干出血,被送进了ICU...
    莉淇阅读 595评论 8 3