chromium源码学习-调试日志 LOG

在学习 chromium 源码时,我们经常需要增加调试日志,常见的用法一般是

// TurboNet.mm
  133
  134  LOG(INFO) << "TurboNet Engine started.";  
  135  LOG(INFO);
  136  LOG(WARNING);
  137  VLOG(1);
  138  VLOG(2);
  139  PLOG(INFO); // 会输出系统错误码信息
  140  PLOG(WARNING);
  141  DLOG(INFO);
  142  DLOG(WARNING);

日志输出效果如下:
在这里插入图片描述

其中 INFO 代表当前这条日志的级别(详见下面 2 小节),使用的时候就是输入 INFO 就行。接下来我们在探索下这个宏背后的内容。 LOG 在 release 和 debug 模式均生效,DLOG 仅在 debug 模式生效。VLOG 支持模式匹配,比如指定特定文件的日志级别、特定目录的日志级别、特定前缀的文件的日志级别等,详见官方注释:

// There are "verbose level" logging macros.  They look like
//
//   VLOG(1) << "I'm printed when you run the program with --v=1 or more";
//   VLOG(2) << "I'm printed when you run the program with --v=2 or more";
//
// These always log at the INFO log level (when they log at all).
// The verbose logging can also be turned on module-by-module.  For instance,
//    --vmodule=profile=2,icon_loader=1,browser_*=3,*/chromeos/*=4 --v=0
// will cause:
//   a. VLOG(2) and lower messages to be printed from profile.{h,cc}
//   b. VLOG(1) and lower messages to be printed from icon_loader.{h,cc}
//   c. VLOG(3) and lower messages to be printed from files prefixed with
//      "browser"
//   d. VLOG(4) and lower messages to be printed from files under a
//     "chromeos" directory.
//   e. VLOG(0) and lower messages to be printed from elsewhere

一、基本用法

LOG 本身是一个宏:

#define LOG(severity) LAZY_STREAM(LOG_STREAM(severity), LOG_IS_ON(severity))

我们逐个解开:

1、LOG_STREAM

#define LOG_STREAM(severity) COMPACT_GOOGLE_LOG_ ## severity.stream()

LOG_STREAM(severity) 会被转成具体 severity 对应的宏,例如 INFO 被转为宏 COMPACT_GOOGLE_LOG_INFO:

#define COMPACT_GOOGLE_LOG_INFO COMPACT_GOOGLE_LOG_EX_INFO(LogMessage)

宏 COMPACT_GOOGLE_LOG_EX_INFO:

#define COMPACT_GOOGLE_LOG_EX_INFO(ClassName, ...)                  \
  ::logging::ClassName(__FILE__, __LINE__, ::logging::LOGGING_INFO, \
                       ##__VA_ARGS__)

ClassName 是 LogMessage,因此这里 COMPACT_GOOGLE_LOG_EX_INFO 代表的是 ::logging::LogMessage 类的构造方法,构造方法里调用 Init 方法初始化了一些调试信息比如文件名,行号,时间戳等,保存在 std::ostringstream 类型的成员变量 stream_ 中,LOG_STREAM 这个宏最终就是返回了这个 stream_对象。

2、LOG_IS_ON

#define LOG_IS_ON(severity)  (::logging::ShouldCreateLogMessage(::logging::LOGGING_##severity))
// severity 值如下
constexpr LogSeverity LOGGING_INFO = 0;
constexpr LogSeverity LOGGING_WARNING = 1;
constexpr LogSeverity LOGGING_ERROR = 2;
constexpr LogSeverity LOGGING_FATAL = 3;
constexpr LogSeverity LOGGING_DEBUG = 4;
constexpr LogSeverity LOGGING_NUM_SEVERITIES = 5;

bool ShouldCreateLogMessage(int severity) {
  if (severity < g_min_log_level)
    return false;

  // Return true here unless we know ~LogMessage won't do anything.
  return g_logging_destination != LOG_NONE || g_log_message_handler ||
         severity >= kAlwaysPrintErrorLevel;
}

// g_min_log_level
// 如果 SetMinLogLevel 未调用,g_min_log_level 默认为 LOGGING_FATAL
// 因此为了输出 LOGGING_INFO 级别的日志,需要将 g_min_log_level 的值设置为 <=-1 的值
void SetMinLogLevel(int level) {
  g_min_log_level = std::min(LOGGING_FATAL, level); 
}

3、LAZY_STREAM

#define LAZY_STREAM(stream, condition)  !(condition) ? (void) 0 : ::logging::LogMessageVoidify() & (stream)

如果 condition 为 false,则返回 nullptr,否则返回 stream 对象,这里 ::logging::LogMessageVoidify() 是一个空操作,该类内部重载了 & 运算符,对应一个空函数体,这么做是为了避免编译器警告 stream 未被使用。

二、进阶用法【未完待续】

VLOG、PLOG、DLOG

1、VLOG

整体跟 LOG 差不多,使用方式如下:

VLOG(1) << "Filter function already exists: " << filter_function
            << " with associated data: " << user_data;
#define VLOG(verbose_level) \
  LAZY_STREAM(VLOG_STREAM(verbose_level), VLOG_IS_ON(verbose_level))

// 除了对日志级别取负值,跟 LOG 一样,
#define VLOG_STREAM(verbose_level) \
  ::logging::LogMessage(__FILE__, __LINE__, -(verbose_level)).stream()

#define VLOG_IS_ON(verboselevel) \
  ((verboselevel) <= ::logging::GetVlogLevel(__FILE__))
template <size_t N>
int GetVlogLevel(const char (&file)[N]) {
  return GetVlogLevelHelper(file, N);
}

int GetVlogLevelHelper(const char* file, size_t N) {
  DCHECK_GT(N, 0U);
  // Note: |g_vlog_info| may change on a different thread during startup
  // (but will always be valid or nullptr).
  VlogInfo* vlog_info = g_vlog_info;
  return vlog_info ?
      vlog_info->GetVlogLevel(base::StringPiece(file, N - 1)) :
      GetVlogVerbosity();
}

GetVlogLevel 会根据预设的模式对 file 进行匹配,匹配成功则可以执行日志输出,否则不执行。

三、日志输出

在 LogMessage::~LogMessage() 方法中,我们可以看到对各个系统平台的调试日志输出做了兼容,例如安卓(调用 __android_log_write)、iOS(调用__builtin_os_log_format)、Win(调用 OutputDebugStringA)等,因此鸿蒙的支持也需要在该方法中完成。
在这里插入图片描述

参考资料

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值