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])这一句的时候,调用结果如下:

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

附件是VS2010的工程,C++日志类,谷歌的东西,很好用,也很强大哦! glog简介 Google glog是一个基于程序级记录日志信息的c++库,编程使用方式与c++的stream操作类似,例: LOG(INFO) << "Found " << num_cookies << " cookies"; “LOG”宏为日志输出关键字,“INFO”为严重性程度。 主要支持功能: 1, 参数设置,以命令行参数的方式设置标志参数来控制日志记录行为; 2, 严重性分级,根据日志严重性分级记录日志; 3, 可有条件地记录日志信息; 4, 条件中止程序。丰富的条件判定宏,可预设程序终止条件; 5, 异常信号处理。程序异常情况,可自定义异常处理过程; 6, 支持debug功能。可只用于debug模式; 7, 自定义日志信息; 8, 线程安全日志记录方式; 9, 系统级日志记录; 10, google perror风格日志信息; 11, 精简日志字符串信息。 开源代码托管 开源代码地址:https://github.com/google/glog 其实官方开源代码已经有大量demo可以参考了,也提供了VS可以直接打开编译的项目。 如何使用 1:把glog文件夹拷贝到源代码目录 2:在工程设置中添加附加包含目录(glog\include;)和附加库目录(glog\lib;),在附件依赖项中添加对应lib文件,一一对应关系如下: MDd libglog32MDd.lib MD libglog32MD.lib MTd libglog32MTd.lib MT libglog32MT.lib 建议使用MDd和MD方式,带上对应的dll(在glog\bin目录,需要时拷贝到bin文件输出目录)可以避免使用MTd,MT引起的内存泄露是值得的。 #include #include using namespace std; //包含glog头文件,建议放在stdafx.h中 //#define GOOGLE_GLOG_DLL_DECL // 使用静态库的时候用这个,不过我测试静态库有内存泄露,所以不推荐使用静态库 #define GLOG_NO_ABBREVIATED_SEVERITIES #include "glog/logging.h" //获取当前程序的运行目录 string GetAppPathA() { char szExePath[MAX_PATH] = {0}; GetModuleFileNameA(NULL,szExePath,MAX_PATH); char *pstr = strrchr(szExePath,'\\'); memset(pstr+1,0,1); string strAppPath(szExePath); return strAppPath; } void main() { //glog初始化 google::InitGoogleLogging("重签程序"); string strLogPath = GetAppPathA().append("LogInfo\\"); CreateDirectoryA(strLogPath.c_str(),NULL); google::SetLogDestination(google::GLOG_INFO,strLogPath.c_str()); //功能测试 LOG(INFO)<<"log start...";//普通日志 LOG(WARNING)<<"Warning log";//警告日志 LOG(ERROR)<<"Error log";//错误日志 int i = 4; LOG_IF(INFO,i == 4)<<"Log if Test"; //以上就是我常用的几个日志函数了,当然还有很多更加强大的日志相关函数,大家如有有兴趣,可以参照官方给的示例使用, //开源代码地址:https://github.com/google/glog MessageBoxA(NULL,"Test Over",":)",MB_ICONINFORMATION); } 测试程序中,我使用的动态链接库方式。(Debug模式中代码生成为MDd,Release为MD)。lib是截止现在2015-11-04-21:35是最新的。采用VS2010编译,MTd,MT,MDd,MD方式编译在测试项目中都有提供。 博文地址: http://blog.csdn.net/sunflover454/article/details/49643625
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值