glog源码分析1 InitGoogleLogging

glog是google开源的C++日志库,提供了流式日志操作和各种方便的宏定义,广泛应用在各种C++项目中。

使用glog的时候,查看官方提供的文档(glog-0.4.0/doc/glog.html),我们看到的第一个例子是这样的:

   #include <glog/logging.h>

   int main(int argc, char* argv[]) {
     // Initialize Google's logging library.
     google::InitGoogleLogging(argv[0]);

     // ...
     LOG(INFO) << "Found " << num_cookies << " cookies";
   }

也就是首先调用InitGoogleLogging做初始化工作,然后就可以输出日志了。那么,InitGoogleLogging这个函数做了哪些初始化工作呢?

翻开glog源码(使用的0.4.0版本),可以看到,InitGoogleLogging在logging.h中声明,在logging.cc中,InitGoogleLogging的定义如下:

void InitGoogleLogging(const char* argv0) {
  glog_internal_namespace_::InitGoogleLoggingUtilities(argv0);
}

InitGoogleLoggingUtilities接收一个const char*类型的参数,例子中使用argv[0]就是将程序名称作为参数传入。

InitGoogleLoggingUtilities在utilities.h中声明,在utilities.cc中定义如下:

void InitGoogleLoggingUtilities(const char* argv0) {
#ifdef HAVE_STACKTRACE
   InstallFailureFunction(&DumpStackTraceAndExit);
#endif
}

InstallFailureFunction的作用是注册一个LOG(FATAL)后的回调函数。在logging.cc中,InstallFailureFunction的定义:

static void logging_fail() {
  abort();
}

typedef void (*logging_fail_func_t)() ATTRIBUTE_NORETURN;

logging_fail_func_t g_logging_fail_func = &logging_fail;

void InstallFailureFunction(void (*fail_func)()) {
  g_logging_fail_func = (logging_fail_func_t)fail_func;
}

其中,g_logging_fail_func是一个logging_fail_func_t类型的回调函数,默认是调用的logging_fail(),logging_fail()的作用是调用abort()直接终止进程。而我们通过InitGoogleLogging将回调函数设置成了DumpStackTraceAndExit。

下面有两个问题:

1、g_logging_fail_func什么时候调用?

我们从glog的宏定义出发,简单说下g_logging_fail_func什么时候调用的。

glog的入口定义如下:

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

##是宏连接符号,如果severity是INFO,那么LOG(INFO)就是GOOGLE_LOG_INFO.stream()

如果severity是FATAL,那么LOG(FATAL)就是GOOGLE_LOG_FATAL.stream()

查看GOOGLE_LOG_FATAL的定义:

#define COMPACT_GOOGLE_LOG_FATAL google::LogMessageFatal( \
      __FILE__, __LINE__)

那么LOG(FATAL)就是google:: LogMessageFatal (__FILE__,__LINE__).stream()

调用一次LOG(FATAL), LogMessageFatal就构造一次,在LogMessageFatal析构时调用Flush()真正写入文件中。

LogMessageFatal的析构函数如下:

LogMessageFatal::~LogMessageFatal() {
    Flush();
    LogMessage::Fail();
}

Flush()就是真正的写日志这些操作,输出完日志之后,LogMessage::Fail()就会调用上面提到的g_logging_fail_func,函数定义如下:

void LogMessage::Fail() {
  g_logging_fail_func();
}

      2、DumpStackTraceAndExit做了什么事情?

在utilities.cc中,DumpStackTraceAndExit的定义如下:

static void DumpStackTraceAndExit() {
  DumpStackTrace(1, DebugWriteToStderr, NULL);
  //省略
  abort();
}

也就是这个函数的作用包括两部分:

<1>、打印函数调用堆栈

<2>、调用abort退出

在utilities.cc中,打印函数调用堆栈函数DumpStackTrace的定义如下:

static void DumpStackTrace(int skip_count, DebugWriter *writerfn, void *arg) {
  // Print stack trace
  void* stack[32];
  int depth = GetStackTrace(stack, ARRAYSIZE(stack), skip_count+1);
  for (int i = 0; i < depth; i++) {
#if defined(HAVE_SYMBOLIZE)
    if (FLAGS_symbolize_stacktrace) {
      DumpPCAndSymbol(writerfn, arg, stack[i], "    ");
    } else {
      DumpPC(writerfn, arg, stack[i], "    ");
    }
#else
    DumpPC(writerfn, arg, stack[i], "    ");
#endif
  }
}

在这个函数中,使用的关键技术包括下面三部分:

<1>、获取stacktrace

在glog中,GetStackTrace的声明在stacktrace.h中,只有GetStackTrace一个方法。GetStackTrace的实现有多个,默认情况下,是使用stacktrace_generic-inl.h中实现的,也就是使用glibc自带的backtrace实现。

int GetStackTrace(void** result, int max_depth, int skip_count) {
  static const int kStackLength = 64;
  void * stack[kStackLength];
  int size;

  size = backtrace(stack, kStackLength);
  //省略
  return result_count;
}

<2>、Symbolize,将stacktrace获取到的信息转化为字符串数组

对于Symbolize,glog没有使用glibc自带的backtrace_symbols,而是自己通过搜索/proc/self/maps查找的,具体实现在symbolize.h/cc。

<3>、demangle,将Symbolize 之后的C++ ABI的标识符转换为C++源程序的标识符,以方便阅读。

对于demangle,glog也没有使用libstdc++的abi::__cxa_demangle(),而是根据Itanium C++ ABI的生成规则,自己解析的,具体实现在demangle.h/cc中。glog中的demangle的实现并不完整,能够解析类名、函数名、构造/析构函数、运算符等,没有解析函数的参数和模板的参数,比如下列跟abi::__cxa_demangle的对比:

使用glibc的backtrace、backtrace_symbols和libstdc++的abi::__cxa_demangle实现的打印函数调用堆栈的代码可以参考这个:https://panthema.net/2008/0901-stacktrace-demangled/

最后,通过一个实际的例子,看看LOG(FATAL)之后,glog打印了哪些东西吧。

#include <glog/logging.h>
void fatalLog() {
    LOG(FATAL) << "fatal message";
}
int main(int argc, char** argv){
    //glog设置
    google::InitGoogleLogging(argv[0]);
    fatalLog();
}

输出的调用堆栈为:

而如果删除google::InitGoogleLogging(argv[0])这一句的时候,调用结果如下:

没有调用堆栈信息,程序直接退出了。

  • 5
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值