Timber

最近项目需要开始搭建框架,偶然了解到(Timeber)[],因为有数据安全性的问题,所以跟数据有关的,我个人觉得还是自己写下 比较靠谱,对比了(Logger)[],各取所长吧。Logger的话前面已经讲过远离,所以今天就来看下Timber的源码。Timber的源码比较短,就一个文件。

源码阅读

按照我的习惯,我阅读源码都是从使用出发。这里我们随便拿一个log.v的方法追踪进去
<pre>
public static void v(@NonNls String message, Object... args) {
TREE_OF_SOULS.v(message, args);
}
</pre>
然后追踪到TREE_OF_SOULS发现它是Tree,然后我门继续看Tree,是个抽象类,这里需要特别注意下这个类,因为下面要将的DebugTree也是继承这个规范。这里我们主要看prepareLog方法,因为所有的i,v,d最终调用的都是这个方法,当然
<pre>
private void prepareLog(int priority, Throwable t, String message, Object... args) {
// Consume tag even when message is not loggable so that next message is correctly tagged.
String tag = getTag();

        if (!isLoggable(tag, priority)) {
            return;
        }
        if (message != null && message.length() == 0) {
            message = null;
        }
        if (message == null) {
            if (t == null) {
                return; // Swallow message if it's null and there's no throwable.
            }
            message = getStackTraceString(t);
        } else {
            if (args.length > 0) {
                message = formatMessage(message, args);
            }
            if (t != null) {
                message += "\n" + getStackTraceString(t);
            }
        }

        log(priority, tag, message, t);
    }

</pre>
首先来看看getTag()方法
<pre>
final ThreadLocal<String> explicitTag = new ThreadLocal<>();
String getTag() {
String tag = explicitTag.get();
if (tag != null) {
explicitTag.remove();
}
return tag;
}
</pre>
ThreadLocal 这是一个线程独立的变量,有兴趣的同学可以看看我前面讲Handler原理的文章,简单就是隔离一个数据,实现1个类只拥有唯一的资源的需求。函数体只涉及到变量的remove,所以我们就来找一下这个变量的赋值来搞清具体用处,我们查找explicitTag的所有使用,发现唯一赋值的地方在我们的外层Timber类里
<pre>
public static Tree tag(String tag) {
Tree[] forest = forestAsArray;
//noinspection ForLoopReplaceableByForEach
for (int i = 0, count = forest.length; i < count; i++) {
forest[i].explicitTag.set(tag);
}
return TREE_OF_SOULS;
}
</pre>
这里循环遍历了我们的Tree数组,将所有内部Tree的Tag都进行了赋值。
然后我门再回过头来看看prepareLog函数,这里主要对message进行了处理,特别关注的是Throwable参数,这里有个getStackTraceString方法,往内层追踪到内层用了Throwable类的printStackTrace方法,这里先不管她的实现细节,总之是获取了Throwable里的信息放到了流里面,最后格式化好message调用log方法,log是个抽象方法由用户实现,这条道就走到底了,接下来我们再来看看Tree的几个实现类,最开始的TREE_OF_SOULS变量,他是一个匿名内部Tree的实现类,覆盖了v,d这些方法,后面我门调用plant方法的时候会往forestAsArray数组里面添加我门的Tree实现
<pre>
private static final Tree[] TREE_ARRAY_EMPTY = new Tree[0];
// Both fields guarded by 'FOREST'.
private static final List<Tree> FOREST = new ArrayList<>();
static volatile Tree[] forestAsArray = TREE_ARRAY_EMPTY;
@Override
public void v(String message, Object... args) {
Tree[] forest = forestAsArray;
//noinspection ForLoopReplaceableByForEach
for (int i = 0, count = forest.length; i < count; i++) {
forest[i].v(message, args);
}
}
</pre>
明明都是final的变量,而且是长度为0的数组,然后又加了遍历,好吧,反正先放着等下再来揣测作者的意图,这个内部类就看完了,我们把看完的代码折起来,现在再来看剩下的,这里有个DebugTree,代码比较短,但我们必须每个方法都来看看。因为这里重载了getTag所以其实会被优先调用(根据前面Tree代码的阅读)
<pre>
@Override
final String getTag() {
String tag = super.getTag();
if (tag != null) {
return tag;
}

        // DO NOT switch this to Thread.getCurrentThread().getStackTrace(). The test will pass
        // because Robolectric runs them on the JVM but on Android the elements are different.
        StackTraceElement[] stackTrace = new Throwable().getStackTrace();
        if (stackTrace.length <= CALL_STACK_INDEX) {
            throw new IllegalStateException(
                    "Synthetic stacktrace didn't have enough elements: are you using proguard?");
        }
        return createStackElementTag(stackTrace[CALL_STACK_INDEX]);
    }

</pre>
这里重点关注下StackTraceElement[] stackTrace = new Throwable().getStackTrace()这句代码上面做了注释,相比于Thread.getCurrentThread().getStackTrace(),获得的线程信息更详细一点具体见下图,这里我们发现new throwable少了vm那层的信息,对于我们来讲这一层其实也没什么参考价值

WX20170424-174628@2x.png

这里我门可以看到主要可操控的地方在于对堆栈信息的筛选。
然后我门回过头来看看plant函数,也是我门的初始化函数
<pre>
public static void plant(Tree tree) {
if (tree == null) {
throw new NullPointerException("tree == null");
}
if (tree == TREE_OF_SOULS) {
throw new IllegalArgumentException("Cannot plant Timber into itself.");
}
synchronized (FOREST) {
FOREST.add(tree);
forestAsArray = FOREST.toArray(new Tree[FOREST.size()]);
}
}
</pre>
这里的关键点在于FORSEST.add(),这个函数将我门实现的Tree加了进去,而在Tree的实现里我门又看到具体的log实现是遍历我门的Tree数组进行打印,到这里其实Timber实现的是一个代理的作用,对日志信息进行了管理。

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

推荐阅读更多精彩内容