如何使用google logging library (glog)

淳于博
2023-12-01

如何使用google logging library (glog)

引言

glog是一个实施在应用级别的库,这个库提供了依据c++风格的流和各种各样的辅助宏。你可以简单的记录一条特定的严重性级别的消息到流里。比如:

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

glog定义了一系列的宏来简化许多常用的日志任务,你可以通过严重性级别来记录日志,通过命令行来控制日志,依据条件记录日志,当期望的条件不满足的时候,终止程序,记录你自己的冗余日志。下面主要介绍glog支持的功能,这个不包含库中所有的功能,但是都是常用的功能那个,如果你想找一些不常用的特色功能,查阅src/glog目录下的头文件即可。

严重性级别

主要有INFO、WARNING、ERROR、FATAL,在记录完FATAL信息后,程序会被终止。当日志的严重性级别为FATAL时,INFO、WARNING、ERROR、FATAL级别的日志都会打印到日志文件中。
DFATAL表示调试模式下的日志严重性级别,但是在生产中通过自动降低日志的严重性级别到ERROR,从而避免终止程序。
除非特别指定,glog将日志记录到"/tmp/...log...

标志设定

许多标志影响着glog的输出表现,如果你的机器安装的有gflags,会自动被发现和使用,你可以通过命令行传递flags,例如,你想打开–logtostderr,你可以开启你的应用程序:

./your_application --logtostderr=1

如果gfalgs没有安装,可以通过设置环境变量,在flags上加上前缀“GLOG_”,例如:

GLOG_logtostderr=1 ./your_application

以下常用:

logtostderr (bool, default=false)

stderrthreshold (int, default=2, which is ERROR)
Copy log messages at or above this level to stderr in addition to logfiles. The numbers of severity levels INFO, WARNING, ERROR, and FATAL are 0, 1, 2, and 3, respectively.

minloglevel (int, default=0, which is INFO)
Log messages at or above this level. Again, the numbers of severity levels INFO, WARNING, ERROR, and FATAL are 0, 1, 2, and 3, respectively.

log_dir (string, default="")
If specified, logfiles are written into this directory instead of the default logging directory.

v (int, default=0)
Show all VLOG(m) messages for m less or equal the value of this flag. Overridable by --vmodule. See the section about verbose logging for more detail.
vmodule (string, default="")

在logging.cc中其他的标志位,搜索源码中的“DEFINE_”来查看所有的flags。你可以修改你的flag values通过全局变量FLAGS_*,但是必须要在调用google::InitGoogleLogging 前使用,示例如下:

 LOG(INFO) << "file";
 // Most flags work immediately after updating values.
 FLAGS_logtostderr = 1;
 LOG(INFO) << "stderr";
 FLAGS_logtostderr = 0;
 // This won't change the log destination. If you want to set this
 // value, you should do this before google::InitGoogleLogging .
 FLAGS_log_dir = "/some/log/directory";
 LOG(INFO) << "the same file";

条件/偶然记录

特定条件下的记录,例如。当且仅当num_cookies大于10的时候,才会输出日志"Got lots of cookies"

LOG_IF(INFO, num_cookies > 10) << "Got lots of cookies";

输出每间隔10次重复的日志:

LOG_EVERY_N(INFO, 10) << "Got the " << google::COUNTER << "th cookie";

输出满足条件的日志:

LOG_IF_EVERY_N(INFO, (size > 1024), 10) << "Got the " << google::COUNTER << "th big cookie";

输出首次20次的日志:

 LOG_FIRST_N(INFO, 20) << "Got the " << google::COUNTER << "th cookie";

支持调试模式

调试模式的日志宏,只有在调试环境下编译才有效,使用这些宏,可避免在生产模式下记录日志,使得程序变慢。

 DLOG(INFO) << "Found cookies";
 DLOG_IF(INFO, num_cookies > 10) << "Got lots of cookies";
 DLOG_EVERY_N(INFO, 10) << "Got the " << google::COUNTER << "th cookie";

CHECK宏

CHECK宏可以快速检查期望的条件表达式,可以尽早发现程序中的错误,这个与C标准库中的assert用法类似。当CHECK中的条件表达式不为true时,直接终止程序,不同的是,CHECK不管在什么模式下都有效,但是assert仅仅在NDEBUG模式下有效果,比如如下表达式,不管在什么模式下都可以使用:

CHECK(fp->Write(x) == 4) << "Write failed!";

还有许许多多的等式和不等式的宏,-CHECK_EQ, CHECK_NE, CHECK_LE, CHECK_LT, CHECK_GE和CHECK_GT,他们比较两个值,当比较结果不是期望的结果时,会打印一条FATAL严重级别的日志。这两个值必须定义了操作符operator<<(ostream, …)。示例如下:

 CHECK_NE(1, 2) << ": The world must be ending!";

有时候为了确保参数的准确性,可以使用如下表达式确保参数的合法性:

 CHECK_EQ(string("abc")[1], 'b');

如果一个参数是一个指针,而且而另外的一个值为NULL,编译器就会报错。为了使之可以正常工作,需要使用 static_cast 来转换NULL,如:

CHECK_EQ(some_ptr, static_cast<SomeType*>(NULL));

其实更好的方法是使用宏 CHECK_NOTNULL :

 CHECK_NOTNULL(some_ptr);
 some_ptr->DoSomething();

因为返回值是对应的指针类型,这点对于构造函数的初始化列表是很有用的:

struct S 
{
     S(Something* ptr) : ptr_(CHECK_NOTNULL(ptr)) {}
     Something* ptr_;
};

