💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
在前面的4.3小节中,我们介绍了Android系统运行时库层的日志库写入接口,但是在实际开发中,我们一般不会直接使用这些接口来写日志。在实际开发中,我们常常希望有些日志只在程序的调试版本中输出,而不希望它们在发布版本中输出。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系统运行时库层的一个头文件log.h中,它的位置如下所示。 ~~~ ~/Android/system/core/include ----cutils ---- log.h ~~~ 这个头文件定义了一个宏LOG_NDEBUG,用来区分程序是调试版本还是发布版本,如下所示。 **system/core/include/cutils/log.h** ~~~ /* * Normally we strip LOGV (VERBOSE messages) from release builds. * You can modify this (for example with "#define LOG_NDEBUG 0" * at the top of your source file) to change that behavior. */ #ifndef LOG_NDEBUG #ifdef NDEBUG #define LOG_NDEBUG 1 #else #define LOG_NDEBUG 0 #endif #endif ~~~ 在程序的发布版本中,宏LOG_NDEBUG定义为1,而在调试版本中定义为0。通过这个宏,我们就可以将某些日志宏在程序的发布版本中定义为空,从而限制它们在程序的发布版本中输出。 这个头文件还定义了宏LOG_TAG,用作当前编译单元的默认日志记录标签,它的定义如下所示。 **system/core/include/cutils/log.h** ~~~ /* * This is the local tag used for the following simplified * logging macros. You can change this preprocessor definition * before using the other macros to change the tag. */ #ifndef LOG_TAG #define LOG_TAG NULL #endif ~~~ 它默认定义为NULL,即没有日志记录标签。如果一个模块想要定义自己的默认日志记录标签,那么就需要使用#define指令来自定义宏LOG_TAG的值。 了解了这两个宏的定义之后,我们就开始分析这三组C/C++日志宏的实现。 **LOGV、LOGD、LOGI、LOGW和LOGE** **system/core/include/cutils/log.h** ~~~ /* * Simplified macro to send a verbose log message using the current LOG_TAG. */ #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 /* * Simplified macro to send a warning log message using the current LOG_TAG. */ #ifndef LOGW #define LOGW(...) ((void)LOG(LOG_WARN, LOG_TAG, __VA_ARGS__)) #endif /* * Simplified macro to send an error log message using the current LOG_TAG. */ #ifndef LOGE #define LOGE(...) ((void)LOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)) #endif ~~~ 这五个宏是用来写入类型为main的日志记录的,它们写入的日志记录的优先级分别为VERBOSE、DEBUG、INFO、WARN和ERROR。其中,宏LOGV只有在宏LOG_NDEBUG定义为0时,即在程序的调试版本中,才是有效的;否则,它只是一个空定义。 这五个宏是通过使用宏LOG来实现日志写入功能的,它的定义如下所示。 **system/core/include/cutils/log.h** ~~~ /* * Basic log message macro. * * Example: * LOG(LOG_WARN, NULL, "Failed with error %d", errno); * * The second argument may be NULL or "" to indicate the "global" tag. */ #ifndef LOG #define LOG(priority, tag, ...) \ LOG_PRI(ANDROID_##priority, tag, __VA_ARGS__) #endif /* * Log macro that allows you to specify a number for the priority. */ #ifndef LOG_PRI #define LOG_PRI(priority, tag, ...) \ android_printLog(priority, tag, __VA_ARGS__) #endif #define android_printLog(prio, tag, fmt...) \ __android_log_print(prio, tag, fmt) ~~~ 当宏LOG展开后,它的第一个参数priority加上前缀“ANDROID_”之后,就变成了另外一个宏LOG_PRI的第一个参数。例如,宏LOGV展开后就得到宏LOG_PRI的第一个参数为ANDROID_LOG_VERBOSE。这些形式为ANDROID_##priority的参数都是类型为android_LogPriority的枚举值,它们的定义如下所示。 **system/core/include/android/log.h** ~~~ /* * Android log priority values, in ascending priority order. */ 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; ~~~ 回到宏LOG_PRI的定义中,它最终是通过调用日志库liblog提供的函数__android_log_print向Logger日志驱动程序中写入日志记录的。函数__android_log_print的实现可以参考前面4.3小节的内容,这里不再详述。 **SLOGV、SLOGD、SLOGI、SLOGW和SLOGE** **system/core/include/cutils/log.h** ~~~ /* * Simplified macro to send a verbose system log message using the current LOG_TAG. */ #ifndef SLOGV #if LOG_NDEBUG #define SLOGV(...) ((void)0) #else #define SLOGV(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) #endif #endif /* * Simplified macro to send a debug system log message using the current LOG_TAG. */ #ifndef SLOGD #define SLOGD(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)) #endif /* * Simplified macro to send an info system log message using the current LOG_TAG. */ #ifndef SLOGI #define SLOGI(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)) #endif /* * Simplified macro to send a warning system log message using the current LOG_TAG. */ #ifndef SLOGW #define SLOGW(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)) #endif /* * Simplified macro to send an error system log message using the current LOG_TAG. */ #ifndef SLOGE #define SLOGE(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)) #endif ~~~ 这五个宏是用来写入类型为system的日志记录的,它们写入的日志记录的优先级分别为VERBOSE、DEBUG、INFO、WARN和ERROR。其中,宏SLOGV只有在宏LOG_NDEBUG定义为0时,即在程序的调试版本中,才是有效的;否则,它只是一个空定义。 这五个宏展开之后,实际上是通过调用日志库liblog提供的函数__android_log_buf_print向Logger日志驱动程序中写入日志记录的。函数__android_log_buf_print的实现可以参考前面4.3小节的内容,这里不再详述。 **LOG_EVENT_INT、LOG_EVENT_LONG和LOG_EVENT_STRING** **system/core/include/cutils/log.h** ~~~ /* * Event log entry types. These must match up with the declarations in * java/android/android/util/EventLog.java. */ typedef enum { EVENT_TYPE_INT = 0, EVENT_TYPE_LONG = 1, EVENT_TYPE_STRING = 2, EVENT_TYPE_LIST = 3, } AndroidEventLogType; #define LOG_EVENT_INT(_tag, _value) { \ int intBuf = _value; \ (void) android_btWriteLog(_tag, EVENT_TYPE_INT, &intBuf, \ sizeof(intBuf)); \ } #define LOG_EVENT_LONG(_tag, _value) { \ long long longBuf = _value; \ (void) android_btWriteLog(_tag, EVENT_TYPE_LONG, &longBuf, \ sizeof(longBuf)); \ } #define LOG_EVENT_STRING(_tag, _value) \ ((void) 0) /* not implemented -- must combine len with string */ /* TODO: something for LIST */ #define android_btWriteLog(tag, type, payload, len) \ __android_log_btwrite(tag, type, payload, len) ~~~ 这三个宏是用来写入类型为events的日志记录的。第6行到第9行首先定义了四个枚举值,它们分别用来代表一个整数(int)、长整数(long)、字符串(string)和列表(list)。前面提到,类型为events的日志记录的内容是由一系列值组成的,这些值是具有类型的,分别对应于EVENT_TYPE_INT、EVENT_TYPE_LONG、EVENT_TYPE_STRING和EVENT_TYPE_LIST四种类型。 宏LOG_EVENT_INT和LOG_EVENT_LONG写入的日志记录的内容分别是一个整数和一个长整数。它们展开之后,实际上是通过调用日志库liblog提供的函数__android_log_btwrite来往Logger日志驱动程序中写入日志记录的。函数__android_log_btwrite的实现可以参考前面4.3小节的内容,这里不再详述。 宏LOG_EVENT_STRING用来往Logger日志驱动程序中写入一条内容为字符串值的日志记录,但是在目前版本的实现中,它只是一个空定义。此外,在目前版本中,系统也没有定义一个用来写入内容为列表的日志记录宏。因此,如果需要往Logger日志驱动程序中写入一条内容为字符串或者列表的日志记录,那么就必须直接使用日志库liblog提供的函数__android_log_bwrite或者__android_log_btwrite。