乐趣区

关于android:Android-log-输出控制

很简略的话题。当我听到有人在探讨本人实现机制管制 log 输入时,我感觉还是有必要记录一下。最近让我比拟困扰的是,很多 Android 根本的技巧都不被通晓。许多人的“锤子”意识很重大,始终应用以往的教训解决所有问题。

影响 Android log 输入的属性

Android 日志存储在由 logd 保护的一组化环形缓冲区,这组缓冲区包含:

  • main:用于存储大多数利用日志。
  • radio:用于存储通信相干的日志。
  • events:用于存储 Event 事件日志。
  • system:用于存储源自 Android 操作系统的音讯。
  • crash:用于存储解体日志。
  • stats:用于存储系统状态事件日志。
  • security:用于存储平安相干事件日志。
  • kernel:用于存储 Linux 内核日志,其输入受其余几个属性的影响,详见 Logcat 读取 Kernel Log。

Android 日志的每个条目都蕴含一个优先级、一个日志所属模块标记以及理论的日志音讯。日志优先级代表日志输入的级别,其优先级为:VERBOSE(V) < DEBUG(D) < INFO(I) < WARNING(W) < ERROR(E) < FATAL(A) < SILENT(S)。日志零碎依据日志级别来输入,仅输入以后级别及以下级别的日志。日志级别能够通过以下四个属性来管制,其优先级如下,

  • persist.log.tag.<MODULE_TAG>:模块日志级别,优先级高于系统日志级别,永恒存储。
  • log.tag.<MODULE_TAG>:模块日志级别,优先级高于系统日志级别,长期存储,重启后隐没。
  • persist.log.tag:系统日志级别,永恒存储
  • log.tag:系统日志级别,长期存储,重启后隐没。

persist.log.tag/log.tag 控制系统的日志级别,当模块日志级别没有设置时(默认都不会设置),所有日志都依据这个级别输入,其中 persist.log.tag 优先级大于 log.tag

persist.log.tag.<MODULE_TAG>/log.tag.<MODULE_TAG> 管制模块的日志级别,但它的优先级大于系统日志级别。当模块日志级别存在时,改模块的日志输入仅由模块日志级别决定,不会参考系统日志级别。其中 persist.log.tag.<MODULE_TAG> 优先级大于 log.tag.<MODULE_TAG>

通过上述的四个属性,能够灵便的控制系统和模块的日志输入。通常,模块日志级别是不被设置的,但它对于开发调试是非常有用的。只有模块中含有足够的日志,就不须要从新编译模块,只有通过属性设置就能够失去须要的调试信息。当上述的四个属性都没有被设置时,零碎会应用默认的日志级别,依据零碎版本的不同,可能是 V 或 I。

如何应用 isLoggable

isLoggablelandroid.util.Log 提供的一个办法,它能够获取指定模块的日志级别。isLoggablel 提供了另一种管制日志输入的办法,能够通过指定模块的日志级别来管制以后日志输入。isLoggablel 的定义如下,

frameworks/base/core/java/android/util/Log.java
    
    /**
     * Checks to see whether or not a log for the specified tag is loggable at the specified level.
     *
     *  The default level of any tag is set to INFO. This means that any level above and including
     *  INFO will be logged. Before you make any calls to a logging method you should check to see
     *  if your tag should be logged. You can change the default level by setting a system property:
     *      'setprop log.tag.<YOUR_LOG_TAG> <LEVEL>'
     *  Where level is either VERBOSE, DEBUG, INFO, WARN, ERROR, ASSERT, or SUPPRESS. SUPPRESS will
     *  turn off all logging for your tag. You can also create a local.prop file that with the
     *  following in it:
     *      'log.tag.<YOUR_LOG_TAG>=<LEVEL>'
     *  and place that in /data/local.prop.
     *
     * @param tag The tag to check.
     * @param level The level to check.
     * @return Whether or not that this is allowed to be logged.
     * @throws IllegalArgumentException is thrown if the tag.length() > 23
     *         for Nougat (7.0) releases (API <= 23) and prior, there is no
     *         tag limit of concern after this API level.
     */
    public static native boolean isLoggable(String tag, int level);

isLoggable 在模块开发过程中非常有用,次要的应用场景为,

  • 依据以后模块的日志级别来管制模块的调试级别,依据调试级别能够控制代码流程和日志输入。
  • 获取相干模块的日志级别来管制以后模块的代码流程和日志输入。

isLoggable 比拟定义的日志级别和参数中须要测验的级别。当 level 大于等于以后日志级别时,log 输入能够记录到日志中,函数返回 true。否则返回 false。一个典型的利用如下,

frameworks/base/core/java/android/bluetooth/BluetoothMapClient.java
    
public final class BluetoothMapClient implements BluetoothProfile {

    private static final String TAG = "BluetoothMapClient";
    private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
    private static final boolean VDBG = Log.isLoggable(TAG, Log.VERBOSE);
    ......
    private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
            new IBluetoothStateChangeCallback.Stub() {public void onBluetoothStateChange(boolean up) {if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
                    if (!up) {if (VDBG) Log.d(TAG, "Unbinding service...");
    ......

上述代码中,当 log.tag.BluetoothMapClient=D 时,DBG=true; VDBG=false。这时 DBG 管制的相干 log 能够输入。当 log.tag.BluetoothMapClient=V 时,DBG=true; VDBG=trueDEBVDBG 管制的 log 都能够输入。

Native 日志输入管制

在 Android Native 中,通常应用 ALOGx() 系列函数来输入日志。ALOGx() 函数与 Log.x() 系列函数一样,也受到日志级别的影响。同样,管制日志级别的四个属性对于 ALOGx() 系列函数也同样无效。这与 Java 层的日志输入并没有区别。ALOGx() 系列函数包含,

  • ALOGV(...):输入 VERBOSE 级别的日志。
  • ALOGD(...):输入 DEBUG 级别的日志。
  • ALOGI(...):输入 INFO 级别的日志。
  • ALOGW(...):输入 WARNING 级别的日志。
  • ALOGE(...):输入 ERROR 级别的日志。

除了应用 ALOGx() 输入日志外,还能够应用 ALOGx_IF() 系列函数来打印日志。ALOGx_IF() 函数依据输出的条件来决定是否打印日志,其函数包含,

  • ALOGV_IF(cond, ...)cond 为 true 时输入 VERBOSE 级别的日志。
  • ALOGD_IF(cond, ...)cond 为 true 时输入 DEBUG 级别的日志。
  • ALOGI_IF(cond, ...)cond 为 true 时输入 INFO 级别的日志。
  • ALOGW_IF(cond, ...)cond 为 true 时输入 WARNING 级别的日志。
  • ALOGE_IF(cond, ...)cond 为 true 时输入 ERROR 级别的日志。

此外,Native 中还有一个 IF_ALOGx() 系列函数,可能起到与 isLoggable 相似的性能,用于判断需要的级别是否大于以后日志级别,从而能够管制日志输入。IF_ALOGx() 系列函数包含,

  • IF_ALOGV():当日志级别小于等于 VERBOSE 时,返回 true。
  • IF_ALOGD():当日志级别小于等于 DEBUG 时,返回 true。
  • IF_ALOGI():当日志级别小于等于 INFO 时,返回 true。
  • IF_ALOGW():当日志级别小于等于 WARNING 时,返回 true。
  • IF_ALOGE():当日志级别小于等于 ERROR 时,返回 true。

最初

具体的代码就不撸了,Android 日志波及的代码太多了,就不一点点看了,很多我也没看懂。在开发过程中可能做到正当应用日志零碎就能够给咱们带来很大的帮忙。大部分状况,咱们并不需要很分明日志零碎的实现,只须要做到充沛的利用日志零碎。切记解脱“锤子”思维。

“手中有锤子的人,把世界上的所有都看成是钉子。”—— 查理·芒格

退出移动版