需要注意的是,因为这个原因,你不能把这个宏当做C++的流来使用。在程序终止之前,请使用上述的 CHECK_EQ来日志信息。
如果你要比较C格式的字符串,glog提供了很多大小写区分和不区分字符串比较宏: CHECK_STREQ, CHECK_STRNE, CHECK_STRCASEEQ,CHECK_STRCASENE。CASE版本的宏是指大小写区分的。在使用这些宏的时候,你可以放心地传递参数NULL,这些宏会把NULL和非NULL视为不等,而两个NULL视为相等。
需要注意的是可能两个参数都是临时字符串,都会在整个表达式结束之后销毁(比如: CHECK_STREQ(Foo().c_str(), Bar().c_str())中的Foo和Bar返回C++格式的std::string).
宏CHECK_DOUBLE_EQ是在一定的误差范围内检查两个浮点数是否相等。如果需要指定误差范围,使用宏CHECK_NEAR,他的第三个参数即为误差范围。

详细日志

当你在跟踪调试很难的bug的时候,日志消息是非常重要的。然而,在日常的开发过程中,你一般希望忽略掉太详细的日志,对于这样的详细日志,glog提供了 VLOG 这个宏,它允许你定义自己的数字化的日志等级。 --v 命名选项控制着详细日志是否会被记录。

VLOG(1) << "I'm printed when you run the program with --v=1 or higher";
VLOG(2) << "I'm printed when you run the program with --v=2 or higher";

在 VLOG 中,详细日志等级越低,这个日志消息越有可能被记录。比如,如果 --v==1 ,那么 VLOG(1) 会打日志,而 VLOG(2) 不会打日志。这个和内置的日志等级相反的,因为 INFO 是0而 ERROR 是2 。 --minloglevel 会记录 WARNING 和 WARNING 以上的所有等级的日志。虽然在宏 VLOG和标志–v中 都可以指定任意一个数字,但是他们的相同的作用值的却是相反的。比如,如果你要使用VLOG(0),你得使用–v=-1或者更低的去压制他。大多情况下我们不是使用默认值的详细日志,所有这个使用的不多。宏VLOG记录的是INFO等级的日志(…)。
一些支持的详细日志操作可以通过命令行来控制:

--vmodule=mapreduce=2,file=1,gfs*=3 --v=0

将会:
从mapreduce.{h,cc}打印 VLOG(2)和更低等级的日志
从file.{h,cc}打印 VLOG(1)和更低等级的日志
从“gfs”开头的文件打印 VLOG(3)和更低等级的日志
从其他地方打印 VLOG(0)和更低等级的日志
支持通配符*和?的函数,请查看command line flags。
像一般日志一样,详细日志的也有条件宏,VLOG_IS_ON(n)。在–v等于或者大于n的时候,这个宏返回true,用法如下:

if (VLOG_IS_ON(2)) 
{
	// do some logging preparation and logging
	// that can't be accomplished with just VLOG(2) << ...;
}

详细日志的条件宏 VLOG_IF , VLOG_EVERY_N 和 VLOG_IF_EVERY_N 的用法和一般日志的 LOG_IF, LOG_EVERY_N, LOF_IF_EVERY一样,但是提供了一个数字化的参数区分日志等级。

VLOG_IF(1, (size > 1024)) << "I'm printed when size is more than   1024 and when you run the "
"program with --v=1 or more";
VLOG_EVERY_N(1, 10)
	<< "I'm printed every 10th occurrence, and when you run the program "
	"with --v=1 or more. Present occurence is " <<  google::COUNTER;
VLOG_IF_EVERY_N(1, (size > 1024), 10)
	<< "I'm printed on every 10th occurence of case when size is more "
	" than 1024, when you run the program with --v=1 or more. ";
"Present occurence is " << google::COUNTER;

错误处理机制

glog提供了一个可信的错误处理机制,在程序崩溃的情况下(因为某些特定的信号,如SIGSEGV)dump下有用的信息。这个错误处理可以通过google::InstallFailureSignalHandler()的方式安装。下面是一个错误处理的输出结果示例:

*** Aborted at 1225095260 (unix time) try "date -d @1225095260" if you are using GNU date ***
	*** SIGSEGV (@0x0) received by PID 17711 (TID 0x7f893090a6f0) from PID 0; stack trace: ***
PC: @           0x412eb1 TestWaitingLogSink::send()
	@     0x7f892fb417d0 (unknown)
	@           0x412eb1 TestWaitingLogSink::send()
	@     0x7f89304f7f06 google::LogMessage::SendToLog()
	@     0x7f89304f35af google::LogMessage::Flush()
	@     0x7f89304f3739 google::LogMessage::~LogMessage()
	@           0x408cf4 TestLogSinkWaitTillSent()
	@           0x4115de main
	@     0x7f892f7ef1c4 (unknown)
	@           0x4046f9 (unknown)

默认情况下,错误处理会把信息输出到标准错误输出,你可以使用 InstallFailureWriter()自行改变输出位置。

杂记

不牺牲应用程序性能的表达式:

CHECK(obj.ok) << obj.CreatePrettyFormattedStringButVerySlow();

使用定义的错误函数:
FATAL或者不满足条件的CHECK会终止程序,可以通过如下方式改变这种行为:

void YourFailureFunction() 
{
     // Reports something...
     exit(1);
}

int main(int argc, char* argv[]) 
{
     google::InstallFailureFunction(&YourFailureFunction);
}
 类似资料: