C/C++日志写入接口

Logger写接口

C/C++日志写入接口

Android系统就提供了三组常用的C/C++宏来封装日志写入接口, 这些宏有的在程序的非调试版本中只是一个空定义, 因此, 可以避免在程序的发布版本中输出日志。

LOGV、 LOGD、 LOGI、 LOGW和LOGE, 它们用来写入类型为main的日志记录;

SLOGV、 SLOGD、 SLOGI、 SLOGW和SLOGE, 它们用来写入类型为system的日志记录;

LOG_EVENT_INT、 LOG_EVENT_LONG和LOG_EVENT_STRING, 它们用来写入类型为events的日志记录

目录 :

~/Android/system/core/include
    cutils
        log.h

区分程序是调试版本还是发布版本 :

// system\core\include\cutils\log.h

#ifndef LOG_NDEBUG
#ifdef NDEBUG
#define LOG_NDEBUG 1
#else
// 调试版本中定义为0
#define LOG_NDEBUG 0
#endif
#endif
// system\core\include\cutils\log.h

// 当前编译单元的默认日志记录标签
#ifndef LOG_TAG
// 没有日志记录标签
#define LOG_TAG NULL
#endif

LOGV 、 LOGD 、 LOGI 、 LOGW 和LOGE

// system\core\include\cutils\log.h

// 用来写入类型为main的日志记录
// 优先级分别为 VERBOSE、 DEBUG、 INFO、 WARN  ERROR
#ifndef LOGV
#if LOG_NDEBUG
#define LOGV(...)   ((void)0)
#else
// 调试版本
#define LOGV(...) ((void)LOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
#endif
#endif

/*
 * Simplified macro to send a debug log message using the current LOG_TAG.
 */
#ifndef LOGD
#define LOGD(...) ((void)LOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__))
#endif

/*
 * Simplified macro to send an info log message using the current LOG_TAG.
 */
#ifndef LOGI
#define LOGI(...) ((void)LOG(LOG_INFO, LOG_TAG, __VA_ARGS__))
#endif

#ifndef LOGW
#define LOGW(...) ((void)LOG(LOG_WARN, LOG_TAG, __VA_ARGS__))
#endif

#ifndef LOGE
#define LOGE(...) ((void)LOG(LOG_ERROR, LOG_TAG, __VA_ARGS__))
#endif

使用宏LOG来实现日志写入功能 :

// system\core\include\cutils\log.h

// 实现日志写入功能
#ifndef LOG

// priority加上前缀“ ANDROID_ ”
// ANDROID_# # priority的参数都是类型为android_LogPriority的枚举值
#define LOG(priority, tag, ...) \
    LOG_PRI(ANDROID_##priority, tag, __VA_ARGS__)
#endif

#ifndef LOG_PRI

// 向Logger日志驱动程序中写入日志记录
#define LOG_PRI(priority, tag, ...) \
    android_printLog(priority, tag, __VA_ARGS__)
#endif

// 向Logger日志驱动程序中写入日志记录
#define android_printLog(prio, tag, fmt...) \
    __android_log_print(prio, tag, fmt)

android_LogPriority的枚举值 :

// system\core\include\android\log.h

typedef enum android_LogPriority {
    ANDROID_LOG_UNKNOWN = 0,
    ANDROID_LOG_DEFAULT,    /* only for SetMinPriority() */
    ANDROID_LOG_VERBOSE,
    ANDROID_LOG_DEBUG,
    ANDROID_LOG_INFO,
    ANDROID_LOG_WARN,
    ANDROID_LOG_ERROR,
    ANDROID_LOG_FATAL,
    ANDROID_LOG_SILENT,     /* only for SetMinPriority(); must be last */
} android_LogPriority;

SLOGV 、 SLOGD 、 SLOGI 、 SLOGW 和SLOGE :

// system\core\include\cutils\log.h

// 用来写入类型为system的日志记录
// 写入的日志记录的优先级分别为VERBOSE、 DEBUG、 INFO、 WARN和ERROR
#ifndef SLOGV
#if LOG_NDEBUG
#define SLOGV(...)   ((void)0)
#else
// 调试版本
// 向Logger日志驱动程序中写入日志记录
#define SLOGV(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
#endif
#endif

#ifndef SLOGD
#define SLOGD(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))
#endif

#ifndef SLOGI
#define SLOGI(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__))
#endif

#ifndef SLOGW
#define SLOGW(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__))
#endif

#ifndef SLOGE
#define SLOGE(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__))
#endif

LOG_EVENT_INT 、 LOG_EVENT_LONG 和LOG_EVENT_STRING :

// system\core\include\cutils\log.h

typedef enum 
{
    // 整数
    EVENT_TYPE_INT      = 0,
    // 长整数
    EVENT_TYPE_LONG     = 1,
    // 字符串
    EVENT_TYPE_STRING   = 2,
    // 列表
    EVENT_TYPE_LIST     = 3,
} AndroidEventLogType;

// 写入类型为events的日志记录
#define LOG_EVENT_INT(_tag, _value) {                                       \
        int intBuf = _value;                                                \
        // 往Logger日志驱动程序中写入日志记录
        (void) android_btWriteLog(_tag, EVENT_TYPE_INT, &intBuf,            \
            sizeof(intBuf));                                                \
    }

#define LOG_EVENT_LONG(_tag, _value) {                                      \
        long long longBuf = _value;                                         \
        // 往Logger日志驱动程序中写入日志记录
        (void) android_btWriteLog(_tag, EVENT_TYPE_LONG, &longBuf,          \
            sizeof(longBuf));                                               \
    }

// 往Logger日志驱动程序中写入一条内容为字符串值的日志记录
#define LOG_EVENT_STRING(_tag, _value)                                      \
// 空定义
    ((void) 0)  /* not implemented -- must combine len with string */

// 向Logger日志驱动程序中写入日志记录
#define android_printLog(prio, tag, fmt...) \
    __android_log_print(prio, tag, fmt)

Java日志写入接口

Android系统在应用程序框架层中定义了三个Java日志写入接口,

android.util.Logandroid.util.Slogandroid.util.EventLog, 写入的日志记录类型分别为main、 system和events。

这三个Java日志写入接口是通过JNI方法来调用日志库liblog提供的函数来实现日志记录的写入功能的。

android.util.Log :

// frameworks\base\core\java\android\util\Log.java

public final class Log 
{

    // 优先级分别为VERBOSE、 DEBUG、 INFO、 WARN  ERROR
    /**
     * Priority constant for the println method; use Log.v.
     */
    public static final int VERBOSE = 2;

    /**
     * Priority constant for the println method; use Log.d.
     */
    public static final int DEBUG = 3;

    /**
     * Priority constant for the println method; use Log.i.
     */
    public static final int INFO = 4;

    /**
     * Priority constant for the println method; use Log.w.
     */
    public static final int WARN = 5;

    /**
     * Priority constant for the println method; use Log.e.
     */
    public static final int ERROR = 6;

    /**
     * Priority constant for the println method.
     */
    public static final int ASSERT = 7;

    //...

    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 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));
    }

    //...

    /** @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 native int println_native(int bufID,
            int priority, String tag, String msg);
}
// frameworks\base\core\jni\android_util_Log.cpp

static jint android_util_Log_println_native(JNIEnv* env, jobject clazz,
        jint bufID, jint priority, jstring tagObj, jstring msgObj)
{
    const char* tag = NULL;
    const char* msg = NULL;

    // 检查写入的日志记录的内容msgObj是否为null
    if (msgObj == NULL) 
    {
        jclass npeClazz;

        npeClazz = env->FindClass("java/lang/NullPointerException");
        assert(npeClazz != NULL);

        env->ThrowNew(npeClazz, "println needs a message");
        return -1;
    }

    // 0、 1、 2和3四个值表示的日志记录的类型分别为main、radio、 events和system

    // 检查写入的日志记录的类型值是否位于0和LOG_ID_MAX之间
    if (bufID < 0 || bufID >= LOG_ID_MAX) 
    {
        jclass npeClazz;

        npeClazz = env->FindClass("java/lang/NullPointerException");
        assert(npeClazz != NULL);

        env->ThrowNew(npeClazz, "bad bufID");
        return -1;
    }

    if (tagObj != NULL)
        tag = env->GetStringUTFChars(tagObj, NULL);
    msg = env->GetStringUTFChars(msgObj, NULL);

    // 往Logger日志驱动程序中写入日志记录
    int res = __android_log_buf_write(bufID, (android_LogPriority)priority, tag, msg);

    if (tag != NULL)
        env->ReleaseStringUTFChars(tagObj, tag);
    env->ReleaseStringUTFChars(msgObj, msg);

    return res;
}

android.util.Slog :

// frameworks\base\core\java\android\util\Slog.java

// 注释中有一个“@hide”关键字, 表示这是一个隐藏接口,
//只在系统内部使用, 应用程序一般不应该使用该接口来写入日志记录
/**
 * @hide
 */
public final class Slog 
{
    // 写入的日志记录的类型为system

    private Slog() {
    }

    // 成员函数有v、 d、 i、w和e
    // 优先级分别为VERBOSE、 DEBUG、 INFO、 WARN和ERROR

    public static int v(String tag, String msg) {
        return Log.println_native(Log.LOG_ID_SYSTEM, Log.VERBOSE, tag, msg);
    }

    public static int v(String tag, String msg, Throwable tr) {
        return Log.println_native(Log.LOG_ID_SYSTEM, Log.VERBOSE, tag,
                msg + '\n' + Log.getStackTraceString(tr));
    }

    public static int d(String tag, String msg) {
        return Log.println_native(Log.LOG_ID_SYSTEM, Log.DEBUG, tag, msg);
    }

    public static int d(String tag, String msg, Throwable tr) {
        return Log.println_native(Log.LOG_ID_SYSTEM, Log.DEBUG, tag,
                msg + '\n' + Log.getStackTraceString(tr));
    }

    public static int i(String tag, String msg) {
        return Log.println_native(Log.LOG_ID_SYSTEM, Log.INFO, tag, msg);
    }

    public static int i(String tag, String msg, Throwable tr) {
        return Log.println_native(Log.LOG_ID_SYSTEM, Log.INFO, tag,
                msg + '\n' + Log.getStackTraceString(tr));
    }

    public static int w(String tag, String msg) {
        return Log.println_native(Log.LOG_ID_SYSTEM, Log.WARN, tag, msg);
    }

    public static int w(String tag, String msg, Throwable tr) {
        return Log.println_native(Log.LOG_ID_SYSTEM, Log.WARN, tag,
                msg + '\n' + Log.getStackTraceString(tr));
    }

    public static int w(String tag, Throwable tr) {
        return Log.println_native(Log.LOG_ID_SYSTEM, Log.WARN, tag, Log.getStackTraceString(tr));
    }

    public static int e(String tag, String msg) {
        return Log.println_native(Log.LOG_ID_SYSTEM, Log.ERROR, tag, msg);
    }

    public static int e(String tag, String msg, Throwable tr) {
        return Log.println_native(Log.LOG_ID_SYSTEM, Log.ERROR, tag,
                msg + '\n' + Log.getStackTraceString(tr));
    }

    public static int println(int priority, String tag, String msg) {
        return Log.println_native(Log.LOG_ID_SYSTEM, priority, tag, msg);
    }
}

android.util.EventLog :

// frameworks\base\core\java\android\util\EventLog.java

public class EventLog 
{
    //...
    // 向Logger日志驱动程序中写入类型为events的日志记录, 
    // 这些日志记录的内容分别为整数、 长整数、 字符串和列表。

    // 整数
    public static native int writeEvent(int tag, int value);

    // 长整数
    public static native int writeEvent(int tag, long value);

    // 字符串
    public static native int writeEvent(int tag, String str);

    // 列表
    public static native int writeEvent(int tag, Object... list);

    //...
}

写入整数长整数类型日志记录的JNI方法writeEvent的实现 :

//frameworks\base\core\jni\android_util_EventLog.cpp

static jint android_util_EventLog_writeEvent_Integer(JNIEnv* env, jobject clazz,
                                                     jint tag, jint value)
{
    // 向Logger日志驱动程序中写入日志记录
    // EVENT_TYPE_INT : 要写入的日志记录的内容为一个整数
    return android_btWriteLog(tag, EVENT_TYPE_INT, &value, sizeof(value));
}

static jint android_util_EventLog_writeEvent_Long(JNIEnv* env, jobject clazz,
                                                  jint tag, jlong value)
{
    // 向Logger日志驱动程序中写入日志记录
    // EVENT_TYPE_LONG :  要写入的日志记录的内容为一个长整数
    return android_btWriteLog(tag, EVENT_TYPE_LONG, &value, sizeof(value));
}

整数的日志记录的内存布局 :

image-20200720151030266

长整数的日志记录的内存布局 :

image-20200720151039785

宏android_btWriteLog的定义 :

// system\core\include\cutils\log.h

// 往Logger日志驱动程序中写入日志记录
#define android_btWriteLog(tag, type, payload, len) \
    __android_log_btwrite(tag, type, payload, len)

写入字符串类型日志记录的JNI方法writeEvent的实现 :

static jint android_util_EventLog_writeEvent_String(JNIEnv* env, jobject clazz,
                                   jint tag, jstring value) 
{
    uint8_t buf[MAX_EVENT_PAYLOAD];

    // Don't throw NPE -- I feel like it's sort of mean for a logging function
    // to be all crashy if you pass in NULL -- but make the NULL value explicit.
    const char *str = value != NULL ? env->GetStringUTFChars(value, NULL) : "NULL";
    jint len = strlen(str);
    const int max = sizeof(buf) - sizeof(len) - 2;  // Type byte, final newline
    if (len > max) 
    {
        len = max;
    }

    // 日志记录内容的类型为字符串
    buf[0] = EVENT_TYPE_STRING;
    // 字符串的长度
    memcpy(&buf[1], &len, sizeof(len));
    // 字符串内容
    memcpy(&buf[1 + sizeof(len)], str, len);
    // ‘\n'来结束该日志记录
    buf[1 + sizeof(len) + len] = '\n';

    if (value != NULL) env->ReleaseStringUTFChars(value, str);

    // 将日志记录写入到Logger日志驱动程序
    return android_bWriteLog(tag, buf, 2 + sizeof(len) + len);
}

内容为字符串的日志记录的内存布局 :

image-20200720151051111

第一个字段记录日志记录内容的类型为字符串,

第二个字段描述该字符串的长度,

第三个字段保存的是字符串内容,

第四个字段使用特殊字符‘\n’来结束该日志记录

写入列表类型日志记录的JNI方法writeEvent的实现 :

static jint android_util_EventLog_writeEvent_Array(JNIEnv* env, jobject clazz,
                                  jint tag, jobjectArray value) 
{
    if (value == NULL) 
    {
        return android_util_EventLog_writeEvent_String(env, clazz, tag, NULL);
    }

    uint8_t buf[MAX_EVENT_PAYLOAD];
    const size_t max = sizeof(buf) - 1;  // leave room for final newline
    size_t pos = 2;  // Save room for type tag & array count

    // 一个列表中最多可以包含255个元素
    jsize copied = 0, num = env->GetArrayLength(value);
    for (; copied < num && copied < 255; ++copied) 
    {
        // 依次取出列表中的元素, 且根据它们的值类型来组织缓冲区buf的内存布局
        jobject item = env->GetObjectArrayElement(value, copied);

        if (item == NULL || env->IsInstanceOf(item, gStringClass)) 
        {
            // 值类型为字符串
            if (pos + 1 + sizeof(jint) > max) 
                break;
            const char *str = item != NULL ? env->GetStringUTFChars((jstring) item, NULL) : "NULL";
            jint len = strlen(str);
            if (pos + 1 + sizeof(len) + len > max) 
                len = max - pos - 1 - sizeof(len);

            // EVENT_TYPE_STRING值
            buf[pos++] = EVENT_TYPE_STRING;
            // 字符串长度值
            memcpy(&buf[pos], &len, sizeof(len));
            // 字符串的内容
            memcpy(&buf[pos + sizeof(len)], str, len);

            pos += sizeof(len) + len;
            if (item != NULL) 
                env->ReleaseStringUTFChars((jstring) item, str);
        } 
        else if (env->IsInstanceOf(item, gIntegerClass)) 
        {
            // 值类型为整数

            jint intVal = env->GetIntField(item, gIntegerValueID);
            if (pos + 1 + sizeof(intVal) > max) 
                break;

            // 第一个字节设置为一个EVENT_TYPE_INT值
            buf[pos++] = EVENT_TYPE_INT;
            // 整数值
            memcpy(&buf[pos], &intVal, sizeof(intVal));
            pos += sizeof(intVal);
        } 
        else if (env->IsInstanceOf(item, gLongClass)) 
        {
            // 值类型为长整数

            jlong longVal = env->GetLongField(item, gLongValueID);
            if (pos + 1 + sizeof(longVal) > max) 
                break;

            // buf的内容就为一个EVENT_TYPE_LONG值
            buf[pos++] = EVENT_TYPE_LONG;
            // 长整数值
            memcpy(&buf[pos], &longVal, sizeof(longVal));
            pos += sizeof(longVal);
        } 
        else 
        {
            // 抛出一个异常

            jniThrowException(env,
                    "java/lang/IllegalArgumentException",
                    "Invalid payload item type");
            return -1;
        }
        env->DeleteLocalRef(item);
    }

    // 列表类型的日志记录
    buf[0] = EVENT_TYPE_LIST;
    // 列表元素个数
    buf[1] = copied;
    // 日志记录的结束标志
    buf[pos++] = '\n';
    // 将日志记录写入到Logger日志驱动程序中
    return android_bWriteLog(tag, buf, pos);
}

内容为列表的日志记录的内存布局 :

image-20200720151100719

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

onnx

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值