Chromium 日志机制

Posted by Hope on December 28, 2019

Chromium 日志机制

C++ 层

在chromium中,提供了多种打日志的宏(下面简称日志宏)。这些日志宏都可以接受一个流的参数。例如

LOG(INFO) << "Found " << num_cookies << " cookies";

剖析LOG

LOG是Chromium中最常用的日志宏。下面分析 LOG(INFO) « “this is a info msg”;

LOG相关宏的定义

#define LOG(severity) LAZY_STREAM(LOG_STREAM(severity), LOG_IS_ON(severity))
#define LOG_IS_ON(severity) (::logging::ShouldCreateLogMessage(::logging::LOG_##severity))
#define LOG_STREAM(severity) COMPACT_GOOGLE_LOG_ ## severity.stream()
#define COMPACT_GOOGLE_LOG_INFO COMPACT_GOOGLE_LOG_EX_INFO(LogMessage)
#define COMPACT_GOOGLE_LOG_EX_INFO(ClassName, ...) \
  ::logging::ClassName(__FILE__, __LINE__, ::logging::LOG_INFO, ##__VA_ARGS__)
#define LAZY_STREAM(stream, condition) !(condition) ? (void) 0 : ::logging::LogMessageVoidify() & (stream)

展开LOG(INFO)

(::logging::ShouldCreateLogMessage(::logging::LOG_INFO)) ? (void) 0 : \
  ::logging::LogMessageVoidify() & (::logging::LogMessage(__FILE__, __LINE__, LOG_INFO).stream()) \
  << "this is a info msg";
  • 当 ::logging::ShouldCreateLogMessage(::logging::LOG_INFO) 为 false 时
    LOG(INFO) << "this is a info msg";
    // 当不满足打印日志的条件时,不会进行任何的求值以避免无意义的计算
    // 等同于
    (void) 0 << "this is a info msg";
    
  • 当 ::logging::ShouldCreateLogMessage(::logging::LOG_INFO) 为 true 时
    LOG(INFO) << "this is a info msg";
    /*
    LogMessage 便是用于打印一条日志的类,其中 LogMessage::stream 方法返回一个类型为 std::ostream 的引用。
    ::logging::LogMessageVoidify()& 为了使用 LogMessage::stream 返回来的引用,
    避免编译时产生 "value computed is not used" 或者 "statement has no effect" 的警告
     */
     // 等同于
    ::logging::LogMessageVoidify()&&stream << "this is a info msg";
    

其中 LogMessage 的析构函数,会做如下事情:如果定义有日志信息的钩子函数,那么就把日志信息交给钩子函数处理;如果没有,则会根据操作系统平台的不同来选择相应的处理方式。对于Android系统而言,会调用__android_log_write这个Android库函数,将日志以chromium这个tag来写入logcat;此外,如果启用了将日志写入文件的功能的话,也会执行相应的操作。

分类

这些日志宏可以从模式和行为上进行划分。在模式上,可以分为 “debug mode” 或者 “release mode”,这由gn参数的is_debug来进行控制。行为上可以划分为(D)LOG,(D)VLOG、(D)PLOG

debug mode 生效的日志宏

在 debug mode 下生效的日志宏,都是以字母D开头的,比如 DLOG、DVLOG、DPLOG、DCHEKC等等。在 “release mode” 下,上述的日志宏是不会被编译进去的。

DLOG(INFO) << "Found cookies";
DVLOG(1) << "I'm printed when you run the program with --v=1 or more"
DCHEKC(num_cookies > 10) << "Got lots of cookies";
DPLOG(ERROR) << "Couldn't do foo";

release mode 生效的日志宏

不以字母D开头的日志宏,在 debug mode 和 release mode 下都生效。比如LOG、VLOG、PLOG、CHEKC等等。

LOG(INFO) << "Found cookies";
LOG(1) << "I'm printed when you run the program with --v=1 or more"
CHEKC(num_cookies > 10) << "Got lots of cookies";
PLOG(ERROR) << "Couldn't do foo";

VLOG

这是一个 verbose level 的日志宏。如果符合打印日志条件,他会以 INFO 日志级别打印出来。使用方法如下:

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";

对于 verbose level,我们可以使用 –vmodule 来对某个模块进行单独设置,也可以使用 –v 来进行全局设置。比如:

--vmodule=profile=2,icon_loader=1,browser_*=3,*/chromeos/*=4 --v=0
  • VLOG(2) and lower messages to be printed from profile.{h,cc}
  • VLOG(1) and lower messages to be printed from icon_loader.{h,cc}
  • VLOG(3) and lower messages to be printed from files prefixed with “browser”
  • VLOG(4) and lower messages to be printed from files under a “chromeos” directory.
  • VLOG(0) and lower messages to be printed from elsewhere

VLOG 相关的宏定义如下

#define VLOG_STREAM(verbose_level) \
  ::logging::LogMessage(__FILE__, __LINE__, -verbose_level).stream()

#define VLOG(verbose_level) \
  LAZY_STREAM(VLOG_STREAM(verbose_level), VLOG_IS_ON(verbose_level))

PLOG

与 LOG 相比较,PLOG 会在日志的最后面自动加上最新的系统异常信息。在 Windows 系统中,是调用 GetLastError() 来获取系统异常信息。在 POSIX 系统中,是使用 errno。

PLOG 相关的宏定义如下

#if defined(OS_WIN)
#define PLOG_STREAM(severity) \
  COMPACT_GOOGLE_LOG_EX_ ## severity(Win32ErrorLogMessage, \
      ::logging::GetLastSystemErrorCode()).stream()
#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
#define PLOG_STREAM(severity) \
  COMPACT_GOOGLE_LOG_EX_ ## severity(ErrnoLogMessage, \
      ::logging::GetLastSystemErrorCode()).stream()
#endif

#define PLOG(severity)                                          \
  LAZY_STREAM(PLOG_STREAM(severity), LOG_IS_ON(severity))

可以看出底层是使用Win32ErrorLogMessage或者ErrnoLogMessage来实现的。而LOG是使用LogMessage来实现的。

JAVA层

在Java层,一般使用org.chromium.base代替android.util.log。org.chromium.base是android.util.log的封装,增加了 formatLog() 和 normalizeTag() 两个方法来分别格式化 TAG 和 message。