读Android 5.X源码系列之 - 再看Log日志系统模块

转自陈启超的博文: 读Android 5.X源码系列之 - 再看Log日志系统模块  

Android应用开发调试离不开各种log日志信息的帮助,一般情况下log日志可以帮你快速定位问题出错的前后位置,除了掌握基本的Java层 使用log api,今天来看一下Android Log框架的构成。顺便说一下很多初学者会遇到各种程序崩溃问题之后不知所措,然后开始在各种讨论交流群提问,这是很浪费时间的。其实我想表达的不是不建 议提问,只是这种提问方式一方面提问别人很难复现你的问题,另一方面你等别人回答的时间里可能你已经可以找到问题的答案。Any way,如果你是Android开发的初学者,还是建议花上一定时间学习各种调试技能,这会让你的学习之路事半功倍。

Android Log系统概览

盗图一张,上图出自参考链接1,基本上把Android Log系统的结构表达清楚了。现在先来再来回顾一下Log的6个信息分级,也是我们经常用来过滤日志信息的标志:

  • Verbose: 用来打印输出价值比较低的信息。

  • Debug: 用来打印调试信息,在Release版本不会输出。

  • Info: 用来打印一般提示信息。

  • Warn: 用来打印警告信息,这种信息一般是提示开发者需要注意,有可能会出现问题!

  • Error: 用来打印错误崩溃日志信息,例如在try-catch的catch中输出捕获的错误信息。

  • Assert: 在Log.wtf()作为参数,表面当前问题是个严重的等级。

在开发中很多人可能不太注意各类分级信息的使用,或者只是让Log日志颜色看起好看一些(囧~)(不同的级别的输出在AS中颜色是不同的),但是正 确的使用信息分级可以让调试更加方便。因为Log信息的输出量在一瞬间是非常大的,可以达到MB级的,如果不通过Log Level过滤,那么查找问题出错的效率会很低下。

在上面的系统Log系统概览图中,有四个Log缓冲区,说明分别如下:

  • main: 缓冲所有Java层中(如Log.i())以及非下面3层的Log信息。

  • radio: 缓冲通信系统的Log信息。

  • event: 缓冲所有event模块的Log信息。

  • system: 缓冲系统组件的Log信息。

So,一般我们只会关心应用层输出到main缓冲区的Log信息,其他我们不是很关心。另外注意,在最新的代码中加入了一个crash缓冲区,下面将会看到。

android.util.log源码初窥

