0. Overview
蓝牙模块出现多种类型的Log:
- ALOGV(....);
- LOG_<log_level>, 如:LOG_VERBOSE(...);
- <module>_TRACE_<log_level>,如 APPL_TRACE_DEBUG(...);
- LOG(log_level) << ...;
- VLOG(log_level) << ...;
1. ALOGV
位于 liblog.so 库,引入 #include "utils/Log.h"
或#include <log/log.h>
头文件,即表明使用本类型的Log。
详见 Android Log - ALOGV
蓝牙模块仅 JNI 层使用了 ALOGV。
ALOGD、ALOGI、ALOGW、ALOGE 默认开启。
2. LOG_VERBOSE 原理
LOG_INFO, LOG_WARN, LOG_ERROR 默认开启。
LOG_VEROBSE 、LOG_DEBUG 2个需要配置开启
基于 liblog.so 库,自定义实现。
2.1 LOG_VEROBSE / LOG_DEBUG 实现
#pragma once
#include <cstdlib>
#ifndef LOG_TAG
#define LOG_TAG "bluetooth"
#endif
#if defined(OS_ANDROID) // 1 OS_ANDROID
#include <log/log.h>
#include "common/init_flags.h"
#ifdef FUZZ_TARGET // 2 FUZZ_TARGET - if
#define LOG_VERBOSE(...)
#define LOG_DEBUG(...)
#define LOG_INFO(...)
#define LOG_WARN(...)
#else // 2 FUZZ_TARGET - else
#define LOG_VERBOSE(fmt, args...) \
do { \
if (bluetooth::common::InitFlags::IsDebugLoggingEnabledForTag(LOG_TAG)) { \
ALOGV("%s:%d %s: " fmt, __FILE__, __LINE__, __func__, ##args); \
} \
} while (false)
#define LOG_DEBUG(fmt, args...) \
do { \
if (bluetooth::common::InitFlags::IsDebugLoggingEnabledForTag(LOG_TAG)) { \
ALOGD("%s:%d %s: " fmt, __FILE__, __LINE__, __func__, ##args); \
} \
} while (false)
#define LOG_INFO(fmt, args...) ALOGI("%s:%d %s: " fmt, __FILE__, __LINE__, __func__, ##args)
#define LOG_WARN(fmt, args...) ALOGW("%s:%d %s: " fmt, __FILE__, __LINE__, __func__, ##args)
#endif // 2 FUZZ_TARGET - end
#define LOG_ERROR(fmt, args...) ALOGE("%s:%d %s: " fmt, __FILE__, __LINE__, __func__, ##args)
#else // 1
......
#endif /* defined(OS_ANDROID) */ // 1
- 首先,由 // 2 可知:若定义了 FUZZ_TARGET ,那么 LOG_VERBOSE 会被关闭,因此这里不能定义 FUZZ_TARGET;
- 其次,由 LOG_VERBOSE 定义可知,其由 ALOGV 实现,因此必须保证 ALOGV 已经开启,否则 LOG_VERBOSE 不会打印输出。开启方式可以参考Android Log - ALOGV。
- 再次,LOG_VERBOSE(LOG_DEBUG) 实现还依赖于 IsDebugLoggingvEnabledForTag(Android 12 开始出现,此版本之前没有这个函数去判断),下面分析该函数,其参数的 LOG_TAG 是 bluetooth。
- 最后,LOG_DEBUG 与 LOG_VERBOSE 的实现以及log level 控制基本相同,不同之处在于其依赖 ALOGD(默认开启,无需单独开启),不再赘述。
2.2 IsDebugLoggingvEnabledForTag 实现
分析 IsDebugLoggingvEnabledForTag 的调用流程,是分析其初始化的逆向,因此这里直接分析初始化流程,最终调用到 IsDebugLoggingvEnabledForTag 函数。
2.2.1 IsDebugLoggingvEnabledForTag 初始化流程
packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterService.java
public class AdapterService extends Service {
......
@Override
public void onCreate() {
......
initNative(mUserManager.isGuestUser(), isCommonCriteriaMode(), configCompareResult,
getInitFlags(), isAtvDevice);
......
}
- initNative(...) 将 getInitFlags() 传递到 Native 层,故先分析 getInitFlags() 。
// Boolean flags
private static final String GD_CORE_FLAG = "INIT_gd_core";
private static final String GD_ADVERTISING_FLAG = "INIT_gd_advertising";
private static final String GD_SCANNING_FLAG = "INIT_gd_scanning";
private static final String GD_HCI_FLAG = "INIT_gd_hci";
private static final String GD_CONTROLLER_FLAG = "INIT_gd_controller";
private static final String GD_ACL_FLAG = "INIT_gd_acl";
private static final String GD_L2CAP_FLAG = "INIT_gd_l2cap";
private static final String GD_RUST_FLAG = "INIT_gd_rust";
private static final String GD_LINK_POLICY_FLAG = "INIT_gd_link_policy";
private static final String BTAA_HCI_LOG_FLAG = "INIT_btaa_hci";
private static final String LOGGING_DEBUG_ENABLED_FOR_ALL_FLAG =
"INIT_logging_debug_enabled_for_all";
// String flags
// Comma separated tags
private static final String LOGGING_DEBUG_ENABLED_FOR_TAGS_FLAG =
"INIT_logging_debug_enabled_for_tags";
private static final String LOGGING_DEBUG_DISABLED_FOR_TAGS_FLAG =
"INIT_logging_debug_disabled_for_tags";
private String[] getInitFlags() {
ArrayList<String> initFlags = new ArrayList<>();
if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_BLUETOOTH, GD_CORE_FLAG, false)) {
initFlags.add(String.format("%s=%s", GD_CORE_FLAG, "true"));
}
......
if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_BLUETOOTH,
LOGGING_DEBUG_ENABLED_FOR_ALL_FLAG, false)) {
initFlags.add(String.format("%s=%s", LOGGING_DEBUG_ENABLED_FOR_ALL_FLAG, "true"));
}
// Enabled list 由 ContentProvider 提供
String debugLoggingEnabledTags = DeviceConfig.getString(DeviceConfig.NAMESPACE_BLUETOOTH,
LOGGING_DEBUG_ENABLED_FOR_TAGS_FLAG, "");
if (!debugLoggingEnabledTags.isEmpty()) {
initFlags.add(String.format("%s=%s", LOGGING_DEBUG_ENABLED_FOR_TAGS_FLAG,
debugLoggingEnabledTags));
}
// Disabled list 由 ContentProvider 提供
String debugLoggingDisabledTags = DeviceConfig.getString(DeviceConfig.NAMESPACE_BLUETOOTH,
LOGGING_DEBUG_DISABLED_FOR_TAGS_FLAG, "");
if (!debugLoggingDisabledTags.isEmpty()) {
initFlags.add(String.format("%s=%s", LOGGING_DEBUG_DISABLED_FOR_TAGS_FLAG,
debugLoggingDisabledTags));
}
......
return initFlags.toArray(new String[0]);
}
- getInitFlags() 主要获取一个 key-value 的键值对的集合,该键值对决定 Log 的开关与否。
- 以上共有 10 个具体tag,2个组控制的tag,1个全局默认tag,它们的生效优先级关系:具体tag已设置,则以之为准;否则,查看组控制tag,若组控制tag未设置,则读取全局默认值。
- DeviceConfig.getXxx() 说明该值由 ContentProvider 决定,具体值是多少、如何设置,后文(2.2.2)分析,这里先分析初始化流程。
static bool initNative(JNIEnv* env, jobject obj, jboolean isGuest,
jboolean isCommonCriteriaMode, int configCompareResult,
jobjectArray initFlags, jboolean isAtvDevice) {
......
int flagCount = env->GetArrayLength(initFlags);
jstring* flagObjs = new jstring[flagCount];
const char** flags = nullptr;
if (flagCount > 0) {
flags = new const char*[flagCount + 1];
flags[flagCount] = nullptr;
}
for (int i = 0; i < flagCount; i++) {
flagObjs[i] = (jstring)env->GetObjectArrayElement(initFlags, i);
flags[i] = env->GetStringUTFChars(flagObjs[i], NULL);
}
int ret = sBluetoothInterface->init(
&sBluetoothCallbacks, isGuest == JNI_TRUE ? 1 : 0,
isCommonCriteriaMode == JNI_TRUE ? 1 : 0, configCompareResult, flags,
isAtvDevice == JNI_TRUE ? 1 : 0);
......
return JNI_TRUE;
}
system/bt/btif/src/bluetooth.cc
static int init(bt_callbacks_t* callbacks, bool start_restricted,
bool is_common_criteria_mode, int config_compare_result,
const char** init_flags, bool is_atv) {
......
bluetooth::common::InitFlags::Load(init_flags);
......
return BT_STATUS_SUCCESS;
}
system/bt/gd/common/init_flags.h
#pragma once
#include <stdexcept>
#include <string>
#include <unordered_map>
#include "src/init_flags.rs.h"
namespace bluetooth {
namespace common {
class InitFlags final {
public:
static void Load(const char** flags);
// 这里要求 tag 在列表中且值为 true,或 debug 全局开关已经打开
inline static bool IsDebugLoggingEnabledForTag(const std::string& tag) {
auto tag_setting = logging_debug_explicit_tag_settings.find(tag);
if (tag_setting != logging_debug_explicit_tag_settings.end()) {
return tag_setting->second;
}
return logging_debug_enabled_for_all;
}
......
private:
......
static bool logging_debug_enabled_for_all;
// save both log allow list and block list in the map to save hashing time
static std::unordered_map<std::string, bool> logging_debug_explicit_tag_settings;
};
} // namespace common
} // namespace bluetooth
- 由 IsDebugLoggingEnabledForTag(...) 函数可知,Log开关的开启有两个要求,满足一个要求即可:
- 指定的 tag 在显示指定了要开启的 LOG_TAG 列表(或);
- 不在指定列表中,但 debug 全局开关已经打开(或)。
优先级关系:已设置具体tag开关,则以设置为准;未单独设置,则以默认值为准。
system/bt/gd/common/init_flags.cc
namespace bluetooth {
namespace common {
// 默认定义为 false,因此默认的 LOG_VORBOSE 处于关闭状态
bool InitFlags::logging_debug_enabled_for_all = false;
std::unordered_map<std::string, bool> InitFlags::logging_debug_explicit_tag_settings = {};
void InitFlags::Load(const char** flags) {
const char** flags_copy = flags;
// 重置
SetAll(false);
// 对所有的 key-value 字符串进行循环处理。
while (flags != nullptr && *flags != nullptr) {
// 以 = 切割字符串
std::string flag_element = *flags;
auto flag_pair = StringSplit(flag_element, "=", 2);
if (flag_pair.size() != 2) {
flags++;
continue;
}
// 在列表中找到全局 key,并对其赋值
ParseBoolFlag(flag_pair, "INIT_logging_debug_enabled_for_all", &logging_debug_enabled_for_all);
// 在列表中找到 enabled key,将 tag 和 true 添加到集合中;
if ("INIT_logging_debug_enabled_for_tags" == flag_pair[0]) {
auto tags = StringSplit(flag_pair[1], ",");
for (const auto& tag : tags) {
auto setting = logging_debug_explicit_tag_settings.find(tag);
if (setting == logging_debug_explicit_tag_settings.end()) {
logging_debug_explicit_tag_settings.insert_or_assign(tag, true);
}
}
}
// 在列表中找到 disabled key,将 tag 和 false 添加到集合中;
if ("INIT_logging_debug_disabled_for_tags" == flag_pair[0]) {
auto tags = StringSplit(flag_pair[1], ",");
for (const auto& tag : tags) {
logging_debug_explicit_tag_settings.insert_or_assign(tag, false);
}
}
flags++;
}
......
}
void InitFlags::SetAll(bool value) {
logging_debug_enabled_for_all = value;
logging_debug_explicit_tag_settings.clear();
}
- 由以上代码分析可知:logging_debug_enabled_for_all 、logging_debug_explicit_tag_settings 由 AdapterService.java 中传入的 key-value 字符串决定,key 集合前文已述(具体名称、解析优先级等),后续分析 value 从何而来
2.2.2 DeviceConfig.getXxx(...)
frameworks/base/core/java/android/provider/DeviceConfig.java
@SystemApi
@RequiresPermission(READ_DEVICE_CONFIG)
public static boolean getBoolean(@NonNull String namespace, @NonNull String name,
boolean defaultValue) {
String value = getProperty(namespace, name);
return value != null ? Boolean.parseBoolean(value) : defaultValue;
}
@SystemApi
@RequiresPermission(READ_DEVICE_CONFIG)
public static String getString(@NonNull String namespace, @NonNull String name,
@Nullable String defaultValue) {
String value = getProperty(namespace, name);
return value != null ? value : defaultValue;
}
@SystemApi
@RequiresPermission(READ_DEVICE_CONFIG)
public static String getProperty(@NonNull String namespace, @NonNull String name) {
return getProperties(namespace, name).getString(name, null);
}
由前文读取该值的源码可知,蓝牙模块的 namespace 是 "bluetooth"。
这里读写的数据库是 device_config,因此可以通过如下命令控制该log
adb shell device_config put bluetooth INIT_gd_core true
adb shell device_config put bluetooth INIT_gd_advertising true
adb shell device_config put bluetooth INIT_gd_scanning true
adb shell device_config put bluetooth INIT_gd_hci true
adb shell device_config put bluetooth INIT_gd_controller true
adb shell device_config put bluetooth INIT_gd_acl true
adb shell device_config put bluetooth INIT_gd_l2cap true
adb shell device_config put bluetooth INIT_gd_rust true
adb shell device_config put bluetooth INIT_gd_link_policy true
adb shell device_config put bluetooth INIT_btaa_hci true
// 未单独设置的tag,则使用 all 属性
adb shell device_config put bluetooth INIT_logging_debug_enabled_for_all true
// 单独关闭或开启log 的tag,优先级高于 all,低于单独设置
//adb shell device_config put bluetooth INIT_logging_debug_enabled_for_tags tags
//adb shell device_config put bluetooth INIT_logging_debug_disabled_for_tags tags
3. APPL_TRACE_DEBUG 原理
XXX_TRACE_WARNING、XXX_TRACE_ERROR 默认开启。
XXX_TRACE_VERBOSE、XXX_TRACE_DEBUG、XXX_TRACE_EVENT、XXX_TRACE_API 4个需要配置开启。
基于 liblog.so 库,自定义实现。
3.1 实现
system/bt/internal_include/bt_trace.h
/* Define trace levels */
typedef enum {
BT_TRACE_LEVEL_NONE = 0, /* No trace messages to be generated */
BT_TRACE_LEVEL_ERROR = 1, /* Error condition trace messages */
BT_TRACE_LEVEL_WARNING = 2, /* Warning condition trace messages */
BT_TRACE_LEVEL_API = 3, /* API traces */
BT_TRACE_LEVEL_EVENT = 4, /* Debug messages for events */
BT_TRACE_LEVEL_DEBUG = 5, /* Full debug messages */
BT_TRACE_LEVEL_VERBOSE = 6, /* Verbose debug messages */
} tLEGACY_TRACE_LEVEL;
#define TRACE_TYPE_MASK 0x000000ff
#define TRACE_GET_TYPE(x) (((uint32_t)(x)) & TRACE_TYPE_MASK)
#define TRACE_TYPE_ERROR 0x00000000
#define TRACE_TYPE_WARNING 0x00000001
#define TRACE_TYPE_API 0x00000002
#define TRACE_TYPE_EVENT 0x00000003
#define TRACE_TYPE_DEBUG 0x00000004
// 初始 level 是 WARNING
#ifndef APPL_INITIAL_TRACE_LEVEL
#define APPL_INITIAL_TRACE_LEVEL BT_TRACE_LEVEL_WARNING
#endif
/* define traces for application */
......
#define APPL_TRACE_EVENT(...) \
{ \
if (appl_trace_level >= BT_TRACE_LEVEL_EVENT) \
LogMsg(TRACE_CTRL_GENERAL | TRACE_LAYER_NONE | TRACE_ORG_APPL | \
TRACE_TYPE_EVENT, \
##__VA_ARGS__); \
}
#define APPL_TRACE_DEBUG(...) \
{ \
if (appl_trace_level >= BT_TRACE_LEVEL_DEBUG) \
LogMsg(TRACE_CTRL_GENERAL | TRACE_LAYER_NONE | TRACE_ORG_APPL | \
TRACE_TYPE_DEBUG, \
##__VA_ARGS__); \
}
#define APPL_TRACE_VERBOSE(...) \
{ \
if (appl_trace_level >= BT_TRACE_LEVEL_VERBOSE) \
LogMsg(TRACE_CTRL_GENERAL | TRACE_LAYER_NONE | TRACE_ORG_APPL | \
TRACE_TYPE_DEBUG, \
##__VA_ARGS__); \
}
extern uint8_t appl_trace_level;
void LogMsg(uint32_t trace_set_mask, const char* fmt_str, ...);
void LogMsg(uint32_t trace_set_mask, const char* fmt_str, ...) {
char buffer[BTE_LOG_BUF_SIZE];
int trace_layer = TRACE_GET_LAYER(trace_set_mask); // 获取 layer,用于后文选择tag
if (trace_layer >= TRACE_LAYER_MAX_NUM) trace_layer = 0;
va_list ap;
va_start(ap, fmt_str);
vsnprintf(&buffer[MSG_BUFFER_OFFSET], BTE_LOG_MAX_SIZE, fmt_str, ap);
va_end(ap);
#undef LOG_TAG
#define LOG_TAG bt_layer_tags[trace_layer]
switch (TRACE_GET_TYPE(trace_set_mask)) { // 获取前文设置的 log_level
case TRACE_TYPE_ERROR:
LOG_ERROR("%s", buffer);
break;
case TRACE_TYPE_WARNING:
LOG_WARN("%s", buffer);
break;
case TRACE_TYPE_API:
case TRACE_TYPE_EVENT:
LOG_INFO("%s", buffer);
break;
case TRACE_TYPE_DEBUG:
LOG_INFO("%s", buffer);
break;
default:
/* we should never get this */
LOG_ERROR("!BAD TRACE TYPE! %s", buffer);
CHECK(TRACE_GET_TYPE(trace_set_mask) == TRACE_TYPE_ERROR);
break;
}
#undef LOG_TAG
#define LOG_TAG "bt_bte"
}
从以上内容可知:
APPL_TRACE_DEBUG 最终由 LOG_INFO (该level默认开启)实现,因此 APPL_TRACE_DEBUG 只需要关注自身设置的 log level 即可。
该结论适用于 <module>_TRACE_<log_level> 所有module和 log_level。
3.2 Log level 实现
static void load_levels_from_config(const config_t* config) {
CHECK(config != NULL);
for (tBTTRC_FUNC_MAP* functions = &bttrc_set_level_map[0];
functions->trc_name; ++functions) {
int value = config_get_int(*config, CONFIG_DEFAULT_SECTION,
functions->trc_name, -1);
if (value != -1) {
functions->trace_level = value;
}
if (bluetooth::common::InitFlags::IsDebugLoggingEnabledForAll()) {
LOG_INFO("Enable logging for %s because all debug logs are enabled",
functions->trc_name);
functions->trace_level = BT_TRACE_LEVEL_VERBOSE;
}
LOG_INFO("BTE_InitTraceLevels -- %s : Level %d", functions->trc_name,
functions->trace_level);
if (functions->p_f) functions->p_f(functions->trace_level);
}
}
此处定义的所有模块,可以由 bt_stack.conf 文件来设置 log level,还可以通过前文的IsDebugLoggingEnabledForAll 函数来开启,若后者开启,则定义的所有模块打开 VERBOSE。
定义的模块定义如下(部分定义的模块不在此处,因此不能通过这里开启log):
static tBTTRC_FUNC_MAP bttrc_set_level_map[] = {
{BTTRC_ID_STK_BTU, BTTRC_ID_STK_HCI, BTU_SetTraceLevel, "TRC_HCI",
DEFAULT_CONF_TRACE_LEVEL},
{BTTRC_ID_STK_L2CAP, BTTRC_ID_STK_L2CAP, L2CA_SetTraceLevel, "TRC_L2CAP",
DEFAULT_CONF_TRACE_LEVEL},
{BTTRC_ID_STK_RFCOMM, BTTRC_ID_STK_RFCOMM_DATA, PORT_SetTraceLevel,
"TRC_RFCOMM", DEFAULT_CONF_TRACE_LEVEL},
{BTTRC_ID_STK_AVDT, BTTRC_ID_STK_AVDT, AVDT_SetTraceLevel, "TRC_AVDT",
DEFAULT_CONF_TRACE_LEVEL},
{BTTRC_ID_STK_AVRC, BTTRC_ID_STK_AVRC, AVRC_SetTraceLevel, "TRC_AVRC",
DEFAULT_CONF_TRACE_LEVEL},
{BTTRC_ID_STK_A2DP, BTTRC_ID_STK_A2DP, A2DP_SetTraceLevel, "TRC_A2D",
DEFAULT_CONF_TRACE_LEVEL},
#if (BNEP_INCLUDED == TRUE)
{BTTRC_ID_STK_BNEP, BTTRC_ID_STK_BNEP, BNEP_SetTraceLevel, "TRC_BNEP",
DEFAULT_CONF_TRACE_LEVEL},
#endif
{BTTRC_ID_STK_BTM_ACL, BTTRC_ID_STK_BTM_SEC, BTM_SetTraceLevel, "TRC_BTM",
DEFAULT_CONF_TRACE_LEVEL},
#if (HID_HOST_INCLUDED == TRUE)
{BTTRC_ID_STK_HID, BTTRC_ID_STK_HID, HID_HostSetTraceLevel, "TRC_HID_HOST",
DEFAULT_CONF_TRACE_LEVEL},
#endif
#if (PAN_INCLUDED == TRUE)
{BTTRC_ID_STK_PAN, BTTRC_ID_STK_PAN, PAN_SetTraceLevel, "TRC_PAN",
DEFAULT_CONF_TRACE_LEVEL},
#endif
{BTTRC_ID_STK_SDP, BTTRC_ID_STK_SDP, SDP_SetTraceLevel, "TRC_SDP",
DEFAULT_CONF_TRACE_LEVEL},
{BTTRC_ID_STK_SMP, BTTRC_ID_STK_SMP, SMP_SetTraceLevel, "TRC_SMP",
DEFAULT_CONF_TRACE_LEVEL},
#if (HID_DEV_INCLUDED == TRUE)
{BTTRC_ID_STK_HIDD, BTTRC_ID_STK_HIDD, HID_DevSetTraceLevel, "TRC_HID_DEV",
DEFAULT_CONF_TRACE_LEVEL},
#endif
/* LayerIDs for BTA, currently everything maps onto appl_trace_level.
*/
{BTTRC_ID_BTA_ACC, BTTRC_ID_BTAPP, BTAPP_SetTraceLevel, "TRC_BTAPP",
DEFAULT_CONF_TRACE_LEVEL},
{BTTRC_ID_BTA_ACC, BTTRC_ID_BTAPP, BTIF_SetTraceLevel, "TRC_BTIF",
DEFAULT_CONF_TRACE_LEVEL},
{0, 0, NULL, NULL, DEFAULT_CONF_TRACE_LEVEL}};
可根据模块、批量开启log或设置成某个 log level。bt_stack.conf 配置如下:
TRC_BTM=6
TRC_HCI=6
TRC_L2CAP=6
TRC_RFCOMM=6
TRC_OBEX=6
TRC_AVCT=6
TRC_AVDT=6
TRC_AVRC=6
TRC_AVDT_SCB=6
TRC_AVDT_CCB=6
TRC_A2D=6
TRC_SDP=6
TRC_SMP=6
TRC_BTAPP=6
TRC_BTIF=6
TRC_BNEP=6
TRC_PAN=6
TRC_HID_HOST=6
TRC_HID_DEV=6
4. LOG(log_level)
位于 libbase.so (基于 liblog.so),引入 #include <android-base/logging.h>
即表明使用本类型的Log。
LOG(INFO)、LOG(WARNING)、LOG(ERROR) 默认开启。
LOG(DEBUG)、LOG(VERBOSE) 2个需要配置开启
此处不详细分析
system/libbase/include/android-base/logging.h
enum LogSeverity {
VERBOSE,
DEBUG,
INFO,
WARNING,
ERROR,
......
};
- 一般情况下,绝大多数的设备过滤器会过滤掉 VERBOSE 级别的log;
可以通过adb shell setprop log.tag.<TAG> V
命令打开指定 TAG 的 VERBOSE log。 - 一般情况下,该进程名称(adb shell ps -A 可以查看进程名称)作为 TAG;
可以通过定义 LOG_TAG 宏#define LOG_TAG xxx
来定义 TAG
可以自定义log写到何处(如 logcat、stderr、dmesg 等),不赘述。
5. VLOG(log_level)
位于 libchrome.so(基于 liblog.so),引入 #include <base/logging.h>
即表明使用本类型的Log。
此处不详细分析
external/libchrome/base/logging.h
typedef int LogSeverity;
const LogSeverity LOG_VERBOSE = -1; // 用于 VLOG 模式
const LogSeverity LOG_INFO = 0;
const LogSeverity LOG_WARNING = 1;
const LogSeverity LOG_ERROR = 2;
const LogSeverity LOG_FATAL = 3;
......
#include <base/logging.h>
有三种 LOG 模式:
常规Log模式:LOG(INFO),LOG(WARNING)、LOG(ERROR) 、LOG(FATAL ),默认都是开启的,无需额外配置;
DLOG 模式:Bluetooth没有用到,不分析。
-
VLOG 模式:verbose logging 模式
从头文件注释可知,verbose logging 的宏定义形如:
VLOG(1) << ... ;
VLOG(2) << ... ;
verbose logging 可以按照模块打开log,
例如:--vmodule=profile=2,icon_loader=1,browser_*=3,*/chromeos/*=4 --v=0
的行为:- profile.h 或 profile.cc 将打印输出 VLOG(2) 及级别更低的log;
- icon-loader.h 或 icon-loader.cc 将打印输出 VLOG(1)及级别更低的log;
- 将 browser 为前缀的文件打印输出VLOG(3)及级别更低的log;
- chromeos 目录下的文件 将打印输出 VLOG(4) 及级别更低的log;
- 其他内容将打印输出 VLOG(0) 及级别更新的log;
Bluetooth模块可以在 bt_stack.conf 文件中配置以开启 verbose logging,
比如:LoggingVModule=--vmodule=*/btm/*=6,*/btu/*=6,*/l2cap/*=6,*/gatt/*=6,*/hid/*=6,*/hf_client/*=6,*/smp/*=6,btm_ble_multi*=6,btif_*=6