package android.util;
import com.android.internal.os.RuntimeInit;
import com.android.internal.util.FastPrintWriter;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.UnknownHostException;
public final class Log {
    public static final int VERBOSE = 2;
    public static final int DEBUG = 3;
    public static final int INFO = 4;
    public static final int WARN = 5;
    public static final int ERROR = 6;
    public static final int ASSERT = 7;
    private static class TerribleFailure extends Exception {
        TerribleFailure(String msg, Throwable cause) { super(msg, cause); }
    }
    public interface TerribleFailureHandler {
        void onTerribleFailure(String tag, TerribleFailure what, boolean system);
    }
    private static TerribleFailureHandler sWtfHandler = new TerribleFailureHandler() {
        public void onTerribleFailure(String tag, TerribleFailure what, boolean system) {
            RuntimeInit.wtf(tag, what, system);
        }
    };
    private Log() {
    }
    public static int v(String tag, String msg) {
        return println_native(LOG_ID_MAIN, VERBOSE, tag, msg);
    }
    public static int v(String tag, String msg, Throwable tr) {
        return println_native(LOG_ID_MAIN, VERBOSE, tag, msg + '\\n' + getStackTraceString(tr));
    }
    public static int d(String tag, String msg) {
        return println_native(LOG_ID_MAIN, DEBUG, tag, msg);
    }
    public static int d(String tag, String msg, Throwable tr) {
        return println_native(LOG_ID_MAIN, DEBUG, tag, msg + '\\n' + getStackTraceString(tr));
    }
    public static int i(String tag, String msg) {
        return println_native(LOG_ID_MAIN, INFO, tag, msg);
    }
    public static int i(String tag, String msg, Throwable tr) {
        return println_native(LOG_ID_MAIN, INFO, tag, msg + '\\n' + getStackTraceString(tr));
    }
    public static int w(String tag, String msg) {
        return println_native(LOG_ID_MAIN, WARN, tag, msg);
    }
    public static int w(String tag, String msg, Throwable tr) {
        return println_native(LOG_ID_MAIN, WARN, tag, msg + '\\n' + getStackTraceString(tr));
    }
    public static native boolean isLoggable(String tag, int level);
    public static int w(String tag, Throwable tr) {
        return println_native(LOG_ID_MAIN, WARN, tag, getStackTraceString(tr));
    }
    public static int e(String tag, String msg) {
        return println_native(LOG_ID_MAIN, ERROR, tag, msg);
    }
    public static int e(String tag, String msg, Throwable tr) {
        return println_native(LOG_ID_MAIN, ERROR, tag, msg + '\\n' + getStackTraceString(tr));
    }
    public static int wtf(String tag, String msg) {
        return wtf(LOG_ID_MAIN, tag, msg, null, false, false);
    }
    public static int wtfStack(String tag, String msg) {
        return wtf(LOG_ID_MAIN, tag, msg, null, true, false);
    }
    public static int wtf(String tag, Throwable tr) {
        return wtf(LOG_ID_MAIN, tag, tr.getMessage(), tr, false, false);
    }
    public static int wtf(String tag, String msg, Throwable tr) {
        return wtf(LOG_ID_MAIN, tag, msg, tr, false, false);
    }
    static int wtf(int logId, String tag, String msg, Throwable tr, boolean localStack,
    boolean system) {
        TerribleFailure what = new TerribleFailure(msg, tr);
        int bytes = println_native(logId, ASSERT, tag, msg + '\\n'
        + getStackTraceString(localStack ? what : tr));
        sWtfHandler.onTerribleFailure(tag, what, system);
        return bytes;
    }
    static void wtfQuiet(int logId, String tag, String msg, boolean system) {
        TerribleFailure what = new TerribleFailure(msg, null);
        sWtfHandler.onTerribleFailure(tag, what, system);
    }
    public static TerribleFailureHandler setWtfHandler(TerribleFailureHandler handler) {
        if (handler == null) {
            throw new NullPointerException("handler == null");
        }
        TerribleFailureHandler oldHandler = sWtfHandler;
        sWtfHandler = handler;
        return oldHandler;
    }
    public static String getStackTraceString(Throwable tr) {
        if (tr == null) {
            return "";
        }
        // This is to reduce the amount of log spew that apps do in the non-error
        // condition of the network being unavailable.
        Throwable t = tr;
        while (t != null) {
            if (t instanceof UnknownHostException) {
                return "";
            }
            t = t.getCause();
        }
        StringWriter sw = new StringWriter();
        PrintWriter pw = new FastPrintWriter(sw, false, 256);
        tr.printStackTrace(pw);
        pw.flush();
        return sw.toString();
    }
    public static int println(int priority, String tag, String msg) {
        return println_native(LOG_ID_MAIN, priority, tag, msg);
    }
    /** @hide */ public static final int LOG_ID_MAIN = 0;
    /** @hide */ public static final int LOG_ID_RADIO = 1;
    /** @hide */ public static final int LOG_ID_EVENTS = 2;
    /** @hide */ public static final int LOG_ID_SYSTEM = 3;
    /** @hide */ public static final int LOG_ID_CRASH = 4;
    /** @hide */ public static native int println_native(int bufID,
    int priority, String tag, String msg);
}

上面是5.1 framework的去除大部分注释的源码,可以在这里在线查看,假设你有对比一下SDK中的5.1的Log源码,那么会发现两个代码还是有些区别的,如果你知道为什么可以在留言中评论指出。(目前我还不太清楚,囧~)

从上面给出的Java代码中可以发现除了上面提到的4个缓冲区标志:LOG_ID_MAINLOG_ID_RADIOLOG_ID_EVENTS`LOG_ID_SYSTEM之外,Android最新的代码增加了LOG_ID_CRASH缓冲区,diff信息可以在这里看到。而且所有的Java层代码都是调用native层的println_native()方法,至于native层的分析,作为应用层开发者不一定需要详细深入的了解。另外再说一个之前的误解,一开始我以为Log.wtf()是Google工程师恶搞文化,含义是“What The Fuck…”的缩写,其实原意是“What a Terrible Failure”(严重的问题)目的是处理严重的、预料中不会出现的问题。

最后给大家推荐一个好用的Log开源库 - Logger

enjoy~

参考链